<?php
if (!defined('BASE_PATH'))
    exit('No direct script access allowed');

/**
 * Returns the url based on route.
 */
function url($route)
{
    $app = \Liten\Liten::getInstance();
    $url = $app->req->url_for($route);
    return $url;
}

/**
 * Renders any unwarranted special characters to HTML entities.
 *
 * @since 1.0.0
 * @param string $str
 * @return mixed
 */
function _h($str)
{
    return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}

/**
 * @since 1.0.0
 */
function timeAgo($original)
{
    // array of time period chunks
    $chunks = array(
        array(60 * 60 * 24 * 365, 'year'),
        array(60 * 60 * 24 * 30, 'month'),
        array(60 * 60 * 24 * 7, 'week'),
        array(60 * 60 * 24, 'day'),
        array(60 * 60, 'hour'),
        array(60, 'min'),
        array(1, 'sec'),
    );

    $today = time(); /* Current unix time  */
    $since = $today - $original;

    // $j saves performing the count function each time around the loop
    for ($i = 0, $j = count($chunks); $i < $j; $i++) {

        $seconds = $chunks[$i][0];
        $name = $chunks[$i][1];

        // finding the biggest chunk (if the chunk fits, break)
        if (($count = floor($since / $seconds)) != 0) {
            break;
        }
    }

    $print = ($count == 1) ? '1 ' . $name : "$count {$name}s";

    if ($i + 1 < $j) {
        // now getting the second item
        $seconds2 = $chunks[$i + 1][0];
        $name2 = $chunks[$i + 1][1];

        // add second item if its greater than 0
        if (($count2 = floor(($since - ($seconds * $count)) / $seconds2)) != 0) {
            $print .= ($count2 == 1) ? ', 1 ' . $name2 : " $count2 {$name2}s";
        }
    }
    return $print;
}

/**
 * @since 1.0.0
 * @return bool
 */
function remoteFileExists($url)
{
    $curl = curl_init($url);
    //don't fetch the actual page, you only want to check the connection is ok
    curl_setopt($curl, CURLOPT_NOBODY, true);
    //do request
    $result = curl_exec($curl);
    $ret = false;
    //if request did not fail
    if ($result !== false) {
        //if request was ok, check response code
        $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);

        if ($statusCode == 200) {
            $ret = true;
        }
    }
    curl_close($curl);
    return $ret;
}

/**
 * Return the file extension of the given filename.
 *
 * @param  string $filename
 * @return string
 * @since   1.0.0
 */
function get_file_ext($filename)
{
    return pathinfo($filename, PATHINFO_EXTENSION);
}

/**
 * Truncate a string to a specified length without cutting a word off
 *
 * @param   string  $string  The string to truncate
 * @param   int     $length  The length to truncate the string to
 * @param   string  $append  Text to append to the string IF it gets
 *                           truncated, defaults to '...'
 * @return  string
 *
 * @access  public
 * @since   1.0.0
 */
function safe_truncate($string, $length, $append = '...')
{
    $ret = substr($string, 0, $length);
    $last_space = strrpos($ret, ' ');

    if ($last_space !== FALSE && $string != $ret) {
        $ret = substr($ret, 0, $last_space);
    }

    if ($ret != $string) {
        $ret .= $append;
    }

    return $ret;
}

/**
 * Special function for files including
 *
 * @param string $file
 * @param bool $once
 * @param bool|Closure $show_errors If bool error will be processed, if Closure - only Closure will be called
 *
 * @return bool
 * @since 1.0.0
 */
function _require($file, $once = false, $show_errors = true)
{
    if (file_exists($file)) {
        if ($once) {
            return require_once $file;
        } else {
            return require $file;
        }
    } elseif (is_bool($show_errors) && $show_errors) {
        $data = debug_backtrace()[0];
        trigger_error("File $file does not exists in $data[file] on line $data[line]", E_USER_ERROR);
    } elseif ($show_errors instanceof \Closure) {
        return (bool) $show_errors();
    }
    return false;
}

/**
 * Special function for files including
 *
 * @param string $file
 * @param bool $once
 * @param bool|Closure $show_errors If bool error will be processed, if Closure - only Closure will be called
 *
 * @return bool
 * @since 1.0.0
 */
function _include($file, $once = false, $show_errors = true)
{
    if (file_exists($file)) {
        if ($once) {
            return include_once $file;
        } else {
            return include $file;
        }
    } elseif (is_bool($show_errors) && $show_errors) {
        $data = debug_backtrace()[0];
        trigger_error("File $file does not exists in $data[file] on line $data[line]", E_USER_WARNING);
    } elseif ($show_errors instanceof \Closure) {
        return (bool) $show_errors();
    }
    return false;
}

/**
 * Special function for files including
 *
 * @param string $file
 * @param bool|Closure $show_errors If bool error will be processed, if Closure - only Closure will be called
 *
 * @return bool
 * @since 1.0.0
 */
function _require_once($file, $show_errors = true)
{
    return _require($file, true, $show_errors);
}

/**
 * Special function for files including
 *
 * @param string $file
 * @param bool|Closure $show_errors If bool error will be processed, if Closure - only Closure will be called
 *
 * @return bool
 * @since 1.0.0
 */
function _include_once($file, $show_errors = true)
{
    return _include($file, true, $show_errors);
}

/**
 * Validate email.
 *
 * @param string $email
 * @return string (ok, empty, bad email).
 * @since 1.0.0
 */
function validate_email($email)
{
    if ($email == '')
        return false;
    if (filter_var($email, FILTER_VALIDATE_EMAIL) == false)
        return false;
    else
        return true;
}

/**
 * Formats date to be stored in MySQL database.
 *
 * @return string
 * @since 1.0.0
 */
function formatDate($date)
{
    $date = strtotime($date);
    $date = date("Y-m-d", $date);

    return $date;
}

/**
 * Removes all whitespace.
 *
 * @param string $str
 * @return mixed
 * @since 1.0.0
 */
function _trim($str)
{
    return preg_replace('/\s/', '', $str);
}

/**
 * Function used to create a slug associated to an "ugly" string.
 *
 * @param string $string the string to transform.
 * @return string the resulting slug.
 * @since 1.0.0
 */
function slugify($string)
{
    $table = array(
        'Š' => 'S', 'š' => 's', 'Đ' => 'Dj', 'đ' => 'dj', 'Ž' => 'Z', 'ž' => 'z', 'Č' => 'C', 'č' => 'c', 'Ć' => 'C', 'ć' => 'c',
        'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A', 'Å' => 'A', 'Æ' => 'A', 'Ç' => 'C', 'È' => 'E', 'É' => 'E',
        'Ê' => 'E', 'Ë' => 'E', 'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I', 'Ñ' => 'N', 'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O',
        'Õ' => 'O', 'Ö' => 'O', 'Ø' => 'O', 'Ù' => 'U', 'Ú' => 'U', 'Û' => 'U', 'Ü' => 'U', 'Ý' => 'Y', 'Þ' => 'B', 'ß' => 'Ss',
        'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ä' => 'a', 'å' => 'a', 'æ' => 'a', 'ç' => 'c', 'è' => 'e', 'é' => 'e',
        'ê' => 'e', 'ë' => 'e', 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i', 'ð' => 'o', 'ñ' => 'n', 'ò' => 'o', 'ó' => 'o',
        'ô' => 'o', 'õ' => 'o', 'ö' => 'o', 'ø' => 'o', 'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ý' => 'y', 'ý' => 'y', 'þ' => 'b',
        'ÿ' => 'y', 'Ŕ' => 'R', 'ŕ' => 'r', '/' => '-', ' ' => '-'
    );
    // -- Remove duplicated spaces
    $stripped = preg_replace(array('/\s{2,}/', '/[\t\n]/'), ' ', $string);
    // -- Returns the slug
    return strtolower(strtr($string, $table));
}

/**
 * @param string $file Filepath
 * @param int $digits Digits to display
 * @return string|bool Size (KB, MB, GB, TB) or boolean
 * @since 1.0.0
 */
function getFilesize($file, $digits = 2)
{
    if (is_file($file)) {
        $fileSize = filesize($file);
        $sizes = array("TB", "GB", "MB", "KB", "B");
        $total = count($sizes);
        while ($total-- && $fileSize > 1024) {
            $fileSize /= 1024;
        }
        return round($fileSize, $digits) . " " . $sizes[$total];
    }
    return false;
}

/**
 * Redirects to another page.
 *
 * @param string $location The path to redirect to
 * @param int $status Status code to use
 * @return bool False if $location is not set
 * @since 1.0.0
 */
function redirect($location, $status = 302)
{
    if (!$location)
        return false;
    header("Location: $location", true, $status);
}
if (!function_exists('hash_equals')) {

    /**
     * Timing attack safe string comparison
     *
     * Compares two strings using the same time whether they're equal or not.
     * This function should be used to mitigate timing attacks; for instance, when testing crypt() password hashes.
     *
     * @param string $known_string The string of known length to compare against
     * @param string $user_string The user-supplied string
     * @return boolean Returns TRUE when the two strings are equal, FALSE otherwise.
     */
    function hash_equals($known_string, $user_string)
    {
        if (func_num_args() !== 2) {
            // handle wrong parameter count as the native implentation
            trigger_error('hash_equals() expects exactly 2 parameters, ' . func_num_args() . ' given', E_USER_WARNING);
            return null;
        }
        if (is_string($known_string) !== true) {
            trigger_error('hash_equals(): Expected known_string to be a string, ' . gettype($known_string) . ' given', E_USER_WARNING);
            return false;
        }
        $known_string_len = strlen($known_string);
        $user_string_type_error = 'hash_equals(): Expected user_string to be a string, ' . gettype($user_string) . ' given'; // prepare wrong type error message now to reduce the impact of string concatenation and the gettype call
        if (is_string($user_string) !== true) {
            trigger_error($user_string_type_error, E_USER_WARNING);
            // prevention of timing attacks might be still possible if we handle $user_string as a string of diffent length (the trigger_error() call increases the execution time a bit)
            $user_string_len = strlen($user_string);
            $user_string_len = $known_string_len + 1;
        } else {
            $user_string_len = $known_string_len + 1;
            $user_string_len = strlen($user_string);
        }
        if ($known_string_len !== $user_string_len) {
            $res = $known_string ^ $known_string; // use $known_string instead of $user_string to handle strings of diffrent length.
            $ret = 1; // set $ret to 1 to make sure false is returned
        } else {
            $res = $known_string ^ $user_string;
            $ret = 0;
        }
        for ($i = strlen($res) - 1; $i >= 0; $i--) {
            $ret |= ord($res[$i]);
        }
        return $ret === 0;
    }
}

/**
 * Outputs the html checked attribute.
 *
 * Compares the first two arguments and if identical marks as checked
 *
 *
 * @param mixed $checked One of the values to compare
 * @param mixed $current (true) The other value to compare if not just true
 * @param bool $echo Whether to echo or just return the string
 * @return string html attribute or empty string
 */
if (!function_exists('checked')) {

    function checked($checked, $current = true, $echo = true)
    {
        return checked_selected_helper($checked, $current, $echo, 'checked');
    }
}

/**
 * Outputs the html selected attribute.
 *
 * Compares the first two arguments and if identical marks as selected
 *
 *
 * @param mixed $selected One of the values to compare
 * @param mixed $current (true) The other value to compare if not just true
 * @param bool $echo Whether to echo or just return the string
 * @return string html attribute or empty string
 */
if (!function_exists('selected')) {

    function selected($selected, $current = true, $echo = true)
    {
        return checked_selected_helper($selected, $current, $echo, 'selected');
    }
}

/**
 * Outputs the html disabled attribute.
 *
 * Compares the first two arguments and if identical marks as disabled
 *
 *
 * @param mixed $disabled One of the values to compare
 * @param mixed $current (true) The other value to compare if not just true
 * @param bool $echo Whether to echo or just return the string
 * @return string html attribute or empty string
 */
if (!function_exists('disabled')) {

    function disabled($disabled, $current = true, $echo = true)
    {
        return checked_selected_helper($disabled, $current, $echo, 'disabled');
    }
}

/**
 * Private helper function for checked, selected, and disabled.
 *
 * Compares the first two arguments and if identical marks as $type
 *
 * @access private
 *
 * @param any $helper One of the values to compare
 * @param any $current (true) The other value to compare if not just true
 * @param bool $echo Whether to echo or just return the string
 * @param string $type The type of checked|selected|disabled we are doing
 * @return string html attribute or empty string
 */
if (!function_exists('checked_selected_helper')) {

    function checked_selected_helper($helper, $current, $echo, $type)
    {
        if ($helper === $current)
            $result = " $type='$type'";
        else
            $result = '';

        if ($echo)
            echo $result;

        return $result;
    }
}

function _filter_input_int($_globals, $variable_name = '')
{
    return filter_input(strtoupper($_globals), $variable_name, FILTER_SANITIZE_NUMBER_INT);
}

function _filter_input_string($_globals, $variable_name = '')
{
    return filter_input(strtoupper($_globals), $variable_name, FILTER_SANITIZE_STRING);
}

function _filter_input_array($_globals, $args = FILTER_SANITIZE_STRING, $bool = true)
{
    return filter_input_array(strtoupper($_globals), $args, $bool);
}

/**
 * Added htmLawed functions
 *
 * @since 5.0.1
 */
function htmLawed($t, $C = 1, $S = array())
{
    $C = is_array($C) ? $C : array();
    if (!empty($C['valid_xhtml'])) {
        $C['elements'] = empty($C['elements']) ? '*-center-dir-font-isindex-menu-s-strike-u' : $C['elements'];
        $C['make_tag_strict'] = isset($C['make_tag_strict']) ? $C['make_tag_strict'] : 2;
        $C['xml:lang'] = isset($C['xml:lang']) ? $C['xml:lang'] : 2;
    }
    // config eles
    $e = array(
        'a' => 1,
        'abbr' => 1,
        'acronym' => 1,
        'address' => 1,
        'applet' => 1,
        'area' => 1,
        'b' => 1,
        'bdo' => 1,
        'big' => 1,
        'blockquote' => 1,
        'br' => 1,
        'button' => 1,
        'caption' => 1,
        'center' => 1,
        'cite' => 1,
        'code' => 1,
        'col' => 1,
        'colgroup' => 1,
        'dd' => 1,
        'del' => 1,
        'dfn' => 1,
        'dir' => 1,
        'div' => 1,
        'dl' => 1,
        'dt' => 1,
        'em' => 1,
        'embed' => 1,
        'fieldset' => 1,
        'font' => 1,
        'form' => 1,
        'h1' => 1,
        'h2' => 1,
        'h3' => 1,
        'h4' => 1,
        'h5' => 1,
        'h6' => 1,
        'hr' => 1,
        'i' => 1,
        'iframe' => 1,
        'img' => 1,
        'input' => 1,
        'ins' => 1,
        'isindex' => 1,
        'kbd' => 1,
        'label' => 1,
        'legend' => 1,
        'li' => 1,
        'map' => 1,
        'menu' => 1,
        'noscript' => 1,
        'object' => 1,
        'ol' => 1,
        'optgroup' => 1,
        'option' => 1,
        'p' => 1,
        'param' => 1,
        'pre' => 1,
        'q' => 1,
        'rb' => 1,
        'rbc' => 1,
        'rp' => 1,
        'rt' => 1,
        'rtc' => 1,
        'ruby' => 1,
        's' => 1,
        'samp' => 1,
        'script' => 1,
        'select' => 1,
        'small' => 1,
        'span' => 1,
        'strike' => 1,
        'strong' => 1,
        'sub' => 1,
        'sup' => 1,
        'table' => 1,
        'tbody' => 1,
        'td' => 1,
        'textarea' => 1,
        'tfoot' => 1,
        'th' => 1,
        'thead' => 1,
        'tr' => 1,
        'tt' => 1,
        'u' => 1,
        'ul' => 1,
        'var' => 1
    ); // 86/deprecated+embed+ruby
    if (!empty($C['safe'])) {
        unset($e['applet'], $e['embed'], $e['iframe'], $e['object'], $e['script']);
    }
    $x = !empty($C['elements']) ? str_replace(array(
            "\n",
            "\r",
            "\t",
            ' '
            ), '', $C['elements']) : '*';
    if ($x == '-*') {
        $e = array();
    } elseif (strpos($x, '*') === false) {
        $e = array_flip(explode(',', $x));
    } else {
        if (isset($x[1])) {
            preg_match_all('`(?:^|-|\+)[^\-+]+?(?=-|\+|$)`', $x, $m, PREG_SET_ORDER);
            for ($i = count($m); --$i >= 0;) {
                $m[$i] = $m[$i][0];
            }
            foreach ($m as $v) {
                if ($v[0] == '+') {
                    $e[substr($v, 1)] = 1;
                }
                if ($v[0] == '-' && isset($e[($v = substr($v, 1))]) && !in_array('+' . $v, $m)) {
                    unset($e[$v]);
                }
            }
        }
    }
    $C['elements'] = & $e;
    // config attrs
    $x = !empty($C['deny_attribute']) ? str_replace(array(
            "\n",
            "\r",
            "\t",
            ' '
            ), '', $C['deny_attribute']) : '';
    $x = array_flip((isset($x[0]) && $x[0] == '*') ? explode('-', $x) : explode(',', $x . (!empty($C['safe']) ? ',on*' : '')));
    if (isset($x['on*'])) {
        unset($x['on*']);
        $x += array(
            'onblur' => 1,
            'onchange' => 1,
            'onclick' => 1,
            'ondblclick' => 1,
            'onfocus' => 1,
            'onkeydown' => 1,
            'onkeypress' => 1,
            'onkeyup' => 1,
            'onmousedown' => 1,
            'onmousemove' => 1,
            'onmouseout' => 1,
            'onmouseover' => 1,
            'onmouseup' => 1,
            'onreset' => 1,
            'onselect' => 1,
            'onsubmit' => 1
        );
    }
    $C['deny_attribute'] = $x;
    // config URL
    $x = (isset($C['schemes'][2]) && strpos($C['schemes'], ':')) ? strtolower($C['schemes']) : 'href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; *:file, http, https';
    $C['schemes'] = array();
    foreach (explode(';', str_replace(array(
        ' ',
        "\t",
        "\r",
        "\n"
            ), '', $x)) as $v) {
        $x = $x2 = null;
        list ($x, $x2) = explode(':', $v, 2);
        if ($x2) {
            $C['schemes'][$x] = array_flip(explode(',', $x2));
        }
    }
    if (!isset($C['schemes']['*'])) {
        $C['schemes']['*'] = array(
            'file' => 1,
            'http' => 1,
            'https' => 1
        );
    }
    if (!empty($C['safe']) && empty($C['schemes']['style'])) {
        $C['schemes']['style'] = array(
            '!' => 1
        );
    }
    $C['abs_url'] = isset($C['abs_url']) ? $C['abs_url'] : 0;
    if (!isset($C['base_url']) or ! preg_match('`^[a-zA-Z\d.+\-]+://[^/]+/(.+?/)?$`', $C['base_url'])) {
        $C['base_url'] = $C['abs_url'] = 0;
    }
    // config rest
    $C['and_mark'] = empty($C['and_mark']) ? 0 : 1;
    $C['anti_link_spam'] = (isset($C['anti_link_spam']) && is_array($C['anti_link_spam']) && count($C['anti_link_spam']) == 2 && (empty($C['anti_link_spam'][0]) or hl_regex($C['anti_link_spam'][0])) && (empty($C['anti_link_spam'][1]) or hl_regex($C['anti_link_spam'][1]))) ? $C['anti_link_spam'] : 0;
    $C['anti_mail_spam'] = isset($C['anti_mail_spam']) ? $C['anti_mail_spam'] : 0;
    $C['balance'] = isset($C['balance']) ? (bool) $C['balance'] : 1;
    $C['cdata'] = isset($C['cdata']) ? $C['cdata'] : (empty($C['safe']) ? 3 : 0);
    $C['clean_ms_char'] = empty($C['clean_ms_char']) ? 0 : $C['clean_ms_char'];
    $C['comment'] = isset($C['comment']) ? $C['comment'] : (empty($C['safe']) ? 3 : 0);
    $C['css_expression'] = empty($C['css_expression']) ? 0 : 1;
    $C['direct_list_nest'] = empty($C['direct_list_nest']) ? 0 : 1;
    $C['hexdec_entity'] = isset($C['hexdec_entity']) ? $C['hexdec_entity'] : 1;
    $C['hook'] = (!empty($C['hook']) && function_exists($C['hook'])) ? $C['hook'] : 0;
    $C['hook_tag'] = (!empty($C['hook_tag']) && function_exists($C['hook_tag'])) ? $C['hook_tag'] : 0;
    $C['keep_bad'] = isset($C['keep_bad']) ? $C['keep_bad'] : 6;
    $C['lc_std_val'] = isset($C['lc_std_val']) ? (bool) $C['lc_std_val'] : 1;
    $C['make_tag_strict'] = isset($C['make_tag_strict']) ? $C['make_tag_strict'] : 1;
    $C['named_entity'] = isset($C['named_entity']) ? (bool) $C['named_entity'] : 1;
    $C['no_deprecated_attr'] = isset($C['no_deprecated_attr']) ? $C['no_deprecated_attr'] : 1;
    $C['parent'] = isset($C['parent'][0]) ? strtolower($C['parent']) : 'body';
    $C['show_setting'] = !empty($C['show_setting']) ? $C['show_setting'] : 0;
    $C['style_pass'] = empty($C['style_pass']) ? 0 : 1;
    $C['tidy'] = empty($C['tidy']) ? 0 : $C['tidy'];
    $C['unique_ids'] = isset($C['unique_ids']) ? $C['unique_ids'] : 1;
    $C['xml:lang'] = isset($C['xml:lang']) ? $C['xml:lang'] : 0;

    if (isset($GLOBALS['C'])) {
        $reC = $GLOBALS['C'];
    }
    $GLOBALS['C'] = $C;
    $S = is_array($S) ? $S : hl_spec($S);
    if (isset($GLOBALS['S'])) {
        $reS = $GLOBALS['S'];
    }
    $GLOBALS['S'] = $S;

    $t = preg_replace('`[\x00-\x08\x0b-\x0c\x0e-\x1f]`', '', $t);
    if ($C['clean_ms_char']) {
        $x = array(
            "\x7f" => '',
            "\x80" => '&#8364;',
            "\x81" => '',
            "\x83" => '&#402;',
            "\x85" => '&#8230;',
            "\x86" => '&#8224;',
            "\x87" => '&#8225;',
            "\x88" => '&#710;',
            "\x89" => '&#8240;',
            "\x8a" => '&#352;',
            "\x8b" => '&#8249;',
            "\x8c" => '&#338;',
            "\x8d" => '',
            "\x8e" => '&#381;',
            "\x8f" => '',
            "\x90" => '',
            "\x95" => '&#8226;',
            "\x96" => '&#8211;',
            "\x97" => '&#8212;',
            "\x98" => '&#732;',
            "\x99" => '&#8482;',
            "\x9a" => '&#353;',
            "\x9b" => '&#8250;',
            "\x9c" => '&#339;',
            "\x9d" => '',
            "\x9e" => '&#382;',
            "\x9f" => '&#376;'
        );
        $x = $x + ($C['clean_ms_char'] == 1 ? array(
                "\x82" => '&#8218;',
                "\x84" => '&#8222;',
                "\x91" => '&#8216;',
                "\x92" => '&#8217;',
                "\x93" => '&#8220;',
                "\x94" => '&#8221;'
                ) : array(
                "\x82" => '\'',
                "\x84" => '"',
                "\x91" => '\'',
                "\x92" => '\'',
                "\x93" => '"',
                "\x94" => '"'
        ));
        $t = strtr($t, $x);
    }
    if ($C['cdata'] or $C['comment']) {
        $t = preg_replace_callback('`<!(?:(?:--.*?--)|(?:\[CDATA\[.*?\]\]))>`sm', 'hl_cmtcd', $t);
    }
    $t = preg_replace_callback('`&amp;([A-Za-z][A-Za-z0-9]{1,30}|#(?:[0-9]{1,8}|[Xx][0-9A-Fa-f]{1,7}));`', 'hl_ent', str_replace('&', '&amp;', $t));
    if ($C['unique_ids'] && !isset($GLOBALS['hl_Ids'])) {
        $GLOBALS['hl_Ids'] = array();
    }
    if ($C['hook']) {
        $t = $C['hook']($t, $C, $S);
    }
    if ($C['show_setting'] && preg_match('`^[a-z][a-z0-9_]*$`i', $C['show_setting'])) {
        $GLOBALS[$C['show_setting']] = array(
            'config' => $C,
            'spec' => $S,
            'time' => microtime()
        );
    }
    // main
    $t = preg_replace_callback('`<(?:(?:\s|$)|(?:[^>]*(?:>|$)))|>`m', 'hl_tag', $t);
    $t = $C['balance'] ? hl_bal($t, $C['keep_bad'], $C['parent']) : $t;
    $t = (($C['cdata'] or $C['comment']) && strpos($t, "\x01") !== false) ? str_replace(array(
            "\x01",
            "\x02",
            "\x03",
            "\x04",
            "\x05"
            ), array(
            '',
            '',
            '&',
            '<',
            '>'
            ), $t) : $t;
    $t = $C['tidy'] ? hl_tidy($t, $C['tidy'], $C['parent']) : $t;
    unset($C, $e);
    if (isset($reC)) {
        $GLOBALS['C'] = $reC;
    }
    if (isset($reS)) {
        $GLOBALS['S'] = $reS;
    }
    return $t;
    // eof
}

function hl_attrval($t, $p)
{
    // check attr val against $S
    $o = 1;
    $l = strlen($t);
    foreach ($p as $k => $v) {
        switch ($k) {
            case 'maxlen':
                if ($l > $v) {
                    $o = 0;
                }
                break;
            case 'minlen':
                if ($l < $v) {
                    $o = 0;
                }
                break;
            case 'maxval':
                if ((float) ($t) > $v) {
                    $o = 0;
                }
                break;
            case 'minval':
                if ((float) ($t) < $v) {
                    $o = 0;
                }
                break;
            case 'match':
                if (!preg_match($v, $t)) {
                    $o = 0;
                }
                break;
            case 'nomatch':
                if (preg_match($v, $t)) {
                    $o = 0;
                }
                break;
            case 'oneof':
                $m = 0;
                foreach (explode('|', $v) as $n) {
                    if ($t == $n) {
                        $m = 1;
                        break;
                    }
                }
                $o = $m;
                break;
            case 'noneof':
                $m = 1;
                foreach (explode('|', $v) as $n) {
                    if ($t == $n) {
                        $m = 0;
                        break;
                    }
                }
                $o = $m;
                break;
            default:
                break;
        }
        if (!$o) {
            break;
        }
    }
    return ($o ? $t : (isset($p['default']) ? $p['default'] : 0));
    // eof
}

function hl_bal($t, $do = 1, $in = 'div')
{
    // balance tags
    // by content
    $cB = array(
        'blockquote' => 1,
        'form' => 1,
        'map' => 1,
        'noscript' => 1
    ); // Block
    $cE = array(
        'area' => 1,
        'br' => 1,
        'col' => 1,
        'embed' => 1,
        'hr' => 1,
        'img' => 1,
        'input' => 1,
        'isindex' => 1,
        'param' => 1
    ); // Empty
    $cF = array(
        'button' => 1,
        'del' => 1,
        'div' => 1,
        'dd' => 1,
        'fieldset' => 1,
        'iframe' => 1,
        'ins' => 1,
        'li' => 1,
        'noscript' => 1,
        'object' => 1,
        'td' => 1,
        'th' => 1
    ); // Flow; later context-wise dynamic move of ins & del to $cI
    $cI = array(
        'a' => 1,
        'abbr' => 1,
        'acronym' => 1,
        'address' => 1,
        'b' => 1,
        'bdo' => 1,
        'big' => 1,
        'caption' => 1,
        'cite' => 1,
        'code' => 1,
        'dfn' => 1,
        'dt' => 1,
        'em' => 1,
        'font' => 1,
        'h1' => 1,
        'h2' => 1,
        'h3' => 1,
        'h4' => 1,
        'h5' => 1,
        'h6' => 1,
        'i' => 1,
        'kbd' => 1,
        'label' => 1,
        'legend' => 1,
        'p' => 1,
        'pre' => 1,
        'q' => 1,
        'rb' => 1,
        'rt' => 1,
        's' => 1,
        'samp' => 1,
        'small' => 1,
        'span' => 1,
        'strike' => 1,
        'strong' => 1,
        'sub' => 1,
        'sup' => 1,
        'tt' => 1,
        'u' => 1,
        'var' => 1
    ); // Inline
    $cN = array(
        'a' => array(
            'a' => 1
        ),
        'button' => array(
            'a' => 1,
            'button' => 1,
            'fieldset' => 1,
            'form' => 1,
            'iframe' => 1,
            'input' => 1,
            'label' => 1,
            'select' => 1,
            'textarea' => 1
        ),
        'fieldset' => array(
            'fieldset' => 1
        ),
        'form' => array(
            'form' => 1
        ),
        'label' => array(
            'label' => 1
        ),
        'noscript' => array(
            'script' => 1
        ),
        'pre' => array(
            'big' => 1,
            'font' => 1,
            'img' => 1,
            'object' => 1,
            'script' => 1,
            'small' => 1,
            'sub' => 1,
            'sup' => 1
        ),
        'rb' => array(
            'ruby' => 1
        ),
        'rt' => array(
            'ruby' => 1
        )
    ); // Illegal
    $cN2 = array_keys($cN);
    $cR = array(
        'blockquote' => 1,
        'dir' => 1,
        'dl' => 1,
        'form' => 1,
        'map' => 1,
        'menu' => 1,
        'noscript' => 1,
        'ol' => 1,
        'optgroup' => 1,
        'rbc' => 1,
        'rtc' => 1,
        'ruby' => 1,
        'select' => 1,
        'table' => 1,
        'tbody' => 1,
        'tfoot' => 1,
        'thead' => 1,
        'tr' => 1,
        'ul' => 1
    );
    $cS = array(
        'colgroup' => array(
            'col' => 1
        ),
        'dir' => array(
            'li' => 1
        ),
        'dl' => array(
            'dd' => 1,
            'dt' => 1
        ),
        'menu' => array(
            'li' => 1
        ),
        'ol' => array(
            'li' => 1
        ),
        'optgroup' => array(
            'option' => 1
        ),
        'option' => array(
            '#pcdata' => 1
        ),
        'rbc' => array(
            'rb' => 1
        ),
        'rp' => array(
            '#pcdata' => 1
        ),
        'rtc' => array(
            'rt' => 1
        ),
        'ruby' => array(
            'rb' => 1,
            'rbc' => 1,
            'rp' => 1,
            'rt' => 1,
            'rtc' => 1
        ),
        'select' => array(
            'optgroup' => 1,
            'option' => 1
        ),
        'script' => array(
            '#pcdata' => 1
        ),
        'table' => array(
            'caption' => 1,
            'col' => 1,
            'colgroup' => 1,
            'tfoot' => 1,
            'tbody' => 1,
            'tr' => 1,
            'thead' => 1
        ),
        'tbody' => array(
            'tr' => 1
        ),
        'tfoot' => array(
            'tr' => 1
        ),
        'textarea' => array(
            '#pcdata' => 1
        ),
        'thead' => array(
            'tr' => 1
        ),
        'tr' => array(
            'td' => 1,
            'th' => 1
        ),
        'ul' => array(
            'li' => 1
        )
    ); // Specific - immediate parent-child
    if ($GLOBALS['C']['direct_list_nest']) {
        $cS['ol'] = $cS['ul'] += array(
            'ol' => 1,
            'ul' => 1
        );
    }
    $cO = array(
        'address' => array(
            'p' => 1
        ),
        'applet' => array(
            'param' => 1
        ),
        'blockquote' => array(
            'script' => 1
        ),
        'fieldset' => array(
            'legend' => 1,
            '#pcdata' => 1
        ),
        'form' => array(
            'script' => 1
        ),
        'map' => array(
            'area' => 1
        ),
        'object' => array(
            'param' => 1,
            'embed' => 1
        )
    ); // Other
    $cT = array(
        'colgroup' => 1,
        'dd' => 1,
        'dt' => 1,
        'li' => 1,
        'option' => 1,
        'p' => 1,
        'td' => 1,
        'tfoot' => 1,
        'th' => 1,
        'thead' => 1,
        'tr' => 1
    ); // Omitable closing
    // block/inline type; ins & del both type; #pcdata: text
    $eB = array(
        'address' => 1,
        'blockquote' => 1,
        'center' => 1,
        'del' => 1,
        'dir' => 1,
        'dl' => 1,
        'div' => 1,
        'fieldset' => 1,
        'form' => 1,
        'ins' => 1,
        'h1' => 1,
        'h2' => 1,
        'h3' => 1,
        'h4' => 1,
        'h5' => 1,
        'h6' => 1,
        'hr' => 1,
        'isindex' => 1,
        'menu' => 1,
        'noscript' => 1,
        'ol' => 1,
        'p' => 1,
        'pre' => 1,
        'table' => 1,
        'ul' => 1
    );
    $eI = array(
        '#pcdata' => 1,
        'a' => 1,
        'abbr' => 1,
        'acronym' => 1,
        'applet' => 1,
        'b' => 1,
        'bdo' => 1,
        'big' => 1,
        'br' => 1,
        'button' => 1,
        'cite' => 1,
        'code' => 1,
        'del' => 1,
        'dfn' => 1,
        'em' => 1,
        'embed' => 1,
        'font' => 1,
        'i' => 1,
        'iframe' => 1,
        'img' => 1,
        'input' => 1,
        'ins' => 1,
        'kbd' => 1,
        'label' => 1,
        'map' => 1,
        'object' => 1,
        'q' => 1,
        'ruby' => 1,
        's' => 1,
        'samp' => 1,
        'select' => 1,
        'script' => 1,
        'small' => 1,
        'span' => 1,
        'strike' => 1,
        'strong' => 1,
        'sub' => 1,
        'sup' => 1,
        'textarea' => 1,
        'tt' => 1,
        'u' => 1,
        'var' => 1
    );
    $eN = array(
        'a' => 1,
        'big' => 1,
        'button' => 1,
        'fieldset' => 1,
        'font' => 1,
        'form' => 1,
        'iframe' => 1,
        'img' => 1,
        'input' => 1,
        'label' => 1,
        'object' => 1,
        'ruby' => 1,
        'script' => 1,
        'select' => 1,
        'small' => 1,
        'sub' => 1,
        'sup' => 1,
        'textarea' => 1
    ); // Exclude from specific ele; $cN values
    $eO = array(
        'area' => 1,
        'caption' => 1,
        'col' => 1,
        'colgroup' => 1,
        'dd' => 1,
        'dt' => 1,
        'legend' => 1,
        'li' => 1,
        'optgroup' => 1,
        'option' => 1,
        'param' => 1,
        'rb' => 1,
        'rbc' => 1,
        'rp' => 1,
        'rt' => 1,
        'rtc' => 1,
        'script' => 1,
        'tbody' => 1,
        'td' => 1,
        'tfoot' => 1,
        'thead' => 1,
        'th' => 1,
        'tr' => 1
    ); // Missing in $eB & $eI
    $eF = $eB + $eI;

    // $in sets allowed child
    $in = ((isset($eF[$in]) && $in != '#pcdata') or isset($eO[$in])) ? $in : 'div';
    if (isset($cE[$in])) {
        return (!$do ? '' : str_replace(array(
                    '<',
                    '>'
                    ), array(
                    '&lt;',
                    '&gt;'
                    ), $t));
    }
    if (isset($cS[$in])) {
        $inOk = $cS[$in];
    } elseif (isset($cI[$in])) {
        $inOk = $eI;
        $cI['del'] = 1;
        $cI['ins'] = 1;
    } elseif (isset($cF[$in])) {
        $inOk = $eF;
        unset($cI['del'], $cI['ins']);
    } elseif (isset($cB[$in])) {
        $inOk = $eB;
        unset($cI['del'], $cI['ins']);
    }
    if (isset($cO[$in])) {
        $inOk = $inOk + $cO[$in];
    }
    if (isset($cN[$in])) {
        $inOk = array_diff_assoc($inOk, $cN[$in]);
    }

    $t = explode('<', $t);
    $ok = $q = array(); // $q seq list of open non-empty ele
    ob_start();

    for ($i = - 1, $ci = count($t); ++$i < $ci;) {
        // allowed $ok in parent $p
        if ($ql = count($q)) {
            $p = array_pop($q);
            $q[] = $p;
            if (isset($cS[$p])) {
                $ok = $cS[$p];
            } elseif (isset($cI[$p])) {
                $ok = $eI;
                $cI['del'] = 1;
                $cI['ins'] = 1;
            } elseif (isset($cF[$p])) {
                $ok = $eF;
                unset($cI['del'], $cI['ins']);
            } elseif (isset($cB[$p])) {
                $ok = $eB;
                unset($cI['del'], $cI['ins']);
            }
            if (isset($cO[$p])) {
                $ok = $ok + $cO[$p];
            }
            if (isset($cN[$p])) {
                $ok = array_diff_assoc($ok, $cN[$p]);
            }
        } else {
            $ok = $inOk;
            unset($cI['del'], $cI['ins']);
        }
        // bad tags, & ele content
        if (isset($e) && ($do == 1 or ( isset($ok['#pcdata']) && ($do == 3 or $do == 5)))) {
            echo '&lt;', $s, $e, $a, '&gt;';
        }
        if (isset($x[0])) {
            if (strlen(trim($x)) && (($ql && isset($cB[$p])) or ( isset($cB[$in]) && !$ql))) {
                echo '<div>', $x, '</div>';
            } elseif ($do < 3 or isset($ok['#pcdata'])) {
                echo $x;
            } elseif (strpos($x, "\x02\x04")) {
                foreach (preg_split('`(\x01\x02[^\x01\x02]+\x02\x01)`', $x, - 1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY) as $v) {
                    echo (substr($v, 0, 2) == "\x01\x02" ? $v : ($do > 4 ? preg_replace('`\S`', '', $v) : ''));
                }
            } elseif ($do > 4) {
                echo preg_replace('`\S`', '', $x);
            }
        }
        // get markup
        if (!preg_match('`^(/?)([a-z1-6]+)([^>]*)>(.*)`sm', $t[$i], $r)) {
            $x = $t[$i];
            continue;
        }
        $s = null;
        $e = null;
        $a = null;
        $x = null;
        list ($all, $s, $e, $a, $x) = $r;
        // close tag
        if ($s) {
            if (isset($cE[$e]) or ! in_array($e, $q)) {
                continue;
            } // Empty/unopen
            if ($p == $e) {
                array_pop($q);
                echo '</', $e, '>';
                unset($e);
                continue;
            } // Last open
            $add = ''; // Nesting - close open tags that need to be
            for ($j = - 1, $cj = count($q); ++$j < $cj;) {
                if (($d = array_pop($q)) == $e) {
                    break;
                } else {
                    $add .= "</{$d}>";
                }
            }
            echo $add, '</', $e, '>';
            unset($e);
            continue;
        }
        // open tag
        // $cB ele needs $eB ele as child
        if (isset($cB[$e]) && strlen(trim($x))) {
            $t[$i] = "{$e}{$a}>";
            array_splice($t, $i + 1, 0, 'div>' . $x);
            unset($e, $x);
            ++$ci;
            --$i;
            continue;
        }
        if ((($ql && isset($cB[$p])) or ( isset($cB[$in]) && !$ql)) && !isset($eB[$e]) && !isset($ok[$e])) {
            array_splice($t, $i, 0, 'div>');
            unset($e, $x);
            ++$ci;
            --$i;
            continue;
        }
        // if no open ele, $in = parent; mostly immediate parent-child relation should hold
        if (!$ql or ! isset($eN[$e]) or ! array_intersect($q, $cN2)) {
            if (!isset($ok[$e])) {
                if ($ql && isset($cT[$p])) {
                    echo '</', array_pop($q), '>';
                    unset($e, $x);
                    --$i;
                }
                continue;
            }
            if (!isset($cE[$e])) {
                $q[] = $e;
            }
            echo '<', $e, $a, '>';
            unset($e);
            continue;
        }
        // specific parent-child
        if (isset($cS[$p][$e])) {
            if (!isset($cE[$e])) {
                $q[] = $e;
            }
            echo '<', $e, $a, '>';
            unset($e);
            continue;
        }
        // nesting
        $add = '';
        $q2 = array();
        for ($k = - 1, $kc = count($q); ++$k < $kc;) {
            $d = $q[$k];
            $ok2 = array();
            if (isset($cS[$d])) {
                $q2[] = $d;
                continue;
            }
            $ok2 = isset($cI[$d]) ? $eI : $eF;
            if (isset($cO[$d])) {
                $ok2 = $ok2 + $cO[$d];
            }
            if (isset($cN[$d])) {
                $ok2 = array_diff_assoc($ok2, $cN[$d]);
            }
            if (!isset($ok2[$e])) {
                if (!$k && !isset($inOk[$e])) {
                    continue 2;
                }
                $add = "</{$d}>";
                for (; ++$k < $kc;) {
                    $add = "</{$q[$k]}>{$add}";
                }
                break;
            } else {
                $q2[] = $d;
            }
        }
        $q = $q2;
        if (!isset($cE[$e])) {
            $q[] = $e;
        }
        echo $add, '<', $e, $a, '>';
        unset($e);
        continue;
    }

    // end
    if ($ql = count($q)) {
        $p = array_pop($q);
        $q[] = $p;
        if (isset($cS[$p])) {
            $ok = $cS[$p];
        } elseif (isset($cI[$p])) {
            $ok = $eI;
            $cI['del'] = 1;
            $cI['ins'] = 1;
        } elseif (isset($cF[$p])) {
            $ok = $eF;
            unset($cI['del'], $cI['ins']);
        } elseif (isset($cB[$p])) {
            $ok = $eB;
            unset($cI['del'], $cI['ins']);
        }
        if (isset($cO[$p])) {
            $ok = $ok + $cO[$p];
        }
        if (isset($cN[$p])) {
            $ok = array_diff_assoc($ok, $cN[$p]);
        }
    } else {
        $ok = $inOk;
        unset($cI['del'], $cI['ins']);
    }
    if (isset($e) && ($do == 1 or ( isset($ok['#pcdata']) && ($do == 3 or $do == 5)))) {
        echo '&lt;', $s, $e, $a, '&gt;';
    }
    if (isset($x[0])) {
        if (strlen(trim($x)) && (($ql && isset($cB[$p])) or ( isset($cB[$in]) && !$ql))) {
            echo '<div>', $x, '</div>';
        } elseif ($do < 3 or isset($ok['#pcdata'])) {
            echo $x;
        } elseif (strpos($x, "\x02\x04")) {
            foreach (preg_split('`(\x01\x02[^\x01\x02]+\x02\x01)`', $x, - 1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY) as $v) {
                echo (substr($v, 0, 2) == "\x01\x02" ? $v : ($do > 4 ? preg_replace('`\S`', '', $v) : ''));
            }
        } elseif ($do > 4) {
            echo preg_replace('`\S`', '', $x);
        }
    }
    while (!empty($q) && ($e = array_pop($q))) {
        echo '</', $e, '>';
    }
    $o = ob_get_contents();
    ob_end_clean();
    return $o;
    // eof
}

function hl_cmtcd($t)
{
    // comment/CDATA sec handler
    $t = $t[0];
    global $C;
    if (!($v = $C[$n = $t[3] == '-' ? 'comment' : 'cdata'])) {
        return $t;
    }
    if ($v == 1) {
        return '';
    }
    if ($n == 'comment') {
        if (substr(($t = preg_replace('`--+`', '-', substr($t, 4, - 3))), - 1) != ' ') {
            $t .= ' ';
        }
    } else {
        $t = substr($t, 1, - 1);
    }
    $t = $v == 2 ? str_replace(array(
            '&',
            '<',
            '>'
            ), array(
            '&amp;',
            '&lt;',
            '&gt;'
            ), $t) : $t;
    return str_replace(array(
        '&',
        '<',
        '>'
        ), array(
        "\x03",
        "\x04",
        "\x05"
        ), ($n == 'comment' ? "\x01\x02\x04!--$t--\x05\x02\x01" : "\x01\x01\x04$t\x05\x01\x01"));
    // eof
}

function hl_ent($t)
{
    // entitity handler
    global $C;
    $t = $t[1];
    static $U = array(
        'quot' => 1,
        'amp' => 1,
        'lt' => 1,
        'gt' => 1
    );
    static $N = array(
        'fnof' => '402',
        'Alpha' => '913',
        'Beta' => '914',
        'Gamma' => '915',
        'Delta' => '916',
        'Epsilon' => '917',
        'Zeta' => '918',
        'Eta' => '919',
        'Theta' => '920',
        'Iota' => '921',
        'Kappa' => '922',
        'Lambda' => '923',
        'Mu' => '924',
        'Nu' => '925',
        'Xi' => '926',
        'Omicron' => '927',
        'Pi' => '928',
        'Rho' => '929',
        'Sigma' => '931',
        'Tau' => '932',
        'Upsilon' => '933',
        'Phi' => '934',
        'Chi' => '935',
        'Psi' => '936',
        'Omega' => '937',
        'alpha' => '945',
        'beta' => '946',
        'gamma' => '947',
        'delta' => '948',
        'epsilon' => '949',
        'zeta' => '950',
        'eta' => '951',
        'theta' => '952',
        'iota' => '953',
        'kappa' => '954',
        'lambda' => '955',
        'mu' => '956',
        'nu' => '957',
        'xi' => '958',
        'omicron' => '959',
        'pi' => '960',
        'rho' => '961',
        'sigmaf' => '962',
        'sigma' => '963',
        'tau' => '964',
        'upsilon' => '965',
        'phi' => '966',
        'chi' => '967',
        'psi' => '968',
        'omega' => '969',
        'thetasym' => '977',
        'upsih' => '978',
        'piv' => '982',
        'bull' => '8226',
        'hellip' => '8230',
        'prime' => '8242',
        'Prime' => '8243',
        'oline' => '8254',
        'frasl' => '8260',
        'weierp' => '8472',
        'image' => '8465',
        'real' => '8476',
        'trade' => '8482',
        'alefsym' => '8501',
        'larr' => '8592',
        'uarr' => '8593',
        'rarr' => '8594',
        'darr' => '8595',
        'harr' => '8596',
        'crarr' => '8629',
        'lArr' => '8656',
        'uArr' => '8657',
        'rArr' => '8658',
        'dArr' => '8659',
        'hArr' => '8660',
        'forall' => '8704',
        'part' => '8706',
        'exist' => '8707',
        'empty' => '8709',
        'nabla' => '8711',
        'isin' => '8712',
        'notin' => '8713',
        'ni' => '8715',
        'prod' => '8719',
        'sum' => '8721',
        'minus' => '8722',
        'lowast' => '8727',
        'radic' => '8730',
        'prop' => '8733',
        'infin' => '8734',
        'ang' => '8736',
        'and' => '8743',
        'or' => '8744',
        'cap' => '8745',
        'cup' => '8746',
        'int' => '8747',
        'there4' => '8756',
        'sim' => '8764',
        'cong' => '8773',
        'asymp' => '8776',
        'ne' => '8800',
        'equiv' => '8801',
        'le' => '8804',
        'ge' => '8805',
        'sub' => '8834',
        'sup' => '8835',
        'nsub' => '8836',
        'sube' => '8838',
        'supe' => '8839',
        'oplus' => '8853',
        'otimes' => '8855',
        'perp' => '8869',
        'sdot' => '8901',
        'lceil' => '8968',
        'rceil' => '8969',
        'lfloor' => '8970',
        'rfloor' => '8971',
        'lang' => '9001',
        'rang' => '9002',
        'loz' => '9674',
        'spades' => '9824',
        'clubs' => '9827',
        'hearts' => '9829',
        'diams' => '9830',
        'apos' => '39',
        'OElig' => '338',
        'oelig' => '339',
        'Scaron' => '352',
        'scaron' => '353',
        'Yuml' => '376',
        'circ' => '710',
        'tilde' => '732',
        'ensp' => '8194',
        'emsp' => '8195',
        'thinsp' => '8201',
        'zwnj' => '8204',
        'zwj' => '8205',
        'lrm' => '8206',
        'rlm' => '8207',
        'ndash' => '8211',
        'mdash' => '8212',
        'lsquo' => '8216',
        'rsquo' => '8217',
        'sbquo' => '8218',
        'ldquo' => '8220',
        'rdquo' => '8221',
        'bdquo' => '8222',
        'dagger' => '8224',
        'Dagger' => '8225',
        'permil' => '8240',
        'lsaquo' => '8249',
        'rsaquo' => '8250',
        'euro' => '8364',
        'nbsp' => '160',
        'iexcl' => '161',
        'cent' => '162',
        'pound' => '163',
        'curren' => '164',
        'yen' => '165',
        'brvbar' => '166',
        'sect' => '167',
        'uml' => '168',
        'copy' => '169',
        'ordf' => '170',
        'laquo' => '171',
        'not' => '172',
        'shy' => '173',
        'reg' => '174',
        'macr' => '175',
        'deg' => '176',
        'plusmn' => '177',
        'sup2' => '178',
        'sup3' => '179',
        'acute' => '180',
        'micro' => '181',
        'para' => '182',
        'middot' => '183',
        'cedil' => '184',
        'sup1' => '185',
        'ordm' => '186',
        'raquo' => '187',
        'frac14' => '188',
        'frac12' => '189',
        'frac34' => '190',
        'iquest' => '191',
        'Agrave' => '192',
        'Aacute' => '193',
        'Acirc' => '194',
        'Atilde' => '195',
        'Auml' => '196',
        'Aring' => '197',
        'AElig' => '198',
        'Ccedil' => '199',
        'Egrave' => '200',
        'Eacute' => '201',
        'Ecirc' => '202',
        'Euml' => '203',
        'Igrave' => '204',
        'Iacute' => '205',
        'Icirc' => '206',
        'Iuml' => '207',
        'ETH' => '208',
        'Ntilde' => '209',
        'Ograve' => '210',
        'Oacute' => '211',
        'Ocirc' => '212',
        'Otilde' => '213',
        'Ouml' => '214',
        'times' => '215',
        'Oslash' => '216',
        'Ugrave' => '217',
        'Uacute' => '218',
        'Ucirc' => '219',
        'Uuml' => '220',
        'Yacute' => '221',
        'THORN' => '222',
        'szlig' => '223',
        'agrave' => '224',
        'aacute' => '225',
        'acirc' => '226',
        'atilde' => '227',
        'auml' => '228',
        'aring' => '229',
        'aelig' => '230',
        'ccedil' => '231',
        'egrave' => '232',
        'eacute' => '233',
        'ecirc' => '234',
        'euml' => '235',
        'igrave' => '236',
        'iacute' => '237',
        'icirc' => '238',
        'iuml' => '239',
        'eth' => '240',
        'ntilde' => '241',
        'ograve' => '242',
        'oacute' => '243',
        'ocirc' => '244',
        'otilde' => '245',
        'ouml' => '246',
        'divide' => '247',
        'oslash' => '248',
        'ugrave' => '249',
        'uacute' => '250',
        'ucirc' => '251',
        'uuml' => '252',
        'yacute' => '253',
        'thorn' => '254',
        'yuml' => '255'
    );
    if ($t[0] != '#') {
        return ($C['and_mark'] ? "\x06" : '&') . (isset($U[$t]) ? $t : (isset($N[$t]) ? (!$C['named_entity'] ? '#' . ($C['hexdec_entity'] > 1 ? 'x' . dechex($N[$t]) : $N[$t]) : $t) : 'amp;' . $t)) . ';';
    }
    if (($n = ctype_digit($t = substr($t, 1)) ? intval($t) : hexdec(substr($t, 1))) < 9 or ( $n > 13 && $n < 32) or $n == 11 or $n == 12 or ( $n > 126 && $n < 160 && $n != 133) or ( $n > 55295 && ($n < 57344 or ( $n > 64975 && $n < 64992) or $n == 65534 or $n == 65535 or $n > 1114111))) {
        return ($C['and_mark'] ? "\x06" : '&') . "amp;#{$t};";
    }
    return ($C['and_mark'] ? "\x06" : '&') . '#' . (((ctype_digit($t) && $C['hexdec_entity'] < 2) or ! $C['hexdec_entity']) ? $n : 'x' . dechex($n)) . ';';
    // eof
}

function hl_prot($p, $c = null)
{
    // check URL scheme
    global $C;
    $b = $a = '';
    if ($c == null) {
        $c = 'style';
        $b = $p[1];
        $a = $p[3];
        $p = trim($p[2]);
    }
    $c = isset($C['schemes'][$c]) ? $C['schemes'][$c] : $C['schemes']['*'];
    static $d = 'denied:';
    if (isset($c['!']) && substr($p, 0, 7) != $d) {
        $p = "$d$p";
    }
    if (isset($c['*']) or ! strcspn($p, '#?;') or ( substr($p, 0, 7) == $d)) {
        return "{$b}{$p}{$a}";
    } // All ok, frag, query, param
    if (preg_match('`^([^:?[@!$()*,=/\'\]]+?)(:|&#(58|x3a);|%3a|\\\\0{0,4}3a).`i', $p, $m) && !isset($c[strtolower($m[1])])) { // Denied prot
        return "{$b}{$d}{$p}{$a}";
    }
    if ($C['abs_url']) {
        if ($C['abs_url'] == - 1 && strpos($p, $C['base_url']) === 0) { // Make url rel
            $p = substr($p, strlen($C['base_url']));
        } elseif (empty($m[1])) { // Make URL abs
            if (substr($p, 0, 2) == '//') {
                $p = substr($C['base_url'], 0, strpos($C['base_url'], ':') + 1) . $p;
            } elseif ($p[0] == '/') {
                $p = preg_replace('`(^.+?://[^/]+)(.*)`', '$1', $C['base_url']) . $p;
            } elseif (strcspn($p, './')) {
                $p = $C['base_url'] . $p;
            } else {
                preg_match('`^([a-zA-Z\d\-+.]+://[^/]+)(.*)`', $C['base_url'], $m);
                $p = preg_replace('`(?<=/)\./`', '', $m[2] . $p);
                while (preg_match('`(?<=/)([^/]{3,}|[^/.]+?|\.[^/.]|[^/.]\.)/\.\./`', $p)) {
                    $p = preg_replace('`(?<=/)([^/]{3,}|[^/.]+?|\.[^/.]|[^/.]\.)/\.\./`', '', $p);
                }
                $p = $m[1] . $p;
            }
        }
    }
    return "{$b}{$p}{$a}";
    // eof
}

function hl_regex($p)
{
    // ?regex
    if (empty($p)) {
        return 0;
    }
    if ($t = ini_get('track_errors')) {
        $o = isset($php_errormsg) ? $php_errormsg : null;
    } else {
        ini_set('track_errors', 1);
    }
    unset($php_errormsg);
    if (($d = ini_get('display_errors'))) {
        ini_set('display_errors', 0);
    }
    preg_match($p, '');
    if ($d) {
        ini_set('display_errors', 1);
    }
    $r = isset($php_errormsg) ? 0 : 1;
    if ($t) {
        $php_errormsg = isset($o) ? $o : null;
    } else {
        ini_set('track_errors', 0);
    }
    return $r;
    // eof
}

function hl_spec($t)
{
    // final $spec
    $s = array();
    $t = str_replace(array(
        "\t",
        "\r",
        "\n",
        ' '
        ), '', preg_replace_callback('/"(?>(`.|[^"])*)"/sm', create_function('$m', 'return substr(str_replace(array(";", "|", "~", " ", ",", "/", "(", ")", \'`"\'), array("\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", "\x08", "\""), $m[0]), 1, -1);'), trim($t)));
    for ($i = count(($t = explode(';', $t))); --$i >= 0;) {
        $w = $t[$i];
        if (empty($w) or ( $e = strpos($w, '=')) === false or ! strlen(($a = substr($w, $e + 1)))) {
            continue;
        }
        $y = $n = array();
        foreach (explode(',', $a) as $v) {
            if (!preg_match('`^([a-z:\-\*]+)(?:\((.*?)\))?`i', $v, $m)) {
                continue;
            }
            if (($x = strtolower($m[1])) == '-*') {
                $n['*'] = 1;
                continue;
            }
            if ($x[0] == '-') {
                $n[substr($x, 1)] = 1;
                continue;
            }
            if (!isset($m[2])) {
                $y[$x] = 1;
                continue;
            }
            foreach (explode('/', $m[2]) as $m) {
                if (empty($m) or ( $p = strpos($m, '=')) == 0 or $p < 5) {
                    $y[$x] = 1;
                    continue;
                }
                $y[$x][strtolower(substr($m, 0, $p))] = str_replace(array(
                    "\x01",
                    "\x02",
                    "\x03",
                    "\x04",
                    "\x05",
                    "\x06",
                    "\x07",
                    "\x08"
                    ), array(
                    ";",
                    "|",
                    "~",
                    " ",
                    ",",
                    "/",
                    "(",
                    ")"
                    ), substr($m, $p + 1));
            }
            if (isset($y[$x]['match']) && !hl_regex($y[$x]['match'])) {
                unset($y[$x]['match']);
            }
            if (isset($y[$x]['nomatch']) && !hl_regex($y[$x]['nomatch'])) {
                unset($y[$x]['nomatch']);
            }
        }
        if (!count($y) && !count($n)) {
            continue;
        }
        foreach (explode(',', substr($w, 0, $e)) as $v) {
            if (!strlen(($v = strtolower($v)))) {
                continue;
            }
            if (count($y)) {
                $s[$v] = $y;
            }
            if (count($n)) {
                $s[$v]['n'] = $n;
            }
        }
    }
    return $s;
    // eof
}

function hl_tag($t)
{
    // tag/attribute handler
    global $C;
    $t = $t[0];
    // invalid < >
    if ($t == '< ') {
        return '&lt; ';
    }
    if ($t == '>') {
        return '&gt;';
    }
    if (!preg_match('`^<(/?)([a-zA-Z][a-zA-Z1-6]*)([^>]*?)\s?>$`m', $t, $m)) {
        return str_replace(array(
            '<',
            '>'
            ), array(
            '&lt;',
            '&gt;'
            ), $t);
    } elseif (!isset($C['elements'][($e = strtolower($m[2]))])) {
        return (($C['keep_bad'] % 2) ? str_replace(array(
                    '<',
                    '>'
                    ), array(
                    '&lt;',
                    '&gt;'
                    ), $t) : '');
    }
    // attr string
    $a = str_replace(array(
        "\n",
        "\r",
        "\t"
        ), ' ', trim($m[3]));
    // tag transform
    static $eD = array(
        'applet' => 1,
        'center' => 1,
        'dir' => 1,
        'embed' => 1,
        'font' => 1,
        'isindex' => 1,
        'menu' => 1,
        's' => 1,
        'strike' => 1,
        'u' => 1
    ); // Deprecated
    if ($C['make_tag_strict'] && isset($eD[$e])) {
        $trt = hl_tag2($e, $a, $C['make_tag_strict']);
        if (!$e) {
            return (($C['keep_bad'] % 2) ? str_replace(array(
                        '<',
                        '>'
                        ), array(
                        '&lt;',
                        '&gt;'
                        ), $t) : '');
        }
    }
    // close tag
    static $eE = array(
        'area' => 1,
        'br' => 1,
        'col' => 1,
        'embed' => 1,
        'hr' => 1,
        'img' => 1,
        'input' => 1,
        'isindex' => 1,
        'param' => 1
    ); // Empty ele
    if (!empty($m[1])) {
        return (!isset($eE[$e]) ? (empty($C['hook_tag']) ? "</$e>" : $C['hook_tag']($e)) : (($C['keep_bad']) % 2 ? str_replace(array(
                        '<',
                        '>'
                        ), array(
                        '&lt;',
                        '&gt;'
                        ), $t) : ''));
    }

    // open tag & attr
    static $aN = array(
        'abbr' => array(
            'td' => 1,
            'th' => 1
        ),
        'accept-charset' => array(
            'form' => 1
        ),
        'accept' => array(
            'form' => 1,
            'input' => 1
        ),
        'accesskey' => array(
            'a' => 1,
            'area' => 1,
            'button' => 1,
            'input' => 1,
            'label' => 1,
            'legend' => 1,
            'textarea' => 1
        ),
        'action' => array(
            'form' => 1
        ),
        'align' => array(
            'caption' => 1,
            'embed' => 1,
            'applet' => 1,
            'iframe' => 1,
            'img' => 1,
            'input' => 1,
            'object' => 1,
            'legend' => 1,
            'table' => 1,
            'hr' => 1,
            'div' => 1,
            'h1' => 1,
            'h2' => 1,
            'h3' => 1,
            'h4' => 1,
            'h5' => 1,
            'h6' => 1,
            'p' => 1,
            'col' => 1,
            'colgroup' => 1,
            'tbody' => 1,
            'td' => 1,
            'tfoot' => 1,
            'th' => 1,
            'thead' => 1,
            'tr' => 1
        ),
        'alt' => array(
            'applet' => 1,
            'area' => 1,
            'img' => 1,
            'input' => 1
        ),
        'archive' => array(
            'applet' => 1,
            'object' => 1
        ),
        'axis' => array(
            'td' => 1,
            'th' => 1
        ),
        'bgcolor' => array(
            'embed' => 1,
            'table' => 1,
            'tr' => 1,
            'td' => 1,
            'th' => 1
        ),
        'border' => array(
            'table' => 1,
            'img' => 1,
            'object' => 1
        ),
        'bordercolor' => array(
            'table' => 1,
            'td' => 1,
            'tr' => 1
        ),
        'cellpadding' => array(
            'table' => 1
        ),
        'cellspacing' => array(
            'table' => 1
        ),
        'char' => array(
            'col' => 1,
            'colgroup' => 1,
            'tbody' => 1,
            'td' => 1,
            'tfoot' => 1,
            'th' => 1,
            'thead' => 1,
            'tr' => 1
        ),
        'charoff' => array(
            'col' => 1,
            'colgroup' => 1,
            'tbody' => 1,
            'td' => 1,
            'tfoot' => 1,
            'th' => 1,
            'thead' => 1,
            'tr' => 1
        ),
        'charset' => array(
            'a' => 1,
            'script' => 1
        ),
        'checked' => array(
            'input' => 1
        ),
        'cite' => array(
            'blockquote' => 1,
            'q' => 1,
            'del' => 1,
            'ins' => 1
        ),
        'classid' => array(
            'object' => 1
        ),
        'clear' => array(
            'br' => 1
        ),
        'code' => array(
            'applet' => 1
        ),
        'codebase' => array(
            'object' => 1,
            'applet' => 1
        ),
        'codetype' => array(
            'object' => 1
        ),
        'color' => array(
            'font' => 1
        ),
        'cols' => array(
            'textarea' => 1
        ),
        'colspan' => array(
            'td' => 1,
            'th' => 1
        ),
        'compact' => array(
            'dir' => 1,
            'dl' => 1,
            'menu' => 1,
            'ol' => 1,
            'ul' => 1
        ),
        'coords' => array(
            'area' => 1,
            'a' => 1
        ),
        'data' => array(
            'object' => 1
        ),
        'datetime' => array(
            'del' => 1,
            'ins' => 1
        ),
        'declare' => array(
            'object' => 1
        ),
        'defer' => array(
            'script' => 1
        ),
        'dir' => array(
            'bdo' => 1
        ),
        'disabled' => array(
            'button' => 1,
            'input' => 1,
            'optgroup' => 1,
            'option' => 1,
            'select' => 1,
            'textarea' => 1
        ),
        'enctype' => array(
            'form' => 1
        ),
        'face' => array(
            'font' => 1
        ),
        'flashvars' => array(
            'embed' => 1
        ),
        'for' => array(
            'label' => 1
        ),
        'frame' => array(
            'table' => 1
        ),
        'frameborder' => array(
            'iframe' => 1
        ),
        'headers' => array(
            'td' => 1,
            'th' => 1
        ),
        'height' => array(
            'embed' => 1,
            'iframe' => 1,
            'td' => 1,
            'th' => 1,
            'img' => 1,
            'object' => 1,
            'applet' => 1
        ),
        'href' => array(
            'a' => 1,
            'area' => 1
        ),
        'hreflang' => array(
            'a' => 1
        ),
        'hspace' => array(
            'applet' => 1,
            'img' => 1,
            'object' => 1
        ),
        'ismap' => array(
            'img' => 1,
            'input' => 1
        ),
        'label' => array(
            'option' => 1,
            'optgroup' => 1
        ),
        'language' => array(
            'script' => 1
        ),
        'longdesc' => array(
            'img' => 1,
            'iframe' => 1
        ),
        'marginheight' => array(
            'iframe' => 1
        ),
        'marginwidth' => array(
            'iframe' => 1
        ),
        'maxlength' => array(
            'input' => 1
        ),
        'method' => array(
            'form' => 1
        ),
        'model' => array(
            'embed' => 1
        ),
        'multiple' => array(
            'select' => 1
        ),
        'name' => array(
            'button' => 1,
            'embed' => 1,
            'textarea' => 1,
            'applet' => 1,
            'select' => 1,
            'form' => 1,
            'iframe' => 1,
            'img' => 1,
            'a' => 1,
            'input' => 1,
            'object' => 1,
            'map' => 1,
            'param' => 1
        ),
        'nohref' => array(
            'area' => 1
        ),
        'noshade' => array(
            'hr' => 1
        ),
        'nowrap' => array(
            'td' => 1,
            'th' => 1
        ),
        'object' => array(
            'applet' => 1
        ),
        'onblur' => array(
            'a' => 1,
            'area' => 1,
            'button' => 1,
            'input' => 1,
            'label' => 1,
            'select' => 1,
            'textarea' => 1
        ),
        'onchange' => array(
            'input' => 1,
            'select' => 1,
            'textarea' => 1
        ),
        'onfocus' => array(
            'a' => 1,
            'area' => 1,
            'button' => 1,
            'input' => 1,
            'label' => 1,
            'select' => 1,
            'textarea' => 1
        ),
        'onreset' => array(
            'form' => 1
        ),
        'onselect' => array(
            'input' => 1,
            'textarea' => 1
        ),
        'onsubmit' => array(
            'form' => 1
        ),
        'pluginspage' => array(
            'embed' => 1
        ),
        'pluginurl' => array(
            'embed' => 1
        ),
        'prompt' => array(
            'isindex' => 1
        ),
        'readonly' => array(
            'textarea' => 1,
            'input' => 1
        ),
        'rel' => array(
            'a' => 1
        ),
        'rev' => array(
            'a' => 1
        ),
        'rows' => array(
            'textarea' => 1
        ),
        'rowspan' => array(
            'td' => 1,
            'th' => 1
        ),
        'rules' => array(
            'table' => 1
        ),
        'scope' => array(
            'td' => 1,
            'th' => 1
        ),
        'scrolling' => array(
            'iframe' => 1
        ),
        'selected' => array(
            'option' => 1
        ),
        'shape' => array(
            'area' => 1,
            'a' => 1
        ),
        'size' => array(
            'hr' => 1,
            'font' => 1,
            'input' => 1,
            'select' => 1
        ),
        'span' => array(
            'col' => 1,
            'colgroup' => 1
        ),
        'src' => array(
            'embed' => 1,
            'script' => 1,
            'input' => 1,
            'iframe' => 1,
            'img' => 1
        ),
        'standby' => array(
            'object' => 1
        ),
        'start' => array(
            'ol' => 1
        ),
        'summary' => array(
            'table' => 1
        ),
        'tabindex' => array(
            'a' => 1,
            'area' => 1,
            'button' => 1,
            'input' => 1,
            'object' => 1,
            'select' => 1,
            'textarea' => 1
        ),
        'target' => array(
            'a' => 1,
            'area' => 1,
            'form' => 1
        ),
        'type' => array(
            'a' => 1,
            'embed' => 1,
            'object' => 1,
            'param' => 1,
            'script' => 1,
            'input' => 1,
            'li' => 1,
            'ol' => 1,
            'ul' => 1,
            'button' => 1
        ),
        'usemap' => array(
            'img' => 1,
            'input' => 1,
            'object' => 1
        ),
        'valign' => array(
            'col' => 1,
            'colgroup' => 1,
            'tbody' => 1,
            'td' => 1,
            'tfoot' => 1,
            'th' => 1,
            'thead' => 1,
            'tr' => 1
        ),
        'value' => array(
            'input' => 1,
            'option' => 1,
            'param' => 1,
            'button' => 1,
            'li' => 1
        ),
        'valuetype' => array(
            'param' => 1
        ),
        'vspace' => array(
            'applet' => 1,
            'img' => 1,
            'object' => 1
        ),
        'width' => array(
            'embed' => 1,
            'hr' => 1,
            'iframe' => 1,
            'img' => 1,
            'object' => 1,
            'table' => 1,
            'td' => 1,
            'th' => 1,
            'applet' => 1,
            'col' => 1,
            'colgroup' => 1,
            'pre' => 1
        ),
        'wmode' => array(
            'embed' => 1
        ),
        'xml:space' => array(
            'pre' => 1,
            'script' => 1,
            'style' => 1
        )
    ); // Ele-specific
    static $aNE = array(
        'checked' => 1,
        'compact' => 1,
        'declare' => 1,
        'defer' => 1,
        'disabled' => 1,
        'ismap' => 1,
        'multiple' => 1,
        'nohref' => 1,
        'noresize' => 1,
        'noshade' => 1,
        'nowrap' => 1,
        'readonly' => 1,
        'selected' => 1
    ); // Empty
    static $aNP = array(
        'action' => 1,
        'cite' => 1,
        'classid' => 1,
        'codebase' => 1,
        'data' => 1,
        'href' => 1,
        'longdesc' => 1,
        'model' => 1,
        'pluginspage' => 1,
        'pluginurl' => 1,
        'usemap' => 1
    ); // Need scheme check; excludes style, on* & src
    static $aNU = array(
        'class' => array(
            'param' => 1,
            'script' => 1
        ),
        'dir' => array(
            'applet' => 1,
            'bdo' => 1,
            'br' => 1,
            'iframe' => 1,
            'param' => 1,
            'script' => 1
        ),
        'id' => array(
            'script' => 1
        ),
        'lang' => array(
            'applet' => 1,
            'br' => 1,
            'iframe' => 1,
            'param' => 1,
            'script' => 1
        ),
        'xml:lang' => array(
            'applet' => 1,
            'br' => 1,
            'iframe' => 1,
            'param' => 1,
            'script' => 1
        ),
        'onclick' => array(
            'applet' => 1,
            'bdo' => 1,
            'br' => 1,
            'font' => 1,
            'iframe' => 1,
            'isindex' => 1,
            'param' => 1,
            'script' => 1
        ),
        'ondblclick' => array(
            'applet' => 1,
            'bdo' => 1,
            'br' => 1,
            'font' => 1,
            'iframe' => 1,
            'isindex' => 1,
            'param' => 1,
            'script' => 1
        ),
        'onkeydown' => array(
            'applet' => 1,
            'bdo' => 1,
            'br' => 1,
            'font' => 1,
            'iframe' => 1,
            'isindex' => 1,
            'param' => 1,
            'script' => 1
        ),
        'onkeypress' => array(
            'applet' => 1,
            'bdo' => 1,
            'br' => 1,
            'font' => 1,
            'iframe' => 1,
            'isindex' => 1,
            'param' => 1,
            'script' => 1
        ),
        'onkeyup' => array(
            'applet' => 1,
            'bdo' => 1,
            'br' => 1,
            'font' => 1,
            'iframe' => 1,
            'isindex' => 1,
            'param' => 1,
            'script' => 1
        ),
        'onmousedown' => array(
            'applet' => 1,
            'bdo' => 1,
            'br' => 1,
            'font' => 1,
            'iframe' => 1,
            'isindex' => 1,
            'param' => 1,
            'script' => 1
        ),
        'onmousemove' => array(
            'applet' => 1,
            'bdo' => 1,
            'br' => 1,
            'font' => 1,
            'iframe' => 1,
            'isindex' => 1,
            'param' => 1,
            'script' => 1
        ),
        'onmouseout' => array(
            'applet' => 1,
            'bdo' => 1,
            'br' => 1,
            'font' => 1,
            'iframe' => 1,
            'isindex' => 1,
            'param' => 1,
            'script' => 1
        ),
        'onmouseover' => array(
            'applet' => 1,
            'bdo' => 1,
            'br' => 1,
            'font' => 1,
            'iframe' => 1,
            'isindex' => 1,
            'param' => 1,
            'script' => 1
        ),
        'onmouseup' => array(
            'applet' => 1,
            'bdo' => 1,
            'br' => 1,
            'font' => 1,
            'iframe' => 1,
            'isindex' => 1,
            'param' => 1,
            'script' => 1
        ),
        'style' => array(
            'param' => 1,
            'script' => 1
        ),
        'title' => array(
            'param' => 1,
            'script' => 1
        )
    ); // Univ & exceptions

    if ($C['lc_std_val']) {
        // predef attr vals for $eAL & $aNE ele
        static $aNL = array(
            'all' => 1,
            'baseline' => 1,
            'bottom' => 1,
            'button' => 1,
            'center' => 1,
            'char' => 1,
            'checkbox' => 1,
            'circle' => 1,
            'col' => 1,
            'colgroup' => 1,
            'cols' => 1,
            'data' => 1,
            'default' => 1,
            'file' => 1,
            'get' => 1,
            'groups' => 1,
            'hidden' => 1,
            'image' => 1,
            'justify' => 1,
            'left' => 1,
            'ltr' => 1,
            'middle' => 1,
            'none' => 1,
            'object' => 1,
            'password' => 1,
            'poly' => 1,
            'post' => 1,
            'preserve' => 1,
            'radio' => 1,
            'rect' => 1,
            'ref' => 1,
            'reset' => 1,
            'right' => 1,
            'row' => 1,
            'rowgroup' => 1,
            'rows' => 1,
            'rtl' => 1,
            'submit' => 1,
            'text' => 1,
            'top' => 1
        );
        static $eAL = array(
            'a' => 1,
            'area' => 1,
            'bdo' => 1,
            'button' => 1,
            'col' => 1,
            'form' => 1,
            'img' => 1,
            'input' => 1,
            'object' => 1,
            'optgroup' => 1,
            'option' => 1,
            'param' => 1,
            'script' => 1,
            'select' => 1,
            'table' => 1,
            'td' => 1,
            'tfoot' => 1,
            'th' => 1,
            'thead' => 1,
            'tr' => 1,
            'xml:space' => 1
        );
        $lcase = isset($eAL[$e]) ? 1 : 0;
    }

    $depTr = 0;
    if ($C['no_deprecated_attr']) {
        // dep attr:applicable ele
        static $aND = array(
            'align' => array(
                'caption' => 1,
                'div' => 1,
                'h1' => 1,
                'h2' => 1,
                'h3' => 1,
                'h4' => 1,
                'h5' => 1,
                'h6' => 1,
                'hr' => 1,
                'img' => 1,
                'input' => 1,
                'legend' => 1,
                'object' => 1,
                'p' => 1,
                'table' => 1
            ),
            'bgcolor' => array(
                'table' => 1,
                'td' => 1,
                'th' => 1,
                'tr' => 1
            ),
            'border' => array(
                'img' => 1,
                'object' => 1
            ),
            'bordercolor' => array(
                'table' => 1,
                'td' => 1,
                'tr' => 1
            ),
            'clear' => array(
                'br' => 1
            ),
            'compact' => array(
                'dl' => 1,
                'ol' => 1,
                'ul' => 1
            ),
            'height' => array(
                'td' => 1,
                'th' => 1
            ),
            'hspace' => array(
                'img' => 1,
                'object' => 1
            ),
            'language' => array(
                'script' => 1
            ),
            'name' => array(
                'a' => 1,
                'form' => 1,
                'iframe' => 1,
                'img' => 1,
                'map' => 1
            ),
            'noshade' => array(
                'hr' => 1
            ),
            'nowrap' => array(
                'td' => 1,
                'th' => 1
            ),
            'size' => array(
                'hr' => 1
            ),
            'start' => array(
                'ol' => 1
            ),
            'type' => array(
                'li' => 1,
                'ol' => 1,
                'ul' => 1
            ),
            'value' => array(
                'li' => 1
            ),
            'vspace' => array(
                'img' => 1,
                'object' => 1
            ),
            'width' => array(
                'hr' => 1,
                'pre' => 1,
                'td' => 1,
                'th' => 1
            )
        );
        static $eAD = array(
            'a' => 1,
            'br' => 1,
            'caption' => 1,
            'div' => 1,
            'dl' => 1,
            'form' => 1,
            'h1' => 1,
            'h2' => 1,
            'h3' => 1,
            'h4' => 1,
            'h5' => 1,
            'h6' => 1,
            'hr' => 1,
            'iframe' => 1,
            'img' => 1,
            'input' => 1,
            'legend' => 1,
            'li' => 1,
            'map' => 1,
            'object' => 1,
            'ol' => 1,
            'p' => 1,
            'pre' => 1,
            'script' => 1,
            'table' => 1,
            'td' => 1,
            'th' => 1,
            'tr' => 1,
            'ul' => 1
        );
        $depTr = isset($eAD[$e]) ? 1 : 0;
    }

    // attr name-vals
    if (strpos($a, "\x01") !== false) {
        $a = preg_replace('`\x01[^\x01]*\x01`', '', $a);
    } // No comment/CDATA sec
    $mode = 0;
    $a = trim($a, ' /');
    $aA = array();
    while (strlen($a)) {
        $w = 0;
        switch ($mode) {
            case 0: // Name
                if (preg_match('`^[a-zA-Z][\-a-zA-Z:]+`', $a, $m)) {
                    $nm = strtolower($m[0]);
                    $w = $mode = 1;
                    $a = ltrim(substr_replace($a, '', 0, strlen($m[0])));
                }
                break;
            case 1:
                if ($a[0] == '=') { // =
                    $w = 1;
                    $mode = 2;
                    $a = ltrim($a, '= ');
                } else { // No val
                    $w = 1;
                    $mode = 0;
                    $a = ltrim($a);
                    $aA[$nm] = '';
                }
                break;
            case 2: // Val
                if (preg_match('`^((?:"[^"]*")|(?:\'[^\']*\')|(?:\s*[^\s"\']+))(.*)`', $a, $m)) {
                    $a = ltrim($m[2]);
                    $m = $m[1];
                    $w = 1;
                    $mode = 0;
                    $aA[$nm] = trim(str_replace('<', '&lt;', ($m[0] == '"' or $m[0] == '\'') ? substr($m, 1, - 1) : $m));
                }
                break;
        }
        if ($w == 0) { // Parse errs, deal with space, " & '
            $a = preg_replace('`^(?:"[^"]*("|$)|\'[^\']*(\'|$)|\S)*\s*`', '', $a);
            $mode = 0;
        }
    }
    if ($mode == 1) {
        $aA[$nm] = '';
    }

    // clean attrs
    global $S;
    $rl = isset($S[$e]) ? $S[$e] : array();
    $a = array();
    $nfr = 0;
    foreach ($aA as $k => $v) {
        if (((isset($C['deny_attribute']['*']) ? isset($C['deny_attribute'][$k]) : !isset($C['deny_attribute'][$k])) && (isset($aN[$k][$e]) or ( isset($aNU[$k]) && !isset($aNU[$k][$e]))) && !isset($rl['n'][$k]) && !isset($rl['n']['*'])) or isset($rl[$k])) {
            if (isset($aNE[$k])) {
                $v = $k;
            } elseif (!empty($lcase) && (($e != 'button' or $e != 'input') or $k == 'type')) { // Rather loose but ?not cause issues
                $v = (isset($aNL[($v2 = strtolower($v))])) ? $v2 : $v;
            }
            if ($k == 'style' && !$C['style_pass']) {
                if (false !== strpos($v, '&#')) {
                    static $sC = array(
                        '&#x20;' => ' ',
                        '&#32;' => ' ',
                        '&#x45;' => 'e',
                        '&#69;' => 'e',
                        '&#x65;' => 'e',
                        '&#101;' => 'e',
                        '&#x58;' => 'x',
                        '&#88;' => 'x',
                        '&#x78;' => 'x',
                        '&#120;' => 'x',
                        '&#x50;' => 'p',
                        '&#80;' => 'p',
                        '&#x70;' => 'p',
                        '&#112;' => 'p',
                        '&#x53;' => 's',
                        '&#83;' => 's',
                        '&#x73;' => 's',
                        '&#115;' => 's',
                        '&#x49;' => 'i',
                        '&#73;' => 'i',
                        '&#x69;' => 'i',
                        '&#105;' => 'i',
                        '&#x4f;' => 'o',
                        '&#79;' => 'o',
                        '&#x6f;' => 'o',
                        '&#111;' => 'o',
                        '&#x4e;' => 'n',
                        '&#78;' => 'n',
                        '&#x6e;' => 'n',
                        '&#110;' => 'n',
                        '&#x55;' => 'u',
                        '&#85;' => 'u',
                        '&#x75;' => 'u',
                        '&#117;' => 'u',
                        '&#x52;' => 'r',
                        '&#82;' => 'r',
                        '&#x72;' => 'r',
                        '&#114;' => 'r',
                        '&#x4c;' => 'l',
                        '&#76;' => 'l',
                        '&#x6c;' => 'l',
                        '&#108;' => 'l',
                        '&#x28;' => '(',
                        '&#40;' => '(',
                        '&#x29;' => ')',
                        '&#41;' => ')',
                        '&#x20;' => ':',
                        '&#32;' => ':',
                        '&#x22;' => '"',
                        '&#34;' => '"',
                        '&#x27;' => "'",
                        '&#39;' => "'",
                        '&#x2f;' => '/',
                        '&#47;' => '/',
                        '&#x2a;' => '*',
                        '&#42;' => '*',
                        '&#x5c;' => '\\',
                        '&#92;' => '\\'
                    );
                    $v = strtr($v, $sC);
                }
                $v = preg_replace_callback('`(url(?:\()(?: )*(?:\'|"|&(?:quot|apos);)?)(.+?)((?:\'|"|&(?:quot|apos);)?(?: )*(?:\)))`iS', 'hl_prot', $v);
                $v = !$C['css_expression'] ? preg_replace('`expression`i', ' ', preg_replace('`\\\\\S|(/|(%2f))(\*|(%2a))`i', ' ', $v)) : $v;
            } elseif (isset($aNP[$k]) or strpos($k, 'src') !== false or $k[0] == 'o') {
                $v = str_replace("­", ' ', (strpos($v, '&') !== false ? str_replace(array(
                            '&#xad;',
                            '&#173;',
                            '&shy;'
                            ), ' ', $v) : $v)); // double-quoted char is soft-hyphen; appears here as "­" or hyphen or something else depending on viewing software
                $v = hl_prot($v, $k);
                if ($k == 'href') { // X-spam
                    if ($C['anti_mail_spam'] && strpos($v, 'mailto:') === 0) {
                        $v = str_replace('@', htmlspecialchars($C['anti_mail_spam']), $v);
                    } elseif ($C['anti_link_spam']) {
                        $r1 = $C['anti_link_spam'][1];
                        if (!empty($r1) && preg_match($r1, $v)) {
                            continue;
                        }
                        $r0 = $C['anti_link_spam'][0];
                        if (!empty($r0) && preg_match($r0, $v)) {
                            if (isset($a['rel'])) {
                                if (!preg_match('`\bnofollow\b`i', $a['rel'])) {
                                    $a['rel'] .= ' nofollow';
                                }
                            } elseif (isset($aA['rel'])) {
                                if (!preg_match('`\bnofollow\b`i', $aA['rel'])) {
                                    $nfr = 1;
                                }
                            } else {
                                $a['rel'] = 'nofollow';
                            }
                        }
                    }
                }
            }
            if (isset($rl[$k]) && is_array($rl[$k]) && ($v = hl_attrval($v, $rl[$k])) === 0) {
                continue;
            }
            $a[$k] = str_replace('"', '&quot;', $v);
        }
    }
    if ($nfr) {
        $a['rel'] = isset($a['rel']) ? $a['rel'] . ' nofollow' : 'nofollow';
    }

    // rqd attr
    static $eAR = array(
        'area' => array(
            'alt' => 'area'
        ),
        'bdo' => array(
            'dir' => 'ltr'
        ),
        'form' => array(
            'action' => ''
        ),
        'img' => array(
            'src' => '',
            'alt' => 'image'
        ),
        'map' => array(
            'name' => ''
        ),
        'optgroup' => array(
            'label' => ''
        ),
        'param' => array(
            'name' => ''
        ),
        'script' => array(
            'type' => 'text/javascript'
        ),
        'textarea' => array(
            'rows' => '10',
            'cols' => '50'
        )
    );
    if (isset($eAR[$e])) {
        foreach ($eAR[$e] as $k => $v) {
            if (!isset($a[$k])) {
                $a[$k] = isset($v[0]) ? $v : $k;
            }
        }
    }

    // depr attrs
    if ($depTr) {
        $c = array();
        foreach ($a as $k => $v) {
            if ($k == 'style' or ! isset($aND[$k][$e])) {
                continue;
            }
            if ($k == 'align') {
                unset($a['align']);
                if ($e == 'img' && ($v == 'left' or $v == 'right')) {
                    $c[] = 'float: ' . $v;
                } elseif (($e == 'div' or $e == 'table') && $v == 'center') {
                    $c[] = 'margin: auto';
                } else {
                    $c[] = 'text-align: ' . $v;
                }
            } elseif ($k == 'bgcolor') {
                unset($a['bgcolor']);
                $c[] = 'background-color: ' . $v;
            } elseif ($k == 'border') {
                unset($a['border']);
                $c[] = "border: {$v}px";
            } elseif ($k == 'bordercolor') {
                unset($a['bordercolor']);
                $c[] = 'border-color: ' . $v;
            } elseif ($k == 'clear') {
                unset($a['clear']);
                $c[] = 'clear: ' . ($v != 'all' ? $v : 'both');
            } elseif ($k == 'compact') {
                unset($a['compact']);
                $c[] = 'font-size: 85%';
            } elseif ($k == 'height' or $k == 'width') {
                unset($a[$k]);
                $c[] = $k . ': ' . ($v[0] != '*' ? $v . (ctype_digit($v) ? 'px' : '') : 'auto');
            } elseif ($k == 'hspace') {
                unset($a['hspace']);
                $c[] = "margin-left: {$v}px; margin-right: {$v}px";
            } elseif ($k == 'language' && !isset($a['type'])) {
                unset($a['language']);
                $a['type'] = 'text/' . strtolower($v);
            } elseif ($k == 'name') {
                if ($C['no_deprecated_attr'] == 2 or ( $e != 'a' && $e != 'map')) {
                    unset($a['name']);
                }
                if (!isset($a['id']) && preg_match('`[a-zA-Z][a-zA-Z\d.:_\-]*`', $v)) {
                    $a['id'] = $v;
                }
            } elseif ($k == 'noshade') {
                unset($a['noshade']);
                $c[] = 'border-style: none; border: 0; background-color: gray; color: gray';
            } elseif ($k == 'nowrap') {
                unset($a['nowrap']);
                $c[] = 'white-space: nowrap';
            } elseif ($k == 'size') {
                unset($a['size']);
                $c[] = 'size: ' . $v . 'px';
            } elseif ($k == 'start' or $k == 'value') {
                unset($a[$k]);
            } elseif ($k == 'type') {
                unset($a['type']);
                static $ol_type = array(
                    'i' => 'lower-roman',
                    'I' => 'upper-roman',
                    'a' => 'lower-latin',
                    'A' => 'upper-latin',
                    '1' => 'decimal'
                );
                $c[] = 'list-style-type: ' . (isset($ol_type[$v]) ? $ol_type[$v] : 'decimal');
            } elseif ($k == 'vspace') {
                unset($a['vspace']);
                $c[] = "margin-top: {$v}px; margin-bottom: {$v}px";
            }
        }
        if (count($c)) {
            $c = implode('; ', $c);
            $a['style'] = isset($a['style']) ? rtrim($a['style'], ' ;') . '; ' . $c . ';' : $c . ';';
        }
    }
    // unique ID
    if ($C['unique_ids'] && isset($a['id'])) {
        if (!preg_match('`^[A-Za-z][A-Za-z0-9_\-.:]*$`', ($id = $a['id'])) or ( isset($GLOBALS['hl_Ids'][$id]) && $C['unique_ids'] == 1)) {
            unset($a['id']);
        } else {
            while (isset($GLOBALS['hl_Ids'][$id])) {
                $id = $C['unique_ids'] . $id;
            }
            $GLOBALS['hl_Ids'][($a['id'] = $id)] = 1;
        }
    }
    // xml:lang
    if ($C['xml:lang'] && isset($a['lang'])) {
        $a['xml:lang'] = isset($a['xml:lang']) ? $a['xml:lang'] : $a['lang'];
        if ($C['xml:lang'] == 2) {
            unset($a['lang']);
        }
    }
    // for transformed tag
    if (!empty($trt)) {
        $a['style'] = isset($a['style']) ? rtrim($a['style'], ' ;') . '; ' . $trt : $trt;
    }
    // return with empty ele /
    if (empty($C['hook_tag'])) {
        $aA = '';
        foreach ($a as $k => $v) {
            $aA .= " {$k}=\"{$v}\"";
        }
        return "<{$e}{$aA}" . (isset($eE[$e]) ? ' /' : '') . '>';
    } else {
        return $C['hook_tag']($e, $a);
    }
    // eof
}

function hl_tag2(&$e, &$a, $t = 1)
{
    // transform tag
    if ($e == 'center') {
        $e = 'div';
        return 'text-align: center;';
    }
    if ($e == 'dir' or $e == 'menu') {
        $e = 'ul';
        return '';
    }
    if ($e == 's' or $e == 'strike') {
        $e = 'span';
        return 'text-decoration: line-through;';
    }
    if ($e == 'u') {
        $e = 'span';
        return 'text-decoration: underline;';
    }
    static $fs = array(
        '0' => 'xx-small',
        '1' => 'xx-small',
        '2' => 'small',
        '3' => 'medium',
        '4' => 'large',
        '5' => 'x-large',
        '6' => 'xx-large',
        '7' => '300%',
        '-1' => 'smaller',
        '-2' => '60%',
        '+1' => 'larger',
        '+2' => '150%',
        '+3' => '200%',
        '+4' => '300%'
    );
    if ($e == 'font') {
        $a2 = '';
        if (preg_match('`face\s*=\s*(\'|")([^=]+?)\\1`i', $a, $m) or preg_match('`face\s*=(\s*)(\S+)`i', $a, $m)) {
            $a2 .= ' font-family: ' . str_replace('"', '\'', trim($m[2])) . ';';
        }
        if (preg_match('`color\s*=\s*(\'|")?(.+?)(\\1|\s|$)`i', $a, $m)) {
            $a2 .= ' color: ' . trim($m[2]) . ';';
        }
        if (preg_match('`size\s*=\s*(\'|")?(.+?)(\\1|\s|$)`i', $a, $m) && isset($fs[($m = trim($m[2]))])) {
            $a2 .= ' font-size: ' . $fs[$m] . ';';
        }
        $e = 'span';
        return ltrim($a2);
    }
    if ($t == 2) {
        $e = 0;
        return 0;
    }
    return '';
    // eof
}

function hl_tidy($t, $w, $p)
{
    // Tidy/compact HTM
    if (strpos(' pre,script,textarea', "$p,")) {
        return $t;
    }
    $t = preg_replace('`\s+`', ' ', preg_replace_callback(array(
        '`(<(!\[CDATA\[))(.+?)(\]\]>)`sm',
        '`(<(!--))(.+?)(-->)`sm',
        '`(<(pre|script|textarea)[^>]*?>)(.+?)(</\2>)`sm'
            ), create_function('$m', 'return $m[1]. str_replace(array("<", ">", "\n", "\r", "\t", " "), array("\x01", "\x02", "\x03", "\x04", "\x05", "\x07"), $m[3]). $m[4];'), $t));
    if (($w = strtolower($w)) == - 1) {
        return str_replace(array(
            "\x01",
            "\x02",
            "\x03",
            "\x04",
            "\x05",
            "\x07"
            ), array(
            '<',
            '>',
            "\n",
            "\r",
            "\t",
            ' '
            ), $t);
    }
    $s = strpos(" $w", 't') ? "\t" : ' ';
    $s = preg_match('`\d`', $w, $m) ? str_repeat($s, $m[0]) : str_repeat($s, ($s == "\t" ? 1 : 2));
    $N = preg_match('`[ts]([1-9])`', $w, $m) ? $m[1] : 0;
    $a = array(
        'br' => 1
    );
    $b = array(
        'button' => 1,
        'input' => 1,
        'option' => 1,
        'param' => 1
    );
    $c = array(
        'caption' => 1,
        'dd' => 1,
        'dt' => 1,
        'h1' => 1,
        'h2' => 1,
        'h3' => 1,
        'h4' => 1,
        'h5' => 1,
        'h6' => 1,
        'isindex' => 1,
        'label' => 1,
        'legend' => 1,
        'li' => 1,
        'object' => 1,
        'p' => 1,
        'pre' => 1,
        'td' => 1,
        'textarea' => 1,
        'th' => 1
    );
    $d = array(
        'address' => 1,
        'blockquote' => 1,
        'center' => 1,
        'colgroup' => 1,
        'dir' => 1,
        'div' => 1,
        'dl' => 1,
        'fieldset' => 1,
        'form' => 1,
        'hr' => 1,
        'iframe' => 1,
        'map' => 1,
        'menu' => 1,
        'noscript' => 1,
        'ol' => 1,
        'optgroup' => 1,
        'rbc' => 1,
        'rtc' => 1,
        'ruby' => 1,
        'script' => 1,
        'select' => 1,
        'table' => 1,
        'tbody' => 1,
        'tfoot' => 1,
        'thead' => 1,
        'tr' => 1,
        'ul' => 1
    );
    $T = explode('<', $t);
    $X = 1;
    while ($X) {
        $n = $N;
        $t = $T;
        ob_start();
        if (isset($d[$p])) {
            echo str_repeat($s, ++$n);
        }
        echo ltrim(array_shift($t));
        for ($i = - 1, $j = count($t); ++$i < $j;) {
            $r = '';
            list ($e, $r) = explode('>', $t[$i]);
            $x = $e[0] == '/' ? 0 : (substr($e, - 1) == '/' ? 1 : ($e[0] != '!' ? 2 : - 1));
            $y = !$x ? ltrim($e, '/') : ($x > 0 ? substr($e, 0, strcspn($e, ' ')) : 0);
            $e = "<$e>";
            if (isset($d[$y])) {
                if (!$x) {
                    if ($n) {
                        echo "\n", str_repeat($s, --$n), "$e\n", str_repeat($s, $n);
                    } else {
                        ++$N;
                        ob_end_clean();
                        continue 2;
                    }
                } else {
                    echo "\n", str_repeat($s, $n), "$e\n", str_repeat($s, ($x != 1 ? ++$n : $n));
                }
                echo $r;
                continue;
            }
            $f = "\n" . str_repeat($s, $n);
            if (isset($c[$y])) {
                if (!$x) {
                    echo $e, $f, $r;
                } else {
                    echo $f, $e, $r;
                }
            } elseif (isset($b[$y])) {
                echo $f, $e, $r;
            } elseif (isset($a[$y])) {
                echo $e, $f, $r;
            } elseif (!$y) {
                echo $f, $e, $f, $r;
            } else {
                echo $e, $r;
            }
        }
        $X = 0;
    }
    $t = str_replace(array(
        "\n ",
        " \n"
        ), "\n", preg_replace('`[\n]\s*?[\n]+`', "\n", ob_get_contents()));
    ob_end_clean();
    if (($l = strpos(" $w", 'r') ? (strpos(" $w", 'n') ? "\r\n" : "\r") : 0)) {
        $t = str_replace("\n", $l, $t);
    }
    return str_replace(array(
        "\x01",
        "\x02",
        "\x03",
        "\x04",
        "\x05",
        "\x07"
        ), array(
        '<',
        '>',
        "\n",
        "\r",
        "\t",
        ' '
        ), $t);
    // eof
}

function hl_version()
{
    // rel
    return '1.1.19';
    // eof
}

function kses($t, $h, $p = array('http', 'https', 'ftp', 'news', 'nntp', 'telnet', 'gopher', 'mailto'))
{
    // kses compat
    foreach ($h as $k => $v) {
        $h[$k]['n']['*'] = 1;
    }
    $C['cdata'] = $C['comment'] = $C['make_tag_strict'] = $C['no_deprecated_attr'] = $C['unique_ids'] = 0;
    $C['keep_bad'] = 1;
    $C['elements'] = count($h) ? strtolower(implode(',', array_keys($h))) : '-*';
    $C['hook'] = 'kses_hook';
    $C['schemes'] = '*:' . implode(',', $p);
    return htmLawed($t, $C, $h);
    // eof
}

function kses_hook($t, &$C, &$S)
{
    // kses compat
    return $t;
    // eof
}

/**
 * Concatenation with separator.
 *
 * @since 1.0.8
 * @param string $separator Delimeter to used between strings.
 * @param type $string1 Left string.
 * @param type $string2 Right string.
 * @return type
 */
function concat_ws($separator, $string1, $string2)
{
    if (null == $separator) {
        $separator = ',';
    }
    return $string1 . $separator . $string2;
}
