DonatShell
Server IP : 180.180.241.3  /  Your IP : 216.73.216.252
Web Server : Microsoft-IIS/7.5
System : Windows NT NETWORK-NHRC 6.1 build 7601 (Windows Server 2008 R2 Standard Edition Service Pack 1) i586
User : IUSR ( 0)
PHP Version : 5.3.28
Disable Function : NONE
MySQL : ON  |  cURL : ON  |  WGET : OFF  |  Perl : OFF  |  Python : OFF  |  Sudo : OFF  |  Pkexec : OFF
Directory :  C:/AppServ/www/app/webroot/news/administrator/components/com_jfusion/models/parsers/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ HOME SHELL ]     

Current File : C:/AppServ/www/app/webroot/news/administrator/components/com_jfusion/models/parsers/nbbc.php
<?php
/**
 * nbbc parser
 *
 * PHP version 5
 *
 * @category  JFusion
 * @package   Models
 * @author    Phantom Inker <webmaster@jfusion.org>
 * @copyright 2008 Phantom Inker. All rights reserved.
 * @license   http://www.gnu.org/copyleft/gpl.html GNU/GPL
 * @link      http://www.jfusion.org
 */
/*
This is a compressed copy of NBBC. Do not edit!

Copyright (c) 2008-9, the Phantom Inker.  All rights reserved.
Portions Copyright (c) 2004-2008 AddedBytes.com

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.

THIS SOFTWARE IS PROVIDED BY THE PHANTOM INKER "AS IS" AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
define("BBCODE_VERSION", "1.4.5");
define("BBCODE_RELEASE", "2010-09-17");
define("BBCODE_VERBATIM", 2);
define("BBCODE_REQUIRED", 1);
define("BBCODE_OPTIONAL", 0);
define("BBCODE_PROHIBIT", -1);
define("BBCODE_CHECK", 1);
define("BBCODE_OUTPUT", 2);
define("BBCODE_ENDTAG", 5);
define("BBCODE_TAG", 4);
define("BBCODE_TEXT", 3);
define("BBCODE_NL", 2);
define("BBCODE_WS", 1);
define("BBCODE_EOI", 0);
define("BBCODE_LEXSTATE_TEXT", 0);
define("BBCODE_LEXSTATE_TAG", 1);
define("BBCODE_MODE_SIMPLE", 0);
define("BBCODE_MODE_CALLBACK", 1);
define("BBCODE_MODE_INTERNAL", 2);
define("BBCODE_MODE_LIBRARY", 3);
define("BBCODE_MODE_ENHANCED", 4);
define("BBCODE_STACK_TOKEN", 0);
define("BBCODE_STACK_TEXT", 1);
define("BBCODE_STACK_TAG", 2);
define("BBCODE_STACK_CLASS", 3);
if (!function_exists('str_split')) {
    /**
     * @param $string
     * @param int $split_length
     * @return array
     */
    function str_split($string, $split_length = 1) {
        $array = explode("\r\n", chunk_split($string, $split_length));
        array_pop($array);
        return $array;
    }
}
$BBCode_SourceDir = dirname(__FILE__);
/**
 *
 */
class BBCodeLexer {
    var $token;
    var $text;
    var $tag;
    var $state;
    var $input;
    var $ptr;
    var $unget;
    var $verbatim;
    var $debug;
    var $tagmarker;
    var $end_tagmarker;
    var $pat_main;
    var $pat_comment;
    var $pat_comment2;
    var $pat_wiki;

    /**
     * @param $string
     * @param string $tagmarker
     */
    function BBCodeLexer($string, $tagmarker = '[') {
        $regex_beginmarkers = Array('[' => '\[', '<' => '<', '{' => '\{', '(' => '\(');
        $regex_endmarkers = Array('[' => '\]', '<' => '>', '{' => '\}', '(' => '\)');
        $endmarkers = Array('[' => ']', '<' => '>', '{' => '}', '(' => ')');
        if (!isset($regex_endmarkers[$tagmarker])) $tagmarker = '[';
        $e = $regex_endmarkers[$tagmarker];
        $b = $regex_beginmarkers[$tagmarker];
        $this->tagmarker = $tagmarker;
        $this->end_tagmarker = $endmarkers[$tagmarker];
        $this->pat_main = "/( " . "{$b}" . "(?! -- | ' | !-- | {$b}{$b} )" . "(?: [^\\n\\r{$b}{$e}] | \\\" [^\\\"\\n\\r]* \\\" | \\' [^\\'\\n\\r]* \\' )*" . "{$e}" . "| {$b}{$b} (?: [^{$e}\\r\\n] | {$e}[^{$e}\\r\\n] )* {$e}{$e}" . "| {$b} (?: -- | ' ) (?: [^{$e}\\n\\r]* ) {$e}" . "| {$b}!-- (?: [^-] | -[^-] | --[^{$e}] )* --{$e}" . "| -----+" . "| \\x0D\\x0A | \\x0A\\x0D | \\x0D | \\x0A" . "| [\\x00-\\x09\\x0B-\\x0C\\x0E-\\x20]+(?=[\\x0D\\x0A{$b}]|-----|$)" . "| (?<=[\\x0D\\x0A{$e}]|-----|^)[\\x00-\\x09\\x0B-\\x0C\\x0E-\\x20]+" . " )/Dx";
        $this->input = preg_split($this->pat_main, $string, -1, PREG_SPLIT_DELIM_CAPTURE);
        $this->pat_comment = "/^ {$b} (?: -- | ' ) /Dx";
        $this->pat_comment2 = "/^ {$b}!-- (?: [^-] | -[^-] | --[^{$e}] )* --{$e} $/Dx";
        $this->pat_wiki = "/^ {$b}{$b} ([^\\|]*) (?:\\|(.*))? {$e}{$e} $/Dx";
        $this->ptr = 0;
        $this->unget = false;
        $this->state = BBCODE_LEXSTATE_TEXT;
        $this->verbatim = false;
        $this->token = BBCODE_EOI;
        $this->tag = false;
        $this->text = '';
    }

    /**
     * @return int
     */
    function GuessTextLength() {
        $length = 0;
        $ptr = 0;
        $state = BBCODE_LEXSTATE_TEXT;
        while ($ptr < count($this->input)) {
            $text = $this->input[$ptr++];
            if ($state == BBCODE_LEXSTATE_TEXT) {
                $state = BBCODE_LEXSTATE_TAG;
                $length+= strlen($text);
            } else {
                switch (ord(substr($this->text, 0, 1))) {
                    case 10:
                    case 13:
                        $state = BBCODE_LEXSTATE_TEXT;
                        $length++;
                    break;
                    default:
                        $state = BBCODE_LEXSTATE_TEXT;
                        $length+= strlen($text);
                    break;
                    case 40:
                    case 60:
                    case 91:
                    case 123:
                        $state = BBCODE_LEXSTATE_TEXT;
                    break;
                }
            }
        }
        return $length;
    }

    /**
     * @return bool|int
     */
    function NextToken() {
        if ($this->unget) {
            $this->unget = false;
            return $this->token;
        }
        while (true) {
            if ($this->ptr >= count($this->input)) {
                $this->text = '';
                $this->tag = false;
                return $this->token = BBCODE_EOI;
            }
            $this->text = preg_replace("/[\\x00-\\x08\\x0B-\\x0C\\x0E-\\x1F]/", "", $this->input[$this->ptr++]);
            if ($this->verbatim) {
                $this->tag = false;
                if ($this->state == BBCODE_LEXSTATE_TEXT) {
                    $this->state = BBCODE_LEXSTATE_TAG;
                    $token_type = BBCODE_TEXT;
                } else {
                    $this->state = BBCODE_LEXSTATE_TEXT;
                    switch (ord(substr($this->text, 0, 1))) {
                        case 10:
                        case 13:
                            $token_type = BBCODE_NL;
                        break;
                        default:
                            $token_type = BBCODE_WS;
                        break;
                        case 45:
                        case 40:
                        case 60:
                        case 91:
                        case 123:
                            $token_type = BBCODE_TEXT;
                        break;
                    }
                }
                if (strlen($this->text) > 0) return $this->token = $token_type;
            } else if ($this->state == BBCODE_LEXSTATE_TEXT) {
                $this->state = BBCODE_LEXSTATE_TAG;
                $this->tag = false;
                if (strlen($this->text) > 0) return $this->token = BBCODE_TEXT;
            } else {
                switch (ord(substr($this->text, 0, 1))) {
                    case 10:
                    case 13:
                        $this->tag = false;
                        $this->state = BBCODE_LEXSTATE_TEXT;
                        return $this->token = BBCODE_NL;
                    case 45:
                        if (preg_match("/^-----/", $this->text)) {
                            $this->tag = Array('_name' => 'rule', '_endtag' => false, '_default' => '');
                            $this->state = BBCODE_LEXSTATE_TEXT;
                            return $this->token = BBCODE_TAG;
                        } else {
                            $this->tag = false;
                            $this->state = BBCODE_LEXSTATE_TEXT;
                            if (strlen($this->text) > 0) return $this->token = BBCODE_TEXT;
                            continue;
                        }
                    default:
                        $this->tag = false;
                        $this->state = BBCODE_LEXSTATE_TEXT;
                        return $this->token = BBCODE_WS;
                    case 40:
                    case 60:
                    case 91:
                    case 123:
                        if (preg_match($this->pat_comment, $this->text)) {
                            $this->state = BBCODE_LEXSTATE_TEXT;
                            continue;
                        }
                        if (preg_match($this->pat_comment2, $this->text)) {
                            $this->state = BBCODE_LEXSTATE_TEXT;
                            continue;
                        }
                        if (preg_match($this->pat_wiki, $this->text, $matches)) {
                            $this->tag = Array('_name' => 'wiki', '_endtag' => false, '_default' => @$matches[1], 'title' => @$matches[2]);
                            $this->state = BBCODE_LEXSTATE_TEXT;
                            return $this->token = BBCODE_TAG;
                        }
                        $this->tag = $this->Internal_DecodeTag($this->text);
                        $this->state = BBCODE_LEXSTATE_TEXT;
                        return $this->token = ($this->tag['_end'] ? BBCODE_ENDTAG : BBCODE_TAG);
                    }
            }
        }
        return false;
    }
    function UngetToken() {
        if ($this->token !== BBCODE_EOI) $this->unget = true;
    }

    /**
     * @return bool|int
     */
    function PeekToken() {
        $result = $this->NextToken();
        if ($this->token !== BBCODE_EOI) $this->unget = true;
        return $result;
    }

    /**
     * @return array
     */
    function SaveState() {
        return Array('token' => $this->token, 'text' => $this->text, 'tag' => $this->tag, 'state' => $this->state, 'input' => $this->input, 'ptr' => $this->ptr, 'unget' => $this->unget, 'verbatim' => $this->verbatim);
    }

    /**
     * @param $state
     * @return mixed
     */
    function RestoreState($state) {
        if (!is_array($state)) return;
        $this->token = @$state['token'];
        $this->text = @$state['text'];
        $this->tag = @$state['tag'];
        $this->state = @$state['state'];
        $this->input = @$state['input'];
        $this->ptr = @$state['ptr'];
        $this->unget = @$state['unget'];
        $this->verbatim = @$state['verbatim'];
    }

    /**
     * @param $string
     * @return mixed
     */
    function Internal_StripQuotes($string) {
        if (preg_match("/^\\\"(.*)\\\"$/", $string, $matches)) return $matches[1];
        else if (preg_match("/^\\'(.*)\\'$/", $string, $matches)) return $matches[1];
        else return $string;
    }

    /**
     * @param $ptr
     * @param $pieces
     * @return int|string
     */
    function Internal_ClassifyPiece($ptr, $pieces) {
        if ($ptr >= count($pieces)) return -1;
        $piece = $pieces[$ptr];
        if ($piece == '=') return '=';
        else if (preg_match("/^[\\'\\\"]/", $piece)) return '"';
        else if (preg_match("/^[\\x00-\\x20]+$/", $piece)) return ' ';
        else return 'A';
    }

    /**
     * @param $tag
     * @return array
     */
    function Internal_DecodeTag($tag) {
        $result = Array('_tag' => $tag, '_endtag' => '', '_name' => '', '_hasend' => false, '_end' => false, '_default' => false);
        $tag = substr($tag, 1, strlen($tag) - 2);
        $ch = ord(substr($tag, 0, 1));
        if ($ch >= 0 && $ch <= 32) return $result;
        $pieces = preg_split("/(\\\"[^\\\"]+\\\"|\\'[^\\']+\\'|=|[\\x00-\\x20]+)/", $tag, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
        $ptr = 0;
        if (count($pieces) < 1) return $result;
        if (@substr($pieces[$ptr], 0, 1) == '/') {
            $result['_name'] = strtolower(substr($pieces[$ptr++], 1));
            $result['_end'] = true;
        } else {
            $result['_name'] = strtolower($pieces[$ptr++]);
            $result['_end'] = false;
        }
        while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') $ptr++;
        $params = Array();
        if ($type != '=') {
            $result['_default'] = false;
            $params[] = Array('key' => '', 'value' => '');
        } else {
            $ptr++;
            while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') $ptr++;
            if ($type == "\"") $value = $this->Internal_StripQuotes($pieces[$ptr++]);
            else {
                $after_space = false;
                $start = $ptr;
                while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != - 1) {
                    if ($type == ' ') $after_space = true;
                    if ($type == '=' && $after_space) break;
                    $ptr++;
                }
                if ($type == - 1) $ptr--;
                if ($type == '=') {
                    $ptr--;
                    while ($ptr > $start && $this->Internal_ClassifyPiece($ptr, $pieces) == ' ') $ptr--;
                    while ($ptr > $start && $this->Internal_ClassifyPiece($ptr, $pieces) != ' ') $ptr--;
                }
                $value = '';
                for (;$start <= $ptr;$start++) {
                    if ($this->Internal_ClassifyPiece($start, $pieces) == ' ') $value.= " ";
                    else $value.= $this->Internal_StripQuotes($pieces[$start]);
                }
                $value = trim($value);
                $ptr++;
            }
            $result['_default'] = $value;
            $params[] = Array('key' => '', 'value' => $value);
        }
        /**
         * @ignore
         * @var $key string
         */
        while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != - 1) {
            while ($type == ' ') {
                $ptr++;
                $type = $this->Internal_ClassifyPiece($ptr, $pieces);
            }
            if ($type == 'A' || $type == '"') $key = strtolower($this->Internal_StripQuotes(@$pieces[$ptr++]));
            else if ($type == '=') {
                $ptr++;
                continue;
            } else if ($type == - 1) break;
            while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') $ptr++;
            if ($type != '=') $value = $this->Internal_StripQuotes($key);
            else {
                $ptr++;
                while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') $ptr++;
                if ($type == '"') {
                    $value = $this->Internal_StripQuotes($pieces[$ptr++]);
                } else if ($type != - 1) {
                    $value = $pieces[$ptr++];
                    while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != - 1 && $type != ' ') $value.= $pieces[$ptr++];
                } else $value = '';
            }
            if (substr($key, 0, 1) != '_') $result[$key] = $value;
            $params[] = Array('key' => $key, 'value' => $value);
        }
        $result['_params'] = $params;
        return $result;
    }
}

/**
 *
 */
class BBCodeLibrary {
    var $default_smileys = Array(':)' => 'smile.gif', ':-)' => 'smile.gif', '=-)' => 'smile.gif', ':(' => 'frown.gif', ':-(' => 'frown.gif', '=(' => 'frown.gif', '=-(' => 'frown.gif', ':D' => 'bigsmile.gif', ':-D' => 'bigsmile.gif', '=D' => 'bigsmile.gif', '=-D' => 'bigsmile.gif', '>:(' => 'angry.gif', '>:-(' => 'angry.gif', '>=(' => 'angry.gif', '>=-(' => 'angry.gif', 'D:' => 'angry.gif', 'D-:' => 'angry.gif', 'D=' => 'angry.gif', 'D-=' => 'angry.gif', '>:)' => 'evil.gif', '>:-)' => 'evil.gif', '>=)' => 'evil.gif', '>=-)' => 'evil.gif', '>:D' => 'evil.gif', '>:-D' => 'evil.gif', '>=D' => 'evil.gif', '>=-D' => 'evil.gif', '>;)' => 'sneaky.gif', '>;-)' => 'sneaky.gif', '>;D' => 'sneaky.gif', '>;-D' => 'sneaky.gif', 'O:)' => 'saint.gif', 'O:-)' => 'saint.gif', 'O=)' => 'saint.gif', 'O=-)' => 'saint.gif', ':O' => 'surprise.gif', ':-O' => 'surprise.gif', '=O' => 'surprise.gif', '=-O' => 'surprise.gif', ':?' => 'confuse.gif', ':-?' => 'confuse.gif', '=?' => 'confuse.gif', '=-?' => 'confuse.gif', ':s' => 'worry.gif', ':-S' => 'worry.gif', '=s' => 'worry.gif', '=-S' => 'worry.gif', ':|' => 'neutral.gif', ':-|' => 'neutral.gif', '=|' => 'neutral.gif', '=-|' => 'neutral.gif', ':I' => 'neutral.gif', ':-I' => 'neutral.gif', '=I' => 'neutral.gif', '=-I' => 'neutral.gif', ':/' => 'irritated.gif', ':-/' => 'irritated.gif', '=/' => 'irritated.gif', '=-/' => 'irritated.gif', ':\\' => 'irritated.gif', ':-\\' => 'irritated.gif', '=\\' => 'irritated.gif', '=-\\' => 'irritated.gif', ':P' => 'tongue.gif', ':-P' => 'tongue.gif', '=P' => 'tongue.gif', '=-P' => 'tongue.gif', 'X-P' => 'tongue.gif', '8)' => 'bigeyes.gif', '8-)' => 'bigeyes.gif', 'B)' => 'cool.gif', 'B-)' => 'cool.gif', ';)' => 'wink.gif', ';-)' => 'wink.gif', ';D' => 'bigwink.gif', ';-D' => 'bigwink.gif', '^_^' => 'anime.gif', '^^;' => 'sweatdrop.gif', '>_>' => 'lookright.gif', '>.>' => 'lookright.gif', '<_<' => 'lookleft.gif', '<.<' => 'lookleft.gif', 'XD' => 'laugh.gif', 'X-D' => 'laugh.gif', ':3' => 'smile3.gif', ':-3' => 'smile3.gif', '=3' => 'smile3.gif', '=-3' => 'smile3.gif', ';3' => 'wink3.gif', ';-3' => 'wink3.gif', '<g>' => 'teeth.gif', '<G>' => 'teeth.gif', 'o.O' => 'boggle.gif', 'O.o' => 'boggle.gif', ':blue:' => 'blue.gif', ':zzz:' => 'sleepy.gif', '<3' => 'heart.gif', ':star:' => 'star.gif',);
    var $default_tag_rules = Array('b' => Array('simple_start' => "<b>", 'simple_end' => "</b>", 'class' => 'inline', 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), 'plain_start' => "<b>", 'plain_end' => "</b>",), 'i' => Array('simple_start' => "<i>", 'simple_end' => "</i>", 'class' => 'inline', 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), 'plain_start' => "<i>", 'plain_end' => "</i>",), 'u' => Array('simple_start' => "<u>", 'simple_end' => "</u>", 'class' => 'inline', 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), 'plain_start' => "<u>", 'plain_end' => "</u>",), 's' => Array('simple_start' => "<strike>", 'simple_end' => "</strike>", 'class' => 'inline', 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), 'plain_start' => "<i>", 'plain_end' => "</i>",), 'font' => Array('mode' => BBCODE_MODE_LIBRARY, 'allow' => Array('_default' => '/^[a-zA-Z0-9._ -]+$/'), 'method' => 'DoFont', 'class' => 'inline', 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'),), 'color' => Array('mode' => BBCODE_MODE_ENHANCED, 'allow' => Array('_default' => '/^#?[a-zA-Z0-9._ -]+$/'), 'template' => '<span style="color:{$_default/tw}">{$_content/v}</span>', 'class' => 'inline', 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'),), 'size' => Array('mode' => BBCODE_MODE_LIBRARY, 'allow' => Array('_default' => '/^[0-9.]+$/D'), 'method' => 'DoSize', 'class' => 'inline', 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'),), 'sup' => Array('simple_start' => "<sup>", 'simple_end' => "</sup>", 'class' => 'inline', 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'),), 'sub' => Array('simple_start' => "<sub>", 'simple_end' => "</sub>", 'class' => 'inline', 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'),), 'spoiler' => Array('simple_start' => "<span class=\"bbcode_spoiler\">", 'simple_end' => "</span>", 'class' => 'inline', 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'),), 'acronym' => Array('mode' => BBCODE_MODE_ENHANCED, 'template' => '<span class="bbcode_acronym" title="{$_default/e}">{$_content/v}</span>', 'class' => 'inline', 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'),), 'url' => Array('mode' => BBCODE_MODE_LIBRARY, 'method' => 'DoURL', 'class' => 'link', 'allow_in' => Array('listitem', 'block', 'columns', 'inline'), 'content' => BBCODE_REQUIRED, 'plain_start' => "<a href=\"{\$link}\">", 'plain_end' => "</a>", 'plain_content' => Array('_content', '_default'), 'plain_link' => Array('_default', '_content'),), 'email' => Array('mode' => BBCODE_MODE_LIBRARY, 'method' => 'DoEmail', 'class' => 'link', 'allow_in' => Array('listitem', 'block', 'columns', 'inline'), 'content' => BBCODE_REQUIRED, 'plain_start' => "<a href=\"mailto:{\$link}\">", 'plain_end' => "</a>", 'plain_content' => Array('_content', '_default'), 'plain_link' => Array('_default', '_content'),), 'wiki' => Array('mode' => BBCODE_MODE_LIBRARY, 'method' => "DoWiki", 'class' => 'link', 'allow_in' => Array('listitem', 'block', 'columns', 'inline'), 'end_tag' => BBCODE_PROHIBIT, 'content' => BBCODE_PROHIBIT, 'plain_start' => "<b>[", 'plain_end' => "]</b>", 'plain_content' => Array('title', '_default'), 'plain_link' => Array('_default', '_content'),), 'img' => Array('mode' => BBCODE_MODE_LIBRARY, 'method' => "DoImage", 'class' => 'image', 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), 'end_tag' => BBCODE_REQUIRED, 'content' => BBCODE_REQUIRED, 'plain_start' => "[image]", 'plain_content' => Array(),), 'rule' => Array('mode' => BBCODE_MODE_LIBRARY, 'method' => "DoRule", 'class' => 'block', 'allow_in' => Array('listitem', 'block', 'columns'), 'end_tag' => BBCODE_PROHIBIT, 'content' => BBCODE_PROHIBIT, 'before_tag' => "sns", 'after_tag' => "sns", 'plain_start' => "\n-----\n", 'plain_end' => "", 'plain_content' => Array(),), 'br' => Array('mode' => BBCODE_MODE_SIMPLE, 'simple_start' => "<br />\n", 'simple_end' => "", 'class' => 'inline', 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), 'end_tag' => BBCODE_PROHIBIT, 'content' => BBCODE_PROHIBIT, 'before_tag' => "s", 'after_tag' => "s", 'plain_start' => "\n", 'plain_end' => "", 'plain_content' => Array(),), 'left' => Array('simple_start' => "\n<div class=\"bbcode_left\" style=\"text-align:left\">\n", 'simple_end' => "\n</div>\n", 'allow_in' => Array('listitem', 'block', 'columns'), 'before_tag' => "sns", 'after_tag' => "sns", 'before_endtag' => "sns", 'after_endtag' => "sns", 'plain_start' => "\n", 'plain_end' => "\n",), 'right' => Array('simple_start' => "\n<div class=\"bbcode_right\" style=\"text-align:right\">\n", 'simple_end' => "\n</div>\n", 'allow_in' => Array('listitem', 'block', 'columns'), 'before_tag' => "sns", 'after_tag' => "sns", 'before_endtag' => "sns", 'after_endtag' => "sns", 'plain_start' => "\n", 'plain_end' => "\n",), 'center' => Array('simple_start' => "\n<div class=\"bbcode_center\" style=\"text-align:center\">\n", 'simple_end' => "\n</div>\n", 'allow_in' => Array('listitem', 'block', 'columns'), 'before_tag' => "sns", 'after_tag' => "sns", 'before_endtag' => "sns", 'after_endtag' => "sns", 'plain_start' => "\n", 'plain_end' => "\n",), 'indent' => Array('simple_start' => "\n<div class=\"bbcode_indent\" style=\"margin-left:4em\">\n", 'simple_end' => "\n</div>\n", 'allow_in' => Array('listitem', 'block', 'columns'), 'before_tag' => "sns", 'after_tag' => "sns", 'before_endtag' => "sns", 'after_endtag' => "sns", 'plain_start' => "\n", 'plain_end' => "\n",), 'columns' => Array('simple_start' => "\n<table class=\"bbcode_columns\"><tbody><tr><td class=\"bbcode_column bbcode_firstcolumn\">\n", 'simple_end' => "\n</td></tr></tbody></table>\n", 'class' => 'columns', 'allow_in' => Array('listitem', 'block', 'columns'), 'end_tag' => BBCODE_REQUIRED, 'content' => BBCODE_REQUIRED, 'before_tag' => "sns", 'after_tag' => "sns", 'before_endtag' => "sns", 'after_endtag' => "sns", 'plain_start' => "\n", 'plain_end' => "\n",), 'nextcol' => Array('simple_start' => "\n</td><td class=\"bbcode_column\">\n", 'class' => 'nextcol', 'allow_in' => Array('columns'), 'end_tag' => BBCODE_PROHIBIT, 'content' => BBCODE_PROHIBIT, 'before_tag' => "sns", 'after_tag' => "sns", 'before_endtag' => "sns", 'after_endtag' => "sns", 'plain_start' => "\n", 'plain_end' => "",), 'code' => Array('mode' => BBCODE_MODE_ENHANCED, 'template' => "\n<div class=\"bbcode_code\">\n<div class=\"bbcode_code_head\">Code:</div>\n<div class=\"bbcode_code_body\">{\$_content/v}</div>\n</div>\n", 'class' => 'code', 'allow_in' => Array('listitem', 'block', 'columns'), 'content' => BBCODE_VERBATIM, 'before_tag' => "sns", 'after_tag' => "sn", 'before_endtag' => "sn", 'after_endtag' => "sns", 'plain_start' => "\n<b>Code:</b>\n", 'plain_end' => "\n",), 'quote' => Array('mode' => BBCODE_MODE_LIBRARY, 'method' => "DoQuote", 'allow_in' => Array('listitem', 'block', 'columns'), 'before_tag' => "sns", 'after_tag' => "sns", 'before_endtag' => "sns", 'after_endtag' => "sns", 'plain_start' => "\n<b>Quote:</b>\n", 'plain_end' => "\n",), 'list' => Array('mode' => BBCODE_MODE_LIBRARY, 'method' => 'DoList', 'class' => 'list', 'allow_in' => Array('listitem', 'block', 'columns'), 'before_tag' => "sns", 'after_tag' => "sns", 'before_endtag' => "sns", 'after_endtag' => "sns", 'plain_start' => "\n", 'plain_end' => "\n",), '*' => Array('simple_start' => "<li>", 'simple_end' => "</li>\n", 'class' => 'listitem', 'allow_in' => Array('list'), 'end_tag' => BBCODE_OPTIONAL, 'before_tag' => "s", 'after_tag' => "s", 'before_endtag' => "sns", 'after_endtag' => "sns", 'plain_start' => "\n * ", 'plain_end' => "\n",),);

    /**
     * @param BBCode_Parser $bbcode
     * @param $action
     * @param $name
     * @param $default
     * @param $params
     * @param $content
     * @return bool|string
     */
    function DoURL($bbcode, $action, $name, $default, $params, $content) {
        if ($action == BBCODE_CHECK) return true;

        $url = is_string($default) ? $default : $bbcode->UnHTMLEncode(strip_tags($content));
        if ($bbcode->IsValidURL($url)) {
            if ($bbcode->debug) print "ISVALIDURL<br />";
            if ($bbcode->url_targetable !== false && isset($params['target'])) $target = " target=\"" . $bbcode->nbbchtmlspecialchars($params['target']) . "\"";
            else $target = '';
            if ($bbcode->url_target !== false) if (!($bbcode->url_targetable == 'override' && isset($params['target']))) $target = " target=\"" . $bbcode->nbbchtmlspecialchars($bbcode->url_target) . "\"";
            return '<a href="' . $bbcode->nbbchtmlspecialchars($url) . '" class="bbcode_url"' . $target . '>' . $content . '</a>';
        } else return $bbcode->nbbchtmlspecialchars($params['_tag']) . $content . $bbcode->nbbchtmlspecialchars($params['_endtag']);
    }

    /**
     * @param BBCode_Parser $bbcode
     * @param $action
     * @param $name
     * @param $default
     * @param $params
     * @param $content
     * @return bool|string
     */
    function DoEmail($bbcode, $action, $name, $default, $params, $content) {
        if ($action == BBCODE_CHECK) return true;
        $email = is_string($default) ? $default : $bbcode->UnHTMLEncode(strip_tags($content));
        if ($bbcode->IsValidEmail($email)) return '<a href="mailto:' . $bbcode->nbbchtmlspecialchars($email) . '" class="bbcode_email">' . $content . '</a>';
        else return $bbcode->nbbchtmlspecialchars($params['_tag']) . $content . $bbcode->nbbchtmlspecialchars($params['_endtag']);
    }

    /**
     * @param $bbcode
     * @param $action
     * @param $name
     * @param $default
     * @param $params
     * @param $content
     * @return string
     */
    function DoSize($bbcode, $action, $name, $default, $params, $content) {
        switch ($default) {
            case '0':
                $size = '.5em';
            break;
            case '1':
                $size = '.67em';
            break;
            case '2':
                $size = '.83em';
            break;
            default:
            case '3':
                $size = '1.0em';
            break;
            case '4':
                $size = '1.17em';
            break;
            case '5':
                $size = '1.5em';
            break;
            case '6':
                $size = '2.0em';
            break;
            case '7':
                $size = '2.5em';
            break;
        }
        return "<span style=\"font-size:$size\">$content</span>";
    }

    /**
     * @param $bbcode
     * @param $action
     * @param $name
     * @param $default
     * @param $params
     * @param $content
     * @return string
     */
    function DoFont($bbcode, $action, $name, $default, $params, $content) {
        $fonts = explode(",", $default);
        $result = '';
        $special_fonts = Array('serif' => 'serif', 'sans-serif' => 'sans-serif', 'sans serif' => 'sans-serif', 'sansserif' => 'sans-serif', 'sans' => 'sans-serif', 'cursive' => 'cursive', 'fantasy' => 'fantasy', 'monospace' => 'monospace', 'mono' => 'monospace',);
        foreach($fonts as $font) {
            $font = trim($font);
            if (isset($special_fonts[$font])) {
                if (strlen($result) > 0) $result.= ",";
                $result.= $special_fonts[$font];
            } else if (strlen($font) > 0) {
                if (strlen($result) > 0) $result.= ",";
                $result.= "'$font'";
            }
        }
        return "<span style=\"font-family:$result\">$content</span>";
    }

    /**
     * @param BBCode_Parser $bbcode
     * @param $action
     * @param $name
     * @param $default
     * @param $params
     * @param $content
     * @return bool|string
     */
    function DoWiki($bbcode, $action, $name, $default, $params, $content) {
        $name = $bbcode->Wikify($default);
        if ($action == BBCODE_CHECK) return strlen($name) > 0;
        $title = trim(@$params['title']);
        if (strlen($title) <= 0) $title = trim($default);
        return "<a href=\"{$bbcode->wiki_url}$name\" class=\"bbcode_wiki\">" . $bbcode->nbbchtmlspecialchars($title) . "</a>";
    }

    /**
     * @param BBCode_Parser $bbcode
     * @param $action
     * @param $name
     * @param $default
     * @param $params
     * @param $content
     * @return bool|string
     */
    function DoImage($bbcode, $action, $name, $default, $params, $content) {
        if ($action == BBCODE_CHECK) return true;
        $content = trim($bbcode->UnHTMLEncode(strip_tags($content)));
        if (preg_match("/\\.(?:gif|jpeg|jpg|jpe|png)$/", $content)) {
            if (preg_match("/^[a-zA-Z0-9_][^:]+$/", $content)) {
                if (!preg_match("/(?:\\/\\.\\.\\/)|(?:^\\.\\.\\/)|(?:^\\/)/", $content)) {
                    $info = @getimagesize("{$bbcode->local_img_dir}/{$content}");
                    if ($info[2] == IMAGETYPE_GIF || $info[2] == IMAGETYPE_JPEG || $info[2] == IMAGETYPE_PNG) {
                        return "<img src=\"" . $bbcode->nbbchtmlspecialchars("{$bbcode->local_img_url}/{$content}") . "\" alt=\"" . $bbcode->nbbchtmlspecialchars(basename($content)) . "\" width=\"" . $bbcode->nbbchtmlspecialchars($info[0]) . "\" height=\"" . $bbcode->nbbchtmlspecialchars($info[1]) . "\" class=\"bbcode_img\" />";
                    }
                }
            } else if ($bbcode->IsValidURL($content, false)) {
                return "<img src=\"" . $bbcode->nbbchtmlspecialchars($content) . "\" alt=\"" . $bbcode->nbbchtmlspecialchars(basename($content)) . "\" class=\"bbcode_img\" />";
            }
        }
        return $bbcode->nbbchtmlspecialchars($params['_tag']) . $bbcode->nbbchtmlspecialchars($content) . $bbcode->nbbchtmlspecialchars($params['_endtag']);
    }

    /**
     * @param $bbcode
     * @param $action
     * @param $name
     * @param $default
     * @param $params
     * @param $content
     * @return bool|string
     */
    function DoRule($bbcode, $action, $name, $default, $params, $content) {
        if ($action == BBCODE_CHECK) return true;
        else return $bbcode->rule_html;
    }

    /**
     * @param BBCode_Parser $bbcode
     * @param $action
     * @param $name
     * @param $default
     * @param $params
     * @param $content
     * @return bool|string
     */
    function DoQuote($bbcode, $action, $name, $default, $params, $content) {
        if ($action == BBCODE_CHECK) return true;
        if (isset($params['name'])) {
            $title = $bbcode->nbbchtmlspecialchars(trim($params['name'])) . " wrote";
            if (isset($params['date'])) $title.= " on " . $bbcode->nbbchtmlspecialchars(trim($params['date']));
            $title.= ":";
            if (isset($params['url'])) {
                $url = trim($params['url']);
                if ($bbcode->IsValidURL($url)) $title = "<a href=\"" . $bbcode->nbbchtmlspecialchars($params['url']) . "\">" . $title . "</a>";
            }
        } else if (!is_string($default)) $title = "Quote:";
        else $title = $bbcode->nbbchtmlspecialchars(trim($default)) . " wrote:";
        return "\n<div class=\"bbcode_quote\">\n<div class=\"bbcode_quote_head\">" . $title . "</div>\n<div class=\"bbcode_quote_body\">" . $content . "</div>\n</div>\n";
    }

    /**
     * @param $bbcode
     * @param $action
     * @param $name
     * @param $default
     * @param $params
     * @param $content
     * @return bool|string
     */
    function DoList($bbcode, $action, $name, $default, $params, $content) {
        $list_styles = Array('1' => 'decimal', '01' => 'decimal-leading-zero', 'i' => 'lower-roman', 'I' => 'upper-roman', 'a' => 'lower-alpha', 'A' => 'upper-alpha',);
        $ci_list_styles = Array('circle' => 'circle', 'disc' => 'disc', 'square' => 'square', 'greek' => 'lower-greek', 'armenian' => 'armenian', 'georgian' => 'georgian',);
        $ul_types = Array('circle' => 'circle', 'disc' => 'disc', 'square' => 'square',);
        $default = trim($default);
        if ($action == BBCODE_CHECK) {
            if (!is_string($default) || strlen($default) == "") return true;
            else if (isset($list_styles[$default])) return true;
            else if (isset($ci_list_styles[strtolower($default) ])) return true;
            else return false;
        }
        $type = '';
        /**
         * @ignore
         * @var $elem string
         */
        if (!is_string($default) || strlen($default) == "") {
            $elem = 'ul';
            $type = '';
        } else if ($default == '1') {
            $elem = 'ol';
            $type = '';
        } else if (isset($list_styles[$default])) {
            $elem = 'ol';
            $type = $list_styles[$default];
        } else {
            $default = strtolower($default);
            if (isset($ul_types[$default])) {
                $elem = 'ul';
                $type = $ul_types[$default];
            } else if (isset($ci_list_styles[$default])) {
                $elem = 'ol';
                $type = $ci_list_styles[$default];
            }
        }
        if (strlen($type)) return "\n<$elem class=\"bbcode_list\" style=\"list-style-type:$type\">\n$content</$elem>\n";
        else return "\n<$elem class=\"bbcode_list\">\n$content</$elem>\n";
    }
}

/**
 *
 */
class BBCodeEmailAddressValidator {
    /**
     * @param $strEmailAddress
     * @return bool
     */
    function check_email_address($strEmailAddress) {
        if (preg_match('/[\x00-\x1F\x7F-\xFF]/', $strEmailAddress)) {
            return false;
        }
        $intAtSymbol = strrpos($strEmailAddress, '@');
        if ($intAtSymbol === false) {
            return false;
        }
        $arrEmailAddress[0] = substr($strEmailAddress, 0, $intAtSymbol);
        $arrEmailAddress[1] = substr($strEmailAddress, $intAtSymbol + 1);
        $arrTempAddress[0] = preg_replace('/"[^"]+"/', '', $arrEmailAddress[0]);
        $arrTempAddress[1] = $arrEmailAddress[1];
        $strTempAddress = $arrTempAddress[0] . $arrTempAddress[1];
        if (strrpos($strTempAddress, '@') !== false) {
            return false;
        }
        if (!$this->check_local_portion($arrEmailAddress[0])) {
            return false;
        }
        if (!$this->check_domain_portion($arrEmailAddress[1])) {
            return false;
        }
        return true;
    }

    /**
     * @param $strLocalPortion
     * @return bool
     */
    function check_local_portion($strLocalPortion) {
        if (!$this->check_text_length($strLocalPortion, 1, 64)) {
            return false;
        }
        $arrLocalPortion = explode('.', $strLocalPortion);
        for ($i = 0, $max = sizeof($arrLocalPortion);$i < $max;$i++) {
            if (!preg_match('.^(' . '([A-Za-z0-9!#$%&\'*+/=?^_`{|}~-]' . '[A-Za-z0-9!#$%&\'*+/=?^_`{|}~-]{0,63})' . '|' . '("[^\\\"]{0,62}")' . ')$.', $arrLocalPortion[$i])) {
                return false;
            }
        }
        return true;
    }

    /**
     * @param $strDomainPortion
     * @return bool
     */
    function check_domain_portion($strDomainPortion) {
        if (!$this->check_text_length($strDomainPortion, 1, 255)) {
            return false;
        }
        if (preg_match('/^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])' . '(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}$/', $strDomainPortion) || preg_match('/^\[(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])' . '(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}\]$/', $strDomainPortion)) {
            return true;
        } else {
            $arrDomainPortion = explode('.', $strDomainPortion);
            if (sizeof($arrDomainPortion) < 2) {
                return false;
            }
            for ($i = 0, $max = sizeof($arrDomainPortion);$i < $max;$i++) {
                if (!$this->check_text_length($arrDomainPortion[$i], 1, 63)) {
                    return false;
                }
                if (!preg_match('/^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|' . '([A-Za-z0-9]+))$/', $arrDomainPortion[$i])) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * @param $strText
     * @param $intMinimum
     * @param $intMaximum
     * @return bool
     */
    function check_text_length($strText, $intMinimum, $intMaximum) {
        $intTextLength = strlen($strText);
        if (($intTextLength < $intMinimum) || ($intTextLength > $intMaximum)) {
            return false;
        } else {
            return true;
        }
    }
}

/**
 *
 */
class BBCode_Parser {
    var $tag_rules;
    var $defaults;
    var $current_class;
    var $root_class;
    var $lost_start_tags;
    var $start_tags;
    var $allow_ampersand;
    var $tag_marker;
    var $ignore_newlines;
    var $plain_mode;
    var $detect_urls;
    var $url_pattern;
    var $output_limit;
    var $text_length;
    var $was_limited;
    var $limit_tail;
    var $limit_precision;
    var $smiley_dir;
    var $smiley_url;
    var $smileys;
    var $smiley_regex;
    var $enable_smileys;
    var $wiki_url;
    var $local_img_dir;
    var $local_img_url;
    var $url_targetable;
    var $url_target;
    var $rule_html;
    var $pre_trim;
    var $post_trim;
    var $debug;
    var $quote_style;
    var $charset;
    var $stack;
    /**
     * @var $lexer BBCodeLexer
     */
    var $lexer;
    var $smiley_info;
    function BBCode_Parser() {
        $this->defaults = new BBCodeLibrary;
        $this->tag_rules = $this->defaults->default_tag_rules;
        $this->smileys = $this->defaults->default_smileys;
        $this->enable_smileys = true;
        $this->smiley_regex = false;
        $this->smiley_dir = $this->GetDefaultSmileyDir();
        $this->smiley_url = $this->GetDefaultSmileyURL();
        $this->wiki_url = $this->GetDefaultWikiURL();
        $this->local_img_dir = $this->GetDefaultLocalImgDir();
        $this->local_img_url = $this->GetDefaultLocalImgURL();
        $this->rule_html = $this->GetDefaultRuleHTML();
        $this->pre_trim = '';
        $this->post_trim = '';
        $this->root_class = 'block';
        $this->lost_start_tags = Array();
        $this->start_tags = Array();
        $this->tag_marker = '[';
        $this->allow_ampsersand = false;
        $this->current_class = $this->root_class;
        $this->debug = false;
        $this->ignore_newlines = false;
        $this->output_limit = 0;
        $this->plain_mode = false;
        $this->was_limited = false;
        $this->limit_tail = "...";
        $this->limit_precision = 0.15;
        $this->detect_urls = false;
        $this->url_pattern = '<a href="{$url/h}">{$text/h}</a>';
        $this->url_targetable = false;
        $this->url_target = false;
        $this->quote_style = ENT_COMPAT;
        $this->charset = 'UTF-8';
    }

    /**
     * @param int $style
     */
    function SetQuoteStyle($style = ENT_COMPAT) {
        $this->quote_style = $style;
    }

    /**
     * @param string $charset
     */
    function SetCharset($charset = 'UTF-8') {
        $this->charset = $charset;
    }

    /**
     * @param string $trim
     */
    function SetPreTrim($trim = "a") {
        $this->pre_trim = $trim;
    }

    /**
     * @return string
     */
    function GetPreTrim() {
        return $this->pre_trim;
    }

    /**
     * @param string $trim
     */
    function SetPostTrim($trim = "a") {
        $this->post_trim = $trim;
    }

    /**
     * @return string
     */
    function GetPostTrim() {
        return $this->post_trim;
    }

    /**
     * @param string $class
     */
    function SetRoot($class = 'block') {
        $this->root_class = $class;
    }
    function SetRootInline() {
        $this->root_class = 'inline';
    }
    function SetRootBlock() {
        $this->root_class = 'block';
    }

    /**
     * @return string
     */
    function GetRoot() {
        return $this->root_class;
    }

    /**
     * @param bool $enable
     */
    function SetDebug($enable = true) {
        $this->debug = $enable;
    }

    /**
     * @return bool
     */
    function GetDebug() {
        return $this->debug;
    }

    /**
     * @param bool $enable
     */
    function SetAllowAmpersand($enable = true) {
        $this->allow_ampersand = $enable;
    }
    function GetAllowAmpersand() {
        return $this->allow_ampersand;
    }

    /**
     * @param string $marker
     */
    function SetTagMarker($marker = '[') {
        $this->tag_marker = $marker;
    }

    /**
     * @return string
     */
    function GetTagMarker() {
        return $this->tag_marker;
    }

    /**
     * @param bool $ignore
     */
    function SetIgnoreNewlines($ignore = true) {
        $this->ignore_newlines = $ignore;
    }

    /**
     * @return bool
     */
    function GetIgnoreNewlines() {
        return $this->ignore_newlines;
    }

    /**
     * @param int $limit
     */
    function SetLimit($limit = 0) {
        $this->output_limit = $limit;
    }

    /**
     * @return int
     */
    function GetLimit() {
        return $this->output_limit;
    }

    /**
     * @param string $tail
     */
    function SetLimitTail($tail = "...") {
        $this->limit_tail = $tail;
    }

    /**
     * @return string
     */
    function GetLimitTail() {
        return $this->limit_tail;
    }

    /**
     * @param float $prec
     */
    function SetLimitPrecision($prec = 0.15) {
        $this->limit_precision = $prec;
    }

    /**
     * @return float
     */
    function GetLimitPrecision() {
        return $this->limit_precision;
    }

    /**
     * @return bool
     */
    function WasLimited() {
        return $this->was_limited;
    }

    /**
     * @param bool $enable
     */
    function SetPlainMode($enable = true) {
        $this->plain_mode = $enable;
    }

    /**
     * @return bool
     */
    function GetPlainMode() {
        return $this->plain_mode;
    }

    /**
     * @param bool $enable
     */
    function SetDetectURLs($enable = true) {
        $this->detect_urls = $enable;
    }

    /**
     * @return bool
     */
    function GetDetectURLs() {
        return $this->detect_urls;
    }

    /**
     * @param $pattern
     */
    function SetURLPattern($pattern) {
        $this->url_pattern = $pattern;
    }

    /**
     * @return string
     */
    function GetURLPattern() {
        return $this->url_pattern;
    }

    /**
     * @param $enable
     */
    function SetURLTargetable($enable) {
        $this->url_targetable = $enable;
    }

    /**
     * @return bool
     */
    function GetURLTargetable() {
        return $this->url_targetable;
    }

    /**
     * @param $target
     */
    function SetURLTarget($target) {
        $this->url_target = $target;
    }

    /**
     * @return bool
     */
    function GetURLTarget() {
        return $this->url_target;
    }

    /**
     * @param $name
     * @param $rule
     */
    function AddRule($name, $rule) {
        $this->tag_rules[$name] = $rule;
    }

    /**
     * @param $name
     */
    function RemoveRule($name) {
        unset($this->tag_rules[$name]);
    }

    /**
     * @param $name
     * @return bool
     */
    function GetRule($name) {
        return isset($this->tag_rules[$name]) ? $this->tag_rules[$name] : false;
    }
    function ClearRules() {
        $this->tag_rules = Array();
    }

    /**
     * @param $name
     * @return bool
     */
    function GetDefaultRule($name) {
        return isset($this->defaults->default_tag_rules[$name]) ? $this->defaults->default_tag_rules[$name] : false;
    }

    /**
     * @param $name
     */
    function SetDefaultRule($name) {
        if (isset($this->defaults->default_tag_rules[$name])) $this->AddRule($name, $this->defaults->default_tag_rules[$name]);
        else $this->RemoveRule($name);
    }

    /**
     * @return array
     */
    function GetDefaultRules() {
        return $this->defaults->default_tag_rules;
    }
    function SetDefaultRules() {
        $this->tag_rules = $this->defaults->default_tag_rules;
    }

    /**
     * @param $url
     */
    function SetWikiURL($url) {
        $this->wiki_url = $url;
    }

    /**
     * @param $url
     * @return string
     */
    function GetWikiURL($url) {
        return $this->wiki_url;
    }

    /**
     * @return string
     */
    function GetDefaultWikiURL() {
        return '/?page=';
    }

    /**
     * @param $path
     */
    function SetLocalImgDir($path) {
        $this->local_img_dir = $path;
    }

    /**
     * @return string
     */
    function GetLocalImgDir() {
        return $this->local_img_dir;
    }

    /**
     * @return string
     */
    function GetDefaultLocalImgDir() {
        return "img";
    }

    /**
     * @param $path
     */
    function SetLocalImgURL($path) {
        $this->local_img_url = $path;
    }

    /**
     * @return string
     */
    function GetLocalImgURL() {
        return $this->local_img_url;
    }

    /**
     * @return string
     */
    function GetDefaultLocalImgURL() {
        return "img";
    }

    /**
     * @param $html
     */
    function SetRuleHTML($html) {
        $this->rule_html = $html;
    }

    /**
     * @return string
     */
    function GetRuleHTML() {
        return $this->rule_html;
    }

    /**
     * @return string
     */
    function GetDefaultRuleHTML() {
        return "\n<hr class=\"bbcode_rule\" />\n";
    }

    /**
     * @param $code
     * @param $image
     */
    function AddSmiley($code, $image) {
        $this->smileys[$code] = $image;
        $this->smiley_regex = false;
    }

    /**
     * @param $code
     */
    function RemoveSmiley($code) {
        unset($this->smileys[$code]);
        $this->smiley_regex = false;
    }

    /**
     * @param $code
     * @return bool
     */
    function GetSmiley($code) {
        return isset($this->smileys[$code]) ? $this->smileys[$code] : false;
    }
    function ClearSmileys() {
        $this->smileys = Array();
        $this->smiley_regex = false;
    }

    /**
     * @param $code
     * @return bool
     */
    function GetDefaultSmiley($code) {
        return isset($this->defaults->default_smileys[$code]) ? $this->defaults->default_smileys[$code] : false;
    }

    /**
     * @param $code
     */
    function SetDefaultSmiley($code) {
        $this->smileys[$code] = @$this->defaults->default_smileys[$code];
        $this->smiley_regex = false;
    }

    /**
     * @return array
     */
    function GetDefaultSmileys() {
        return $this->defaults->default_smileys;
    }
    function SetDefaultSmileys() {
        $this->smileys = $this->defaults->default_smileys;
        $this->smiley_regex = false;
    }

    /**
     * @param $path
     */
    function SetSmileyDir($path) {
        $this->smiley_dir = $path;
    }

    /**
     * @return string
     */
    function GetSmileyDir() {
        return $this->smiley_dir;
    }

    /**
     * @return string
     */
    function GetDefaultSmileyDir() {
        return "smileys";
    }

    /**
     * @param $path
     */
    function SetSmileyURL($path) {
        $this->smiley_url = $path;
    }

    /**
     * @return string
     */
    function GetSmileyURL() {
        return $this->smiley_url;
    }

    /**
     * @return string
     */
    function GetDefaultSmileyURL() {
        return "smileys";
    }

    /**
     * @param bool $enable
     */
    function SetEnableSmileys($enable = true) {
        $this->enable_smileys = $enable;
    }

    /**
     * @return bool
     */
    function GetEnableSmileys() {
        return $this->enable_smileys;
    }

    /**
     * @param $string
     * @return mixed
     */
    function nl2br($string) {
        return preg_replace("/\\x0A|\\x0D|\\x0A\\x0D|\\x0D\\x0A/", "<br />\n", $string);
    }

    /**
     * @param $string
     * @return string
     */
    function UnHTMLEncode($string) {
        if (function_exists("html_entity_decode")) return html_entity_decode($string, $this->quote_style, $this->charset);
        $string = preg_replace('~&#x([0-9a-f]+);~ei', 'chr(hexdec("\\1"))', $string);
        $string = preg_replace('~&#([0-9]+);~e', 'chr("\\1")', $string);
        $trans_tbl = get_html_translation_table(HTML_ENTITIES, $this->quote_style);
        $trans_tbl = array_flip($trans_tbl);
        return strtr($string, $trans_tbl);
    }

    /**
     * @param $string
     * @return string
     */
    function Wikify($string) {
        return rawurlencode(str_replace(" ", "_", trim(preg_replace("/[!?;@#\$%\\^&*<>=+`~\\x00-\\x20_-]+/", " ", $string))));
    }

    /**
     * @param $string
     * @param bool $email_too
     * @return bool
     */
    function IsValidURL($string, $email_too = true) {
        if (preg_match("/^
(?:https?|ftp):\\/\\/
(?:
(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+
[a-zA-Z0-9]
(?:[a-zA-Z0-9-]*[a-zA-Z0-9])?
|
\\[
(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}
(?:
25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-zA-Z0-9-]*[a-zA-Z0-9]:
(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21-\\x5A\\x53-\\x7F]
|\\\\[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F])+
)
\\]
)
(?::[0-9]{1,5})?
(?:[\\/\\?\\#][^\\n\\r]*)?
$/Dx", $string)) return true;
        if (preg_match("/^[^:]+([\\/\\\\?#][^\\r\\n]*)?$/D", $string)) return true;
        if ($email_too) if (substr($string, 0, 7) == "mailto:") return $this->IsValidEmail(substr($string, 7));
        return false;
    }

    /**
     * @param $string
     * @return bool
     */
    function IsValidEmail($string) {
        $validator = new BBCodeEmailAddressValidator;
        return $validator->check_email_address($string);
        /*
        return preg_match("/^
        (?:
        [a-z0-9\\!\\#\\\$\\%\\&\\'\\*\\+\\/=\\?\\^_`\\{\\|\\}~-]+
        (?:\.[a-z0-9\\!\\#\\\$\\%\\&\\'\\*\\+\\/=\\?\\^_`\\{\\|\\}~-]+)*
        |
        \"(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F]
        |\\\\[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F])*\"
        )
        @
        (?:
        (?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+
        [a-z0-9]
        (?:[a-z0-9-]*[a-z0-9])?
        |
        \\[
        (?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}
        (?:
        25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:
        (?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21-\\x5A\\x53-\\x7F]
        |\\\\[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F])+
        )
        \\]
        )
        $/Dx", $string);
        */
    }

    /**
     * @param $string
     * @return mixed|string
     */
    function HTMLEncode($string) {
        if (!$this->allow_ampersand) return $this->nbbchtmlspecialchars($string);
        else return str_replace(Array('<', '>', '"'), Array('&lt;', '&gt;', '&quot;'), $string);
    }

    /**
     * @param $string
     * @return array|mixed|string
     */
    function FixupOutput($string) {
        if (!$this->detect_urls) {
            $output = $this->Internal_ProcessSmileys($string);
        } else {
            $chunks = $this->Internal_AutoDetectURLs($string);
            $output = Array();
            if (count($chunks)) {
                $is_a_url = false;
                foreach($chunks as $index => $chunk) {
                    if (!$is_a_url) {
                        $chunk = $this->Internal_ProcessSmileys($chunk);
                    }
                    $output[] = $chunk;
                    $is_a_url = !$is_a_url;
                }
            }
            $output = implode("", $output);
        }
        return $output;
    }

    /**
     * @param $string
     * @return mixed|string
     */
    function Internal_ProcessSmileys($string) {
        if (!$this->enable_smileys || $this->plain_mode) {
            $output = $this->HTMLEncode($string);
        } else {
            if ($this->smiley_regex === false) {
                $this->Internal_RebuildSmileys();
            }
            $tokens = preg_split($this->smiley_regex, $string, -1, PREG_SPLIT_DELIM_CAPTURE);
            if (count($tokens) <= 1) {
                $output = $this->HTMLEncode($string);
            } else {
                $output = '';
                $is_a_smiley = false;
                foreach($tokens as $token) {
                    if (!$is_a_smiley) {
                        $output.= $this->HTMLEncode($token);
                    } else {
                        if (isset($this->smiley_info[$token])) {
                            $info = $this->smiley_info[$token];
                        } else {
                            $info = @getimagesize($this->smiley_dir . '/' . $this->smileys[$token]);
                            $this->smiley_info[$token] = $info;
                        }
                        $alt = $this->nbbchtmlspecialchars($token);
                        $output.= "<img src=\"" . $this->nbbchtmlspecialchars($this->smiley_url . '/' . $this->smileys[$token]) . "\" width=\"{$info[0]}\" height=\"{$info[1]}\"" . " alt=\"$alt\" title=\"$alt\" class=\"bbcode_smiley\" />";
                    }
                    $is_a_smiley = !$is_a_smiley;
                }
            }
        }
        return $output;
    }
    function Internal_RebuildSmileys() {
        $regex = Array("/(?<![\\w])(");
        $first = true;
        foreach($this->smileys as $code => $filename) {
            if (!$first) $regex[] = "|";
            $regex[] = preg_quote("$code", '/');
            $first = false;
        }
        $regex[] = ")(?![\\w])/";
        $this->smiley_regex = implode("", $regex);
    }

    /**
     * @param $string
     * @return array
     */
    function Internal_AutoDetectURLs($string) {
        $output = preg_split("/( (?:
(?:https?|ftp) : \\/*
(?:
(?: (?: [a-zA-Z0-9-]{2,} \\. )+
(?: arpa | com | org | net | edu | gov | mil | int | [a-z]{2}
| aero | biz | coop | info | museum | name | pro
| example | invalid | localhost | test | local | onion | swift ) )
| (?: [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} )
| (?: [0-9A-Fa-f:]+ : [0-9A-Fa-f]{1,4} )
)
(?: : [0-9]+ )?
(?! [a-zA-Z0-9.:-] )
(?:
\\/
[^&?#\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]*
)?
(?:
[?#]
[^\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]+
)?
) | (?:
(?:
(?: (?: [a-zA-Z0-9-]{2,} \\. )+
(?: arpa | com | org | net | edu | gov | mil | int | [a-z]{2}
| aero | biz | coop | info | museum | name | pro
| example | invalid | localhost | test | local | onion | swift ) )
| (?: [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} )
)
(?: : [0-9]+ )?
(?! [a-zA-Z0-9.:-] )
(?:
\\/
[^&?#\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]*
)?
(?:
[?#]
[^\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]+
)?
) | (?:
[a-zA-Z0-9._-]{2,} @
(?:
(?: (?: [a-zA-Z0-9-]{2,} \\. )+
(?: arpa | com | org | net | edu | gov | mil | int | [a-z]{2}
| aero | biz | coop | info | museum | name | pro
| example | invalid | localhost | test | local | onion | swift ) )
| (?: [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} )
)
) )/Dx", $string, -1, PREG_SPLIT_DELIM_CAPTURE);
        if (count($output) > 1) {
            $is_a_url = false;
            foreach($output as $index => $token) {
                if ($is_a_url) {
                    if (preg_match("/^[a-zA-Z0-9._-]{2,}@/", $token)) {
                        $url = "mailto:" . $token;
                    } else if (preg_match("/^(https?:|ftp:)\\/*([^\\/&?#]+)\\/*(.*)\$/", $token, $matches)) {
                        $url = $matches[1] . '/' . '/' . $matches[2] . "/" . $matches[3];
                    } else {
                        preg_match("/^([^\\/&?#]+)\\/*(.*)\$/", $token, $matches);
                        $url = "http:/" . "/" . $matches[1] . "/" . $matches[2];
                    }
                    $params = @parse_url($url);
                    if (!is_array($params)) $params = Array();
                    $params['url'] = $url;
                    $params['link'] = $url;
                    $params['text'] = $token;
                    $output[$index] = $this->FillTemplate($this->url_pattern, $params);
                }
                $is_a_url = !$is_a_url;
            }
        }
        return $output;
    }

    /**
     * @param $template
     * @param $insert_array
     * @param array $default_array
     * @return string
     */
    function FillTemplate($template, $insert_array, $default_array = Array()) {
        $pieces = preg_split('/(\{\$[a-zA-Z0-9_.:\/-]+\})/', $template, -1, PREG_SPLIT_DELIM_CAPTURE);
        if (count($pieces) <= 1) return $template;
        $result = Array();
        $is_an_insert = false;
        foreach($pieces as $piece) {
            if (!$is_an_insert) {
                $result[] = $piece;
            } else if (!preg_match('/\{\$([a-zA-Z0-9_:-]+)((?:\\.[a-zA-Z0-9_:-]+)*)(?:\/([a-zA-Z0-9_:-]+))?\}/', $piece, $matches)) {
                $result[] = $piece;
            } else {
                if (isset($insert_array[$matches[1]])) $value = @$insert_array[$matches[1]];
                else $value = @$default_array[$matches[1]];
                if (strlen(@$matches[2])) {
                    foreach(explode(".", substr($matches[2], 1)) as $index) {
                        if (is_array($value)) $value = @$value[$index];
                        else if (is_object($value)) {
                            $value = (array)$value;
                            $value = @$value[$index];
                        } else $value = '';
                    }
                }
                switch (gettype($value)) {
                    case 'boolean':
                        $value = $value ? "true" : "false";
                    break;
                    case 'integer':
                        $value = (string)$value;
                    break;
                    case 'double':
                        $value = (string)$value;
                    break;
                    case 'string':
                    break;
                    default:
                        $value = '';
                    break;
                }
                if (strlen(@$matches[3])) $flags = array_flip(str_split($matches[3]));
                else $flags = Array();
                if (!isset($flags['v'])) {
                    if (isset($flags['w'])) $value = preg_replace("/[\\x00-\\x09\\x0B-\x0C\x0E-\\x20]+/", " ", $value);
                    if (isset($flags['t'])) $value = trim($value);
                    if (isset($flags['b'])) $value = basename($value);
                    if (isset($flags['e'])) $value = $this->HTMLEncode($value);
                    else if (isset($flags['k'])) $value = $this->Wikify($value);
                    else if (isset($flags['h'])) $value = $this->nbbchtmlspecialchars($value);
                    else if (isset($flags['u'])) $value = urlencode($value);
                    if (isset($flags['n'])) $value = $this->nl2br($value);
                }
                $result[] = $value;
            }
            $is_an_insert = !$is_an_insert;
        }
        return implode("", $result);
    }

    /**
     * @param $array
     * @param int $start
     * @return string
     */
    function Internal_CollectText($array, $start = 0) {
        ob_start();
        for ($start = intval($start), $end = count($array);$start < $end;$start++) print $array[$start][BBCODE_STACK_TEXT];
        $output = ob_get_contents();
        ob_end_clean();
        return $output;
    }

    /**
     * @param $array
     * @param int $start
     * @param int $end
     * @return string
     */
    function Internal_CollectTextReverse($array, $start = 0, $end = 0) {
        ob_start();
        for ($start = intval($start);$start >= $end;$start--) print $array[$start][BBCODE_STACK_TEXT];
        $output = ob_get_contents();
        ob_end_clean();
        return $output;
    }

    /**
     * @param $pos
     * @return array
     */
    function Internal_GenerateOutput($pos) {
        $output = Array();
        while (count($this->stack) > $pos) {
            $token = array_pop($this->stack);
            if ($token[BBCODE_STACK_TOKEN] != BBCODE_TAG) {
                $output[] = $token;
            } else {
                $name = @$token[BBCODE_STACK_TAG]['_name'];
                $rule = @$this->tag_rules[$name];
                $end_tag = @$rule['end_tag'];
                if (!isset($rule['end_tag'])) $end_tag = BBCODE_REQUIRED;
                else $end_tag = $rule['end_tag'];
                array_pop($this->start_tags[$name]);
                if ($end_tag == BBCODE_PROHIBIT) {
                    $output[] = Array(BBCODE_STACK_TOKEN => BBCODE_TEXT, BBCODE_STACK_TAG => false, BBCODE_STACK_TEXT => $token[BBCODE_STACK_TEXT], BBCODE_STACK_CLASS => $this->current_class,);
                } else {
                    if ($end_tag == BBCODE_REQUIRED) @$this->lost_start_tags[$name]+= 1;
                    $end = $this->Internal_CleanupWSByIteratingPointer(@$rule['before_endtag'], 0, $output);
                    $this->Internal_CleanupWSByPoppingStack(@$rule['after_tag'], $output);
                    $tag_body = $this->Internal_CollectTextReverse($output, count($output) - 1, $end);
                    $this->Internal_CleanupWSByPoppingStack(@$rule['before_tag'], $this->stack);
                    $this->Internal_UpdateParamsForMissingEndTag(@$token[BBCODE_STACK_TAG]);
                    $tag_output = $this->DoTag(BBCODE_OUTPUT, $name, @$token[BBCODE_STACK_TAG]['_default'], @$token[BBCODE_STACK_TAG], $tag_body);
                    $output = Array(Array(BBCODE_STACK_TOKEN => BBCODE_TEXT, BBCODE_STACK_TAG => false, BBCODE_STACK_TEXT => $tag_output, BBCODE_STACK_CLASS => $this->current_class));
                }
            }
        }
        $this->Internal_ComputeCurrentClass();
        return $output;
    }

    /**
     * @param $class_list
     * @return bool
     */
    function Internal_RewindToClass($class_list) {
        $pos = count($this->stack) - 1;
        while ($pos >= 0 && !in_array($this->stack[$pos][BBCODE_STACK_CLASS], $class_list)) $pos--;
        if ($pos < 0) {
            if (!in_array($this->root_class, $class_list)) return false;
        }
        $output = $this->Internal_GenerateOutput($pos + 1);
        while (count($output)) {
            $token = array_pop($output);
            $token[BBCODE_STACK_CLASS] = $this->current_class;
            $this->stack[] = $token;
        }
        return true;
    }

    /**
     * @param $tag_name
     * @return array|bool|string
     */
    function Internal_FinishTag($tag_name) {
        if (strlen($tag_name) <= 0) return false;
        if (isset($this->start_tags[$tag_name]) && count($this->start_tags[$tag_name])) $pos = array_pop($this->start_tags[$tag_name]);
        else $pos = - 1;
        if ($pos < 0) return false;
        $newpos = $this->Internal_CleanupWSByIteratingPointer(@$this->tag_rules[$tag_name]['after_tag'], $pos + 1, $this->stack);
        $delta = $newpos - ($pos + 1);
        $output = $this->Internal_GenerateOutput($newpos);
        $newend = $this->Internal_CleanupWSByIteratingPointer(@$this->tag_rules[$tag_name]['before_endtag'], 0, $output);
        $output = $this->Internal_CollectTextReverse($output, count($output) - 1, $newend);
        while ($delta-- > 0) array_pop($this->stack);
        $this->Internal_ComputeCurrentClass();
        return $output;
    }
    function Internal_ComputeCurrentClass() {
        if (count($this->stack) > 0) $this->current_class = $this->stack[count($this->stack) - 1][BBCODE_STACK_CLASS];
        else $this->current_class = $this->root_class;
    }

    /**
     * @param array|bool $array
     * @param bool $raw
     * @return string
     */
    function Internal_DumpStack($array = false, $raw = false) {
        if (!$raw) $string = '<span style="color: #0000CC;">';
        else $string = '';
        if ($array === false) $array = $this->stack;
        foreach($array as $item) {
            switch (@$item[BBCODE_STACK_TOKEN]) {
                case BBCODE_TEXT:
                    $string.= "\"" . $this->nbbchtmlspecialchars(@$item[BBCODE_STACK_TEXT]) . "\" ";
                break;
                case BBCODE_WS:
                    $string.= "WS ";
                break;
                case BBCODE_NL:
                    $string.= "NL ";
                break;
                case BBCODE_TAG:
                    $string.= "[" . $this->nbbchtmlspecialchars(@$item[BBCODE_STACK_TAG]['_name']) . "] ";
                break;
                default:
                    $string.= "unknown ";
                break;
            }
        }
        if (!$raw) $string.= "</span>";
        return $string;
    }

    /**
     * @param $pattern
     * @param $array
     * @return mixed
     */
    function Internal_CleanupWSByPoppingStack($pattern, &$array) {
        if (strlen($pattern) <= 0) return;
        $oldlen = count($array);
        foreach(str_split($pattern) as $char) {
            switch ($char) {
                case 's':
                    while (count($array) > 0 && $array[count($array) - 1][BBCODE_STACK_TOKEN] == BBCODE_WS) array_pop($array);
                    break;
                case 'n':
                    if (count($array) > 0 && $array[count($array) - 1][BBCODE_STACK_TOKEN] == BBCODE_NL) array_pop($array);
                    break;
                case 'a':
                    while (count($array) > 0 && (($token = $array[count($array) - 1][BBCODE_STACK_TOKEN]) == BBCODE_WS || $token == BBCODE_NL)) array_pop($array);
                    break;
            }
        }
        if (count($array) != $oldlen) {
            $this->Internal_ComputeCurrentClass();
        }
    }

    /**
     * @param $pattern
     * @return mixed
     */
    function Internal_CleanupWSByEatingInput($pattern) {
        if (strlen($pattern) <= 0) return;
        foreach(str_split($pattern) as $char) {
            switch ($char) {
                case 's':
                    $token_type = $this->lexer->NextToken();
                    while ($token_type == BBCODE_WS) {
                        $token_type = $this->lexer->NextToken();
                    }
                    $this->lexer->UngetToken();
                break;
                case 'n':
                    $token_type = $this->lexer->NextToken();
                    if ($token_type != BBCODE_NL) $this->lexer->UngetToken();
                    break;
                case 'a':
                    $token_type = $this->lexer->NextToken();
                    while ($token_type == BBCODE_WS || $token_type == BBCODE_NL) {
                        $token_type = $this->lexer->NextToken();
                    }
                    $this->lexer->UngetToken();
                    break;
                }
        }
    }

    /**
     * @param $pattern
     * @param $pos
     * @param $array
     * @return mixed
     */
    function Internal_CleanupWSByIteratingPointer($pattern, $pos, $array) {
        if (strlen($pattern) <= 0) return $pos;
        foreach(str_split($pattern) as $char) {
            switch ($char) {
                case 's':
                    while ($pos < count($array) && $array[$pos][BBCODE_STACK_TOKEN] == BBCODE_WS) $pos++;
                    break;
                case 'n':
                    if ($pos < count($array) && $array[$pos][BBCODE_STACK_TOKEN] == BBCODE_NL) $pos++;
                    break;
                case 'a':
                    while ($pos < count($array) && (($token = $array[$pos][BBCODE_STACK_TOKEN]) == BBCODE_WS || $token == BBCODE_NL)) $pos++;
                    break;
            }
        }
        return $pos;
    }

    /**
     * @param $string
     * @param $limit
     * @return string
     */
    function Internal_LimitText($string, $limit) {
        $chunks = preg_split("/([\\x00-\\x20]+)/", $string, -1, PREG_SPLIT_DELIM_CAPTURE);
        $output = '';
        foreach($chunks as $chunk) {
            if (strlen($output) + strlen($chunk) > $limit) break;
            $output.= $chunk;
        }
        $output = rtrim($output);
        return $output;
    }
    function Internal_DoLimit() {
        $this->Internal_CleanupWSByPoppingStack("a", $this->stack);
        if (strlen($this->limit_tail) > 0) {
            $this->stack[] = Array(BBCODE_STACK_TOKEN => BBCODE_TEXT, BBCODE_STACK_TEXT => $this->limit_tail, BBCODE_STACK_TAG => false, BBCODE_STACK_CLASS => $this->current_class,);
        }
        $this->was_limited = true;
    }

    /**
     * @param $action
     * @param $tag_name
     * @param $default_value
     * @param $params
     * @param $contents
     * @return bool|mixed|string
     */
    function DoTag($action, $tag_name, $default_value, $params, $contents) {
        $tag_rule = @$this->tag_rules[$tag_name];
        switch ($action) {
            case BBCODE_CHECK:
                if (isset($tag_rule['allow'])) {
                    foreach($tag_rule['allow'] as $param => $pattern) {
                        if ($param == '_content') $value = $contents;
                        else if ($param == '_defaultcontent') {
                            if (strlen($default_value)) $value = $default_value;
                            else $value = $contents;
                        } else {
                            if (isset($params[$param])) $value = $params[$param];
                            else $value = @$tag_rule['default'][$param];
                        }
                        if (!preg_match($pattern, $value)) {
                            return false;
                        }
                    }
                    return true;
                }
                switch (@$tag_rule['mode']) {
                    default:
                    case BBCODE_MODE_SIMPLE:
                        $result = true;
                    break;
                    case BBCODE_MODE_ENHANCED:
                        $result = true;
                    break;
                    case BBCODE_MODE_INTERNAL:
                        $result = @call_user_func(Array($this, @$tag_rule['method']), BBCODE_CHECK, $tag_name, $default_value, $params, $contents);
                    break;
                    case BBCODE_MODE_LIBRARY:
                        $result = @call_user_func(Array($this->defaults, @$tag_rule['method']), $this, BBCODE_CHECK, $tag_name, $default_value, $params, $contents);
                    break;
                    case BBCODE_MODE_CALLBACK:
                        $result = @call_user_func(@$tag_rule['method'], $this, BBCODE_CHECK, $tag_name, $default_value, $params, $contents);
                    break;
                }
                return $result;
            case BBCODE_OUTPUT:
                if ($this->plain_mode) {
                    if (!isset($tag_rule['plain_content'])) $plain_content = Array('_content');
                    else $plain_content = $tag_rule['plain_content'];
                    $result = $possible_content = '';
                    foreach($plain_content as $possible_content) {
                        if ($possible_content == '_content' && strlen($contents) > 0) {
                            $result = $contents;
                            break;
                        }
                        if (isset($params[$possible_content]) && strlen($params[$possible_content]) > 0) {
                            $result = $this->nbbchtmlspecialchars($params[$possible_content]);
                            break;
                        }
                    }
                    $start = @$tag_rule['plain_start'];
                    $end = @$tag_rule['plain_end'];
                    if (isset($tag_rule['plain_link'])) {
                        $link = $possible_content = '';
                        foreach($tag_rule['plain_link'] as $possible_content) {
                            if ($possible_content == '_content' && strlen($contents) > 0) {
                                $link = $this->UnHTMLEncode(strip_tags($contents));
                                break;
                            }
                            if (isset($params[$possible_content]) && strlen($params[$possible_content]) > 0) {
                                $link = $params[$possible_content];
                                break;
                            }
                        }
                        $params = @parse_url($link);
                        if (!is_array($params)) $params = Array();
                        $params['link'] = $link;
                        $params['url'] = $link;
                        $start = $this->FillTemplate($start, $params);
                        $end = $this->FillTemplate($end, $params);
                    }
                    return $start . $result . $end;
                }
                switch (@$tag_rule['mode']) {
                    default:
                    case BBCODE_MODE_SIMPLE:
                        $result = @$tag_rule['simple_start'] . $contents . @$tag_rule['simple_end'];
                    break;
                    case BBCODE_MODE_ENHANCED:
                        $result = $this->Internal_DoEnhancedTag($tag_rule, $params, $contents);
                    break;
                    case BBCODE_MODE_INTERNAL:
                        $result = @call_user_func(Array($this, @$tag_rule['method']), BBCODE_OUTPUT, $tag_name, $default_value, $params, $contents);
                    break;
                    case BBCODE_MODE_LIBRARY:
                        $result = @call_user_func(Array($this->defaults, @$tag_rule['method']), $this, BBCODE_OUTPUT, $tag_name, $default_value, $params, $contents);
                    break;
                    case BBCODE_MODE_CALLBACK:
                        $result = @call_user_func(@$tag_rule['method'], $this, BBCODE_OUTPUT, $tag_name, $default_value, $params, $contents);
                    break;
                }
                return $result;
            default:
                return false;
            }
    }

    /**
     * @param $tag_rule
     * @param $params
     * @param $contents
     * @return string
     */
    function Internal_DoEnhancedTag($tag_rule, $params, $contents) {
        $params['_content'] = $contents;
        $params['_defaultcontent'] = strlen(@$params['_default']) ? $params['_default'] : $contents;
        return $this->FillTemplate(@$tag_rule['template'], $params, @$tag_rule['default']);
    }

    /**
     * @param $params
     */
    function Internal_UpdateParamsForMissingEndTag(&$params) {
        switch ($this->tag_marker) {
            case '[':
                $tail_marker = ']';
            break;
            case '<':
                $tail_marker = '>';
            break;
            case '{':
                $tail_marker = '}';
            break;
            case '(':
                $tail_marker = ')';
            break;
            default:
                $tail_marker = $this->tag_marker;
            break;
        }
        $params['_endtag'] = $this->tag_marker . '/' . $params['_name'] . $tail_marker;
    }

    /**
     * @param $tag_name
     * @param $tag_params
     * @param $tag_rule
     * @return mixed
     */
    function Internal_ProcessIsolatedTag($tag_name, $tag_params, $tag_rule) {
        if (!$this->DoTag(BBCODE_CHECK, $tag_name, @$tag_params['_default'], $tag_params, "")) {
            $this->stack[] = Array(BBCODE_STACK_TOKEN => BBCODE_TEXT, BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), BBCODE_STACK_TAG => false, BBCODE_STACK_CLASS => $this->current_class,);
            return;
        }
        $this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_tag'], $this->stack);
        $output = $this->DoTag(BBCODE_OUTPUT, $tag_name, @$tag_params['_default'], $tag_params, "");
        $this->Internal_CleanupWSByEatingInput(@$tag_rule['after_tag']);
        $this->stack[] = Array(BBCODE_STACK_TOKEN => BBCODE_TEXT, BBCODE_STACK_TEXT => $output, BBCODE_STACK_TAG => false, BBCODE_STACK_CLASS => $this->current_class,);
    }

    /**
     * @param $tag_name
     * @param $tag_params
     * @param $tag_rule
     * @return mixed
     */
    function Internal_ProcessVerbatimTag($tag_name, $tag_params, $tag_rule) {
        $state = $this->lexer->SaveState();
        $end_tag = $this->lexer->tagmarker . "/" . $tag_name . $this->lexer->end_tagmarker;
        $start = count($this->stack);
        $this->lexer->verbatim = true;
        /**
         * @ignore
         * @var $end_tag_params array
         */
        while (($token_type = $this->lexer->NextToken()) != BBCODE_EOI) {
            if ($this->lexer->text == $end_tag) {
                $end_tag_params = $this->lexer->tag;
                break;
            }
            if ($this->output_limit > 0 && $this->text_length + strlen($this->lexer->text) >= $this->output_limit) {
                $text = $this->Internal_LimitText($this->lexer->text, $this->output_limit - $this->text_length);
                if (strlen($text) > 0) {
                    $this->text_length+= strlen($text);
                    $this->stack[] = Array(BBCODE_STACK_TOKEN => BBCODE_TEXT, BBCODE_STACK_TEXT => $this->FixupOutput($text), BBCODE_STACK_TAG => false, BBCODE_STACK_CLASS => $this->current_class,);
                }
                $this->Internal_DoLimit();
                break;
            }
            $this->text_length+= strlen($this->lexer->text);
            $this->stack[] = Array(BBCODE_STACK_TOKEN => $token_type, BBCODE_STACK_TEXT => $this->nbbchtmlspecialchars($this->lexer->text), BBCODE_STACK_TAG => $this->lexer->tag, BBCODE_STACK_CLASS => $this->current_class,);
        }
        $this->lexer->verbatim = false;
        if ($token_type == BBCODE_EOI) {
            $this->lexer->RestoreState($state);
            $this->stack[] = Array(BBCODE_STACK_TOKEN => BBCODE_TEXT, BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), BBCODE_STACK_TAG => false, BBCODE_STACK_CLASS => $this->current_class,);
            return;
        }
        $newstart = $this->Internal_CleanupWSByIteratingPointer(@$tag_rule['after_tag'], $start, $this->stack);
        $this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_endtag'], $this->stack);
        $this->Internal_CleanupWSByEatingInput(@$tag_rule['after_endtag']);
        $content = $this->Internal_CollectText($this->stack, $newstart);
        array_splice($this->stack, $start);
        $this->Internal_ComputeCurrentClass();
        $this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_tag'], $this->stack);
        $tag_params['_endtag'] = $end_tag_params['_tag'];
        $tag_params['_hasend'] = true;
        $output = $this->DoTag(BBCODE_OUTPUT, $tag_name, @$tag_params['_default'], $tag_params, $content);
        $this->stack[] = Array(BBCODE_STACK_TOKEN => BBCODE_TEXT, BBCODE_STACK_TEXT => $output, BBCODE_STACK_TAG => false, BBCODE_STACK_CLASS => $this->current_class,);
    }
    function Internal_ParseStartTagToken() {
        $tag_params = $this->lexer->tag;
        $tag_name = @$tag_params['_name'];
        if (!isset($this->tag_rules[$tag_name])) {
            $this->stack[] = Array(BBCODE_STACK_TOKEN => BBCODE_TEXT, BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), BBCODE_STACK_TAG => false, BBCODE_STACK_CLASS => $this->current_class,);
            return;
        }
        $tag_rule = $this->tag_rules[$tag_name];
        $allow_in = is_array($tag_rule['allow_in']) ? $tag_rule['allow_in'] : Array($this->root_class);
        if (!in_array($this->current_class, $allow_in)) {
            if (!$this->Internal_RewindToClass($allow_in)) {
                $this->stack[] = Array(BBCODE_STACK_TOKEN => BBCODE_TEXT, BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), BBCODE_STACK_TAG => false, BBCODE_STACK_CLASS => $this->current_class,);
                return;
            }
        }
        $end_tag = isset($tag_rule['end_tag']) ? $tag_rule['end_tag'] : BBCODE_REQUIRED;
        if ($end_tag == BBCODE_PROHIBIT) {
            $this->Internal_ProcessIsolatedTag($tag_name, $tag_params, $tag_rule);
            return;
        }
        if (!$this->DoTag(BBCODE_CHECK, $tag_name, @$tag_params['_default'], $tag_params, "")) {
            $this->stack[] = Array(BBCODE_STACK_TOKEN => BBCODE_TEXT, BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), BBCODE_STACK_TAG => false, BBCODE_STACK_CLASS => $this->current_class,);
            return;
        }
        if (@$tag_rule['content'] == BBCODE_VERBATIM) {
            $this->Internal_ProcessVerbatimTag($tag_name, $tag_params, $tag_rule);
            return;
        }
        if (isset($tag_rule['class'])) $newclass = $tag_rule['class'];
        else $newclass = $this->root_class;
        $this->stack[] = Array(BBCODE_STACK_TOKEN => $this->lexer->token, BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), BBCODE_STACK_TAG => $this->lexer->tag, BBCODE_STACK_CLASS => ($this->current_class = $newclass),);
        if (!isset($this->start_tags[$tag_name])) $this->start_tags[$tag_name] = Array(count($this->stack) - 1);
        else $this->start_tags[$tag_name][] = count($this->stack) - 1;
    }
    function Internal_ParseEndTagToken() {
        $tag_params = $this->lexer->tag;
        $tag_name = @$tag_params['_name'];
        $contents = $this->Internal_FinishTag($tag_name);
        if ($contents === false) {
            if (@$this->lost_start_tags[$tag_name] > 0) {
                $this->lost_start_tags[$tag_name]--;
            } else {
                $this->stack[] = Array(BBCODE_STACK_TOKEN => BBCODE_TEXT, BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), BBCODE_STACK_TAG => false, BBCODE_STACK_CLASS => $this->current_class,);
            }
            return;
        }
        $start_tag_node = array_pop($this->stack);
        $start_tag_params = $start_tag_node[BBCODE_STACK_TAG];
        $this->Internal_ComputeCurrentClass();
        $this->Internal_CleanupWSByPoppingStack(@$this->tag_rules[$tag_name]['before_tag'], $this->stack);
        $start_tag_params['_endtag'] = $tag_params['_tag'];
        $start_tag_params['_hasend'] = true;
        $output = $this->DoTag(BBCODE_OUTPUT, $tag_name, @$start_tag_params['_default'], $start_tag_params, $contents);
        $this->Internal_CleanupWSByEatingInput(@$this->tag_rules[$tag_name]['after_endtag']);
        $this->stack[] = Array(BBCODE_STACK_TOKEN => BBCODE_TEXT, BBCODE_STACK_TEXT => $output, BBCODE_STACK_TAG => false, BBCODE_STACK_CLASS => $this->current_class,);
    }

    /**
     * @param $string
     * @return array|mixed|string
     */
    function Parse($string) {
        $this->lexer = new BBCodeLexer($string, $this->tag_marker);
        $this->lexer->debug = $this->debug;
        $old_output_limit = $this->output_limit;
        if ($this->output_limit > 0) {
            if (strlen($string) < $this->output_limit) {
                $this->output_limit = 0;
            } else if ($this->limit_precision > 0) {
                $guess_length = $this->lexer->GuessTextLength();
                if ($guess_length < $this->output_limit * ($this->limit_precision + 1.0)) {
                    $this->output_limit = 0;
                } else {
                }
            }
        }
        $this->stack = Array();
        $this->start_tags = Array();
        $this->lost_start_tags = Array();
        $this->text_length = 0;
        $this->was_limited = false;
        if (strlen($this->pre_trim) > 0) $this->Internal_CleanupWSByEatingInput($this->pre_trim);
        $newline = $this->plain_mode ? "\n" : "<br />\n";
        while (true) {
            if (($token_type = $this->lexer->NextToken()) == BBCODE_EOI) {
                break;
            }
            switch ($token_type) {
                case BBCODE_TEXT:
                    if ((!isset($this->lexer->input[$this->lexer->ptr]) || (isset($this->lexer->input[$this->lexer->ptr]) && $this->lexer->input[$this->lexer->ptr] != '[/img]')) && $this->output_limit > 0 && $this->text_length + strlen($this->lexer->text) >= $this->output_limit) {
                        $text = $this->Internal_LimitText($this->lexer->text, $this->output_limit - $this->text_length);
                        if (strlen($text) > 0) {
                            $this->text_length+= strlen($text);
                            $this->stack[] = Array(BBCODE_STACK_TOKEN => BBCODE_TEXT, BBCODE_STACK_TEXT => $this->FixupOutput($text), BBCODE_STACK_TAG => false, BBCODE_STACK_CLASS => $this->current_class,);
                        }
                        $this->Internal_DoLimit();
                        break 2;
                    }
                    if (isset($this->lexer->input[$this->lexer->ptr]) && $this->lexer->input[$this->lexer->ptr] != '[/img]') {
                        $this->text_length+=1;
                    } else {
                    	$this->text_length+= strlen($this->lexer->text);
                    }
                    $this->stack[] = Array(BBCODE_STACK_TOKEN => BBCODE_TEXT, BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), BBCODE_STACK_TAG => false, BBCODE_STACK_CLASS => $this->current_class,);
                break;
                case BBCODE_WS:
                    if ($this->output_limit > 0 && $this->text_length + strlen($this->lexer->text) >= $this->output_limit) {
                        $this->Internal_DoLimit();
                        break 2;
                    }
                    $this->text_length+= strlen($this->lexer->text);
                    $this->stack[] = Array(BBCODE_STACK_TOKEN => BBCODE_WS, BBCODE_STACK_TEXT => $this->lexer->text, BBCODE_STACK_TAG => false, BBCODE_STACK_CLASS => $this->current_class,);
                break;
                case BBCODE_NL:
                    if ($this->ignore_newlines) {
                        if ($this->output_limit > 0 && $this->text_length + 1 >= $this->output_limit) {
                            $this->Internal_DoLimit();
                            break 2;
                        }
                        $this->text_length+= 1;
                        $this->stack[] = Array(BBCODE_STACK_TOKEN => BBCODE_WS, BBCODE_STACK_TEXT => "\n", BBCODE_STACK_TAG => false, BBCODE_STACK_CLASS => $this->current_class,);
                    } else {
                        $this->Internal_CleanupWSByPoppingStack("s", $this->stack);
                        if ($this->output_limit > 0 && $this->text_length + 1 >= $this->output_limit) {
                            $this->Internal_DoLimit();
                            break 2;
                        }
                        $this->text_length+= 1;
                        $this->stack[] = Array(BBCODE_STACK_TOKEN => BBCODE_NL, BBCODE_STACK_TEXT => $newline, BBCODE_STACK_TAG => false, BBCODE_STACK_CLASS => $this->current_class,);
                        $this->Internal_CleanupWSByEatingInput("s");
                    }
                break;
                case BBCODE_TAG:
                    $this->Internal_ParseStartTagToken();
                break;
                case BBCODE_ENDTAG:
                    $this->Internal_ParseEndTagToken();
                break;
                default:
                break;
            }
        }
        if (strlen($this->post_trim) > 0) $this->Internal_CleanupWSByPoppingStack($this->post_trim, $this->stack);
        $result = $this->Internal_GenerateOutput(0);
        $result = $this->Internal_CollectTextReverse($result, count($result) - 1);
        $this->output_limit = $old_output_limit;
        if ($this->plain_mode) {
            $result = preg_replace("/[\\x00-\\x09\\x0B-\\x20]+/", " ", $result);
            $result = preg_replace("/(?:[\\x20]*\\n){2,}[\\x20]*/", "\n\n", $result);
            $result = trim($result);
        }
        return $result;
    }

    /**
     * @param $text
     * @return string
     */
    function nbbchtmlspecialchars($text) {
        if (version_compare(PHP_VERSION, '5.2.3') >= 0) {
            $text = htmlspecialchars($text, $this->quote_style, $this->charset, false);
        } else {
            $text = htmlspecialchars($text, $this->quote_style, $this->charset);
        }
        return $text;
    }
}

Anon7 - 2022
AnonSec Team