Вход Регистрация
Файл: IPBMafia.ru_IPB_3.4.6_Final_Rus _Nulled/board/upload/admin/sources/classes/text/parser.php
Строк: 1679
<?php

/**
 * <pre>
 * Invision Power Services
 * IP.Board v3.4.6
 * HTML parsing core
 * Last Updated: $Date: 2012-06-08 09:28:02 +0100 (Fri, 08 Jun 2012) $
 * </pre>
 *
 * @author         $Author: mmecham $
 * @copyright    (c) 2001 - 2009 Invision Power Services, Inc.
 * @license        http://www.invisionpower.com/company/standards.php#license
 * @package        IP.Board
 * @link        http://www.invisionpower.com
 * @since        9th March 2005 11:03
 * @version        $Revision: 10894 $
 *
 * <code>
 * $html = $editor->process( $_POST['Post'] );
 *
 * require( IPS_ROOT_PATH . 'sources/classes/text/parser.php' );
 *
 * $parser = new classes_text_parser();
 * print $parser->HtmlToBBCode( '<strong>Moo!</strong>' );
 *
 * Prints:
 * [b]Moo[/b]
 * </code>
 *
 */

if ( ! defined'IN_IPB' ) )
{
    print 
"<h1>Incorrect access</h1>You cannot access this file directly. If you have recently upgraded, make sure you upgraded all the relevant files.";
    exit();
}

/**
 * There are three modes
 * html:         <strong>Moo</strong><br />[sharedmedia:1:string]
 * bbcode:         [b]moo![/b]n[sharedmedia:1:string]
 * display:     <strong>Moo</strong><img src='....' />
 * @author matt
 *
 */
class classes_text_parser
{
    
/**
     * Settings
     */
    
protected static $Perms = array( 'skipBadWords' => false'parseBBCode' => true'parseHtml' => false'parseEmoticons' => true'parseArea' => 'posts' );
    private   
$_errors;
    
    
/**
     * Used for acroynm replacement
     */
    
private $_currentAcronym null;
     
    
/**
     * Legacy method
     * @todo remove in 4.0
     */
    
public $error '';

    
/**
     * Force bbcode parser to kick in
     * @var    bool
     */
    
protected $forceBbcode    false;
    
    
/**
     * Force bbcode parser to kick in
     * @var    bool
     */
    
protected static $NoBBCodeAutoLinkify false;
    
    
/**
     * Do not process media tags while auto-linking.
     * @var bool
     */
    
protected static $NoMediaAutoLinkify false;
    
    
/**
     * Constructor
     *
     * @access    public
     * @return    @e void
     */
    
public function __construct()
    {
        
/* Make object */
        
$this->registry    =  ipsRegistry::instance();
        
$this->DB           =  $this->registry->DB();
        
$this->settings    =& $this->registry->fetchSettings();
        
$this->request     =& $this->registry->fetchRequest();
        
$this->cache       =  $this->registry->cache();
        
$this->caches      =& $this->registry->cache()->fetchCaches();
        
$this->lang        $this->registry->getClass('class_localization');
        
        
self::$Perms['memberData'] = ( is_arrayself::$Perms['memberData'] ) ) ? self::$Perms['memberData'] : ipsRegistry::member()->fetchMemberData();
    }

    
/**
     * Force bbcode mode (used for emails where bbcode isn't used but autolink parsing needs to be done)
     *
     * @param    bool
     * @return    null
     */
    
public function setForceBbcode$force=false )
    {
        
$this->forceBbcode    $force;
    }
    
    
/**
     * Set multiple settings
     * @param array $settings
     */
    
public function set( array $settings )
    {
        foreach( 
$settings as $setting => $value )
        {
            switch( 
$setting )
            {
                case 
'parseBBCode':
                    
self::$Perms$setting ] = (bool) $value;
                break;
                case 
'parseHtml':
                    
self::$Perms$setting ] = (bool) $value;
                break;
                case 
'parseEmoticons':
                    
self::$Perms$setting ] = (bool) $value;
                break;
                case 
'memberData':
                    
self::$Perms$setting ] = $value;
                break;
                case 
'parseArea':
                    
self::$Perms$setting ] = $value;
                break;
            }
        }
    }
    
    
/**
     * Returns errors, yo.
     * @return array
     */
    
public function getErrors()
    {
        return 
$this->_errors;
    }
    
    
/**
     * Display the HTML to IPB
     *
     * Notes:
     * CODE: Need to convert _prettyXprint, _linenums _lang- into correct class names
     * @param    string  HTML
     * @return    string    Fully parsed HTML
     */
    
public function display$html )
    {
        
$classToLoad IPSLib::loadLibraryIPS_ROOT_PATH 'sources/classes/text/parser/bbcode.php''class_text_parser_bbcode' );
        
$bbcodeParser = new $classToLoad();
        
        
$classToLoad IPSLib::loadLibraryIPS_ROOT_PATH 'sources/classes/text/parser/html.php''class_text_parser_html' );
        
$htmlParser = new $classToLoad();
        
        if ( 
$this->isBBCode$html ) )
        {
            
$html $bbcodeParser->BBCodeToHtml$html );
        }

        
/* Remove disabled tags */
        
if ( ! self::$Perms['parseHtml'] )
        {
            
$html $this->removeDisabledTags$html );
        }

        
/* Parse display tags */
        
$html $bbcodeParser->toDisplay$html );
        
        
/* Finish off HTML display */
        
$html $htmlParser->toDisplay$html );
        
        
/* Emoticons */
        
if ( self::$Perms['parseEmoticons'] )
        {
            
$html $this->parseEmoticons$html );
        }
        
        
/* Badwords */
        
if ( ! self::$Perms['skipBadWords'] )
        {
            
$html $this->parseBadWords$html );
        }
    
        if ( 
self::$Perms['parseHtml'] )
        {
            if ( 
IPS_APP_COMPONENT != 'forums' && IPS_APP_COMPONENT != 'members' )
            {
                
/* Allow apps that don't save HTML state HTML editing */
                
$html str_replace"&#39;" "'"$html );
                
$html str_replace"&#33;" "!"$html );
                
$html str_replace"&#036;""$"$html );
                
$html str_replace"&#124;""|"$html );
                
$html str_replace"&amp;" "&"$html );
                
$html str_replace"&gt;"     ">"$html );
                
$html str_replace"&lt;"     "<"$html );
                
$html str_replace"&#60;" "<"$html );
                
$html str_replace"&#62;" ">"$html );
                
$html str_replace"&quot;"'"'$html );
            }
            else
            {
                
/* Fixes an issue with legacy posts */
                
$html str_replace'&quot;''"'$html );
                
$html str_replace'&lt;''<'$html );
                
$html str_replace'&gt;''>'$html );
            }
            
            
/* Legacy posts and object/embed */
            
preg_match_all'#(codebase|src|href|pluginspage|data|value)="<a href=["']([^"']+?)["']([^>]+?)?>(.+?)</a>#is'$html$urlMatches );
            
            
/* Finish up URLs and such */
            
for( $i $i count$urlMatches[0] ) ; $i++ )
            {
                
$raw  $urlMatches[0][ $i ];
                
$attr $urlMatches[1][ $i ];
                
$url  $urlMatches[2][ $i ];
                
$text $urlMatches[4][ $i ];
                
                
$html str_replace$raw$attr '="' $url '"'$html );
            }
            
            
$html preg_replace'#<br([^>]+?)?>(s+?)?<param#is'"<param"$html );
            
$html preg_replace'#<br([^>]+?)?>(s+?)?<embed#is'"<embed"$html );
            
$html preg_replace'#<br([^>]+?)?>(s+?)?</object>#is'"</object>"$html );
            
            
/* Ugh... */
            
$html $this->HtmlAllowedPreContents$html'pre', array( '<''>' ) );
        }
        
        
/* SEO stuffs */
        
$html $this->_seoAcronymExpansion$html );
        
        
/* Little secret codes */
        
$html str_ireplace"(c)" "&copy;"$html );
        
$html str_ireplace"(tm)""&#153;"$html );
        
$html str_ireplace"(r)" "&reg;" $html );
    
        return 
$html;
    }
    
    
/**
     * Removes disabled tags
     *
     * @param    string
     * @return    string
     */
    
public function removeDisabledTags$html )
    {
        
$disabledTagMap = array( 'b'     => array('b''strong'),
                                 
'i'     => array('em'),
                                 
's'     => array('strike'),
                                 
'sup'   => array('sup'),
                                 
'sub'   => array('sub'),
                                 
'code'  => array('pre'),
                                 
'quote' => array('blockquote'),
                                 
'url'   => array('a') );
        
        
$allowedCssProps = array( 'color''font-family''font-size''background-color''font-weight''font-style''text-align''margin''margin-left''display' );
        
$disabledTags    $this->getDisabledTags();
    
        
        if ( 
count$disabledTags ) )
        {
            
$workingProps array_combine$allowedCssProps$allowedCssProps );
            
            foreach( 
$disabledTags as $tag )
            {
                switch( 
$tag )
                {
                    case 
'font':
                        unset( 
$workingProps['font-family'] );
                    break;
                    case 
'color':
                        unset( 
$workingProps['color'] );
                    break;
                    case 
'background':
                        unset( 
$workingProps['background-color'] );
                    break;
                    case 
'size':
                        unset( 
$workingProps['font-size'] );
                    break;
                }
            }
        }
        else
        {
            return 
$html;
        }
        
        
/* Take care of css styles first */
        
preg_match_all'#<(span|p|div) ([^>]+?)?style=(['"])([^'"]+?)(['"])#i'$html$matchesPREG_SET_ORDER );

        foreach( 
$matches as $val )
        {
            
$all       $val[0];
            
$tag       $val[1];
            
$other     $val[2];
            
$open      $val[3];
            
$rawStyles trim$val[4] );
            
$close     $val[5];
            
            if ( 
count$workingProps ) )
            {
                
$styleArray explode';'$rawStyles );
                
$styleClean = array();
                
                foreach( 
$styleArray as $style )
                {
                    
$style trim$style );
                    
$tmp   explode':'$style );
                    
$rule  trim$tmp[0] );
                    
                    if ( 
in_array$rule$workingProps) )
                    {
                        
$styleClean[] = $style;
                    }
                    
                }
                
                if ( 
count$styleClean ) )
                {
                    
$html str_replace$all'<' $tag ' ' $other ' style=' $open implode';'$styleClean ) . $close$html );
                    
                }
                else
                {
                    
$html str_replace$all'<' $tag $other$html );
                }
                
            }
        }
        
        
$tagArray = array();
        
        
/* Now the rest of the tags */
        
foreach( $disabledTags as $i => $tag )
        {
            if ( isset( 
$disabledTagMap$tag ] ) )
            {
                
$tagArray array_merge$tagArray$disabledTagMap$tag ] );
            }
            else
            {
                
$tagArray[] = $tag;
            }
        }
        
        
/* Make sure longest tags go first */
        
rsort$tagArray );
        
        foreach( 
$tagArray as $tag )
        {
            if ( 
$tag == 'font' OR $tag == 'background' OR $tag == 'color' OR $tag == 'size' )
            {
                continue;
            }
            
            
/* else.. */
            
preg_match_all'#<(/)?' $tag '(s([^>]+?)?>|>)#i'$html$matchesPREG_SET_ORDER );

            foreach( 
$matches as $val )
            {
                
$all       $val[0];
                
$close     $val[1];
                
$content   $val[2];
                
                
$html str_replace$all''$html );
            }
        }
        
        return 
$html;
    }
    
    
/**
     * Takes content from the DB and processes before it gets to the editor
     *
     * @param string $html
     * @return    string
     */
    
public function htmlToEditor$html )
    {
        
$html $this->_processNoParseCodes$html1true );

        
/* Don't attempt to convert manually entered CODE and QUOTE as this can come from a preview where someone has entered
         * manual tags in the RTE and breaks because this below fires up the legacy parser which expects <br> and not PRE linebreaks, etc
         * And also they can retain the manual tags they entered.
         */
        
if ( ! $this->lang->isRtl() )
        {
            
/* RTL tries to move square brakcets around */
            
$html str_ireplace'[code' '<!--open:code-->' $html );
            
$html str_ireplace'[quote''<!--open:quote-->'$html );
            
$html str_ireplace'[url'  '<!--open:url-->'$html );
            
$html str_ireplace'[img'  '<!--open:img-->'$html );
        }
        
        
/* Editing an older post? */
        
if ( $this->isBBCode$html ) )
        {
            
self::$NoBBCodeAutoLinkify true;
            
            
$html $this->BBCodeToHtml$html );
            
            
self::$NoBBCodeAutoLinkify false;
        }
        
        
$html str_replace'<!--open:code-->' '[code' $html );
        
$html str_replace'<!--open:quote-->''[quote'$html );
        
$html str_replace'<!--open:url-->'  '[url'  $html );
        
$html str_replace'<!--open:img-->'  '[img'  $html );
        
        
/* Dollar signs confuse CKEditor */
        
$html str_replace'$''&#36;'$html );
        
        
/* We want to restore CODE boxes */
        
if ( preg_match'#<pres+?class=["']_prettyXprint#i', $html ) AND self::$Perms['parseHtml'] )
        
{
            
$html $this->preToCode$html );
        }
        
        
/* ARGH MY EYES ARGH MY EYES SOMEONE DID A BAD WORD */
        
$html $this->parseBadWords$html );
        
        
/* Make sure no parse tags are correct */
        
$html $this->_processNoParseCodes$html);
        
$html $this->_processNoParseCodes$html);
        
        return 
$html;
    }
    
    
/**
     * Takes content from the editor and makes it lovely and clean for saving
     *
     * @param string $html
     * @return    string
     */
    
public function editorToHtml$editor )
    {
        
$editor $this->emoticonImgtoCode$editor );
        
$editor $this->_stripEmptyLeadingAndTrailingParagraphTags$editor );
        
        
$editor $this->_processNoParseCodes$editor);
        
        
/* always make sure CODE goes first */
        
foreach( array( 'codebox''code''xml''sql''html' ) as $tagName )
        {
            
$editor $this->codeToPre$editor$tagName );
        }

        
/* Pre quote tags */
        
$editor    $this->blockquoteToBlockquote$editor );

        
/* MANUAL QUOTE TAGS */
        
$editor $this->quoteToBlockquote$editor );
        
        
/* Make sure no parse tags are correct */
        
$editor $this->_processNoParseCodes$editor);
        
//$editor = $this->_processNoParseCodes( $editor, 3 );
        
        
return $editor;
    }

    
/**
     *  Code to pre
     */
    
public function codeToPre$editor$tagName )
    {
        
$data    $this->getTagPositions$editor$tagName, array( '[' ']' ) );

        if ( 
is_array$data['open'] ) )
        {
            foreach( 
$data['openWithTag'] as $id => $val )
            {
                
$o $data['openWithTag'][$id];
                
$c $data['closeWithTag'][$id] - $o;
                
                if ( 
$c )
                {
                    continue;
                }
                
                
$slice substr$editor$o$c );
                
$openTag substr$editor$o$data['open'][$id] - $o );
                
$closeTag substr$editor$data['close'][$id], strlen$tagName ) + );
                
                
$_openTagLen strlen$openTag );
                
$_closeTagLen strlen$closeTag );
                
                list( 
$tag$rawAttr )  = explode'='$openTag );
                list( 
$lang$lineNum ) = explode':'str_replace( array( '['']' ), ''$rawAttr ) );
                
                
// Fix lang if using XML tag
                
if ( ! $lang && $tagName != 'code' )
                {
                    
$lang $tagName;
                }
                
                
// Need to bump up lengths of opening and closing
                
$_origLength strlen$slice );
                
                
$sliceContents substr$editor$data['open'][$id], $data['close'][$id] - $data['open'][$id] );
                
                
/* Extra conversion for BBCODE>HTML mode */
                
$replacement $sliceContents;
                
$replacement str_replace( array( "rn""r" ), "n"$replacement );
                
$replacement preg_replace'#(https|http|ftp)://#''1-~~-//'$replacement );
                
$replacement str_replace'[''&#91;'$replacement );
                
$replacement preg_replace"#<br([^>]+?)?>(n)?#i""n"$replacement );
                
$replacement trimstr_replace"</p>n""n"$replacement ) );
                
                
/* Stop (r) (tm) and (c) from switching out */
                
$replacement preg_replace'#((tm|r|c))#i''&#40;$1&#41;'$replacement );
                
                
$replacement IPSText::stripTags$replacement'pre' );
                
                
$slice str_replace$sliceContents$replacement$slice );
                
                
/* add in class attributes */
                
$classExtra .= '_linenums:' intvaltrim$lineNum ) );
                
                if ( 
$lang )
                {
                    
$classExtra .= ' _lang-' trimhtmlspecialchars$lang ) );
                }
        
                
/* Convert tags */
                
$_newOpenTag "<pre class="_prettyXprint " . $classExtra . "">";
                
$_newCloseTag "</pre>";
                
                
$slice str_replace$openTag$_newOpenTag$slice );
                
$slice str_replace$closeTag$_newCloseTag$slice );
                
                
$editor substr_replace$editor$slice$o$c );
                
                break;
            }
        }
        
        
/* Recursively parse quotes */
        
if ( count$data['open'] ) > AND count$data['close'] ) > )
        {
            
$editor    $this->codeToPre$editor$tagName );
        }
            
        
$editor preg_replace'#<p([^>]+?)?' '>((&nbsp;|s)+?)?</p>(s+?)?<pre#i''<pre'$editor );
        
$editor preg_replace'#</pre>(s+?)?<p([^>]+?)?' '>((&nbsp;|s)+?)?</p>#i''</pre>'$editor );
        
        return 
$editor;
    }

    
/**
     * Convert QUOTE to blockquote
     */
    
public function blockquoteToBlockquote$text )
    {
        
/* BLOCKQUOTE: Fetch paired opening and closing tags */
        
$data $this->getTagPositions$text'blockquote', array( '<' '>' ) );

        if ( 
is_array$data['open'] ) )
        {
            foreach( 
$data['open'] as $id => $val )
            {
                
$o $data['open'][ $id ];
                
$c $data['close'][ $id ] - $o;
        
                
$slice substr$text$o$c );
                
                
/* Need to bump up lengths of opening and closing */
                
$_origLength strlen$slice );
        
                
/* Wrap in a P tags */
                
$slice '<p>' $this->_stripParagraphWrap$this->blockquoteToBlockquote$slice ) ) . '</p>';

                
$_newLength  strlen$slice );
        
                
$editor substr_replace$text$slice$o$c );

                break;
            }
        }

        return 
$text;
    }

    
/**
     * Convert QUOTE to blockquote
     */
    
public function quoteToBlockquote$text$inner=false )
    {
        
/* MANUAL QUOTE TAGS */
        
$data    $this->getTagPositions$text'quote', array( '[' ']' ) );

        if ( 
is_array$data['open'] ) && count$data['open'] ) == count$data['close'] ) )
        {
            foreach( 
$data['openWithTag'] as $id => $val )
            {
                
$o $data['openWithTag'][ $id ];
                
$c $data['closeWithTag'][ $id ] - $o;

                if ( 
$o || $c )
                {
                    continue;
                }
                
                
$slice     substr$text$o$c );
                
$openTag   substr$text$o$data['open'][ $id ] - $o  );
                
$closeTag  substr$text$data['close'][ $id ], );

                
$sliceContents substr$text$data['open'][ $id ], $data['close'][ $id ] - $o );

                
$slice str_replace$sliceContents$this->_stripParagraphWrap$sliceContents ), $slice );

                
$options   $this->getTagAttributes$openTag );
                
                
# Need to bump up lengths of opening and closing
                
$_origLength strlen$slice );
        
                
$ops = array();

                
# Allow collapse
                
$options['collapsed'] = ( isset( $options['collapse'] ) ) ? $options['collapse'] : $options['collapsed'];
                
                if ( 
$options['name'] )
                {
                    
$ops[] = 'data-author="' $options['name'] . '"';
                }
                
                if ( 
$options['post'] )
                {
                    
$ops[] = 'data-cid="' $options['post'] . '"';
                }
                
                if ( 
$options['timestamp'] )
                {
                    
$ops[] = 'data-time="' $options['timestamp'] . '"';
                }
                
                if ( 
$options['date'] )
                {
                    
$ops[] = 'data-date="' $options['date'] . '"';
                }
                
                if ( 
$options['collapsed'] )
                {
                    
$ops[] = 'data-collapsed="' intval$options['collapsed'] ) . '"';
                }

                
$slice    substr_replace$slice'</blockquote>'strlen($slice) - 8);
                
$slice    substr_replace$slice"<blockquote class='ipsBlockquote' " implode' '$ops ) . '>'0strlen($openTag) );

                
$text substr_replace$text'<p>' $this->_stripParagraphWrap$slice ) . '</p>'$o$c );

                break;
            }
        }

        
/* Recursively parse quotes */
        
if ( count$data['open'] ) > && count$data['close'] ) > )
        {
            
$text    $this->quoteToBlockquote$texttrue );
        }

        if( 
$inner )
        {
            return 
$text;
        }

        
$text preg_replace'#<(div|p)([^>]+?)?>(?:s+?)?(<blockquote([^>]+?)>)(?:s+?)?</(div|p)>#''3<br />'$text );
        
$text preg_replace'#<(div|p)([^>]+?)?>(?:s+?)?(</blockquote>)(?:s+?)?</(div|p)>#''3'$text );
        
$text preg_replace'#<(div|p)([^>]+?)?>(?:s+?)?(</p></blockquote>)(?:s+?)?</(div|p)>#''3'$text );
        
        return 
$text;
    }
    
    
/**
     *  Pre to Code
     */
    
public function preToCode$editor )
    {
        
$tagName 'pre';
        
$data    $this->getTagPositions$editor$tagName, array( '<' '>' ) );
    
        if ( 
is_array$data['open'] ) )
        {
            foreach( 
$data['openWithTag'] as $id => $val )
            {
                
$o $data['openWithTag'][$id];
                
$c $data['closeWithTag'][$id] - $o;
    
                if ( 
$c )
                {
                    continue;
                }
    
                
$slice    substr$editor$o$c );
                
$openTag  substr$editor$o$data['open'][$id] - $o );
                
$closeTag substr$editor$data['close'][$id], strlen$tagName ) + );
                
$lineNum  0;
                
$lang     'auto';
                
                
$_openTagLen  strlen$openTag );
                
$_closeTagLen strlen$closeTag );
    
                
/* Line num */
                
preg_match'#_linenums:(d+?)#'$openTag$match );
                
                if ( 
$match[1] )
                {
                    
$lineNum intval$match[1] );
                }
                
                
/* Line num */
                
preg_match'#_lang-([a-z-_])#i'$openTag$match );
                
                if ( 
$match[1] )
                {
                    
$lang $match[1];
                }
    
                
// Need to bump up lengths of opening and closing
                
$_origLength strlen$slice );
    
                
$sliceContents substr$editor$data['open'][$id], $data['close'][$id] - $data['open'][$id] );
    
                
/* Extra conversion for BBCODE>HTML mode */
                
$replacement $sliceContents;
    
                
$replacement IPSText::htmlspecialchars$replacement );
    
                
$slice str_replace$sliceContents$replacement$slice );
    
                
/* Convert tags */
                
$_newOpenTag "[code=" $lang ':'$lineNum ']';
                
$_newCloseTag "[/code]";
    
                
$slice str_replace$openTag$_newOpenTag$slice );
                
$slice str_replace$closeTag$_newCloseTag$slice );
    
                
$_newOpenTagLen strlen$openTag );
                
$_newCloseTagLen strlen$closeTag );
                
$_newLength strlen$slice );
    
                
$editor substr_replace$editor$slice$o$c );
    
                break;
            }
        }
    
        
/* Recursively parse quotes */
        
if ( count$data['open'] ) > )
        {
            
$editor    $this->preToCode$editor );
        }
        
        return 
$editor;
    }
    
    
/**
     * Convert HTML to BBCode
     * @param    string    HTML
     * @param    string    BBCode
     */
    
public function HtmlToBBCode$text )
    {
        
$classToLoad IPSLib::loadLibraryIPS_ROOT_PATH 'sources/classes/text/parser/html.php''class_text_parser_html' );
        
$html = new $classToLoad();
        
        
$text $html->toBBCode$text );
        
        return 
$text;
    }
    
    
/**
     * Convert BBCode to HTML
     * @param   string $text
     * @return     string    $text
     */
    
public function BBCodeToHtml$text )
    {
        
$classToLoad IPSLib::loadLibraryIPS_ROOT_PATH 'sources/classes/text/parser/bbcode.php''class_text_parser_bbcode' );
        
$bbcode = new $classToLoad();
        
        if ( 
$this->isBBCode$text ) )
        {
            
$text $bbcode->toHtml$text );
        }
        
        return 
$text;
    }
    
    
/**
     * Does it need conversion?
     * @param    string
     * @return     boolean
     * @since    3.4
     */
    
public function isBBCode$string )
    {
        if ( 
$this->forceBbcode )
        {
            return 
true;
        }
        
        if ( 
strstr$string'[' ) )
        {
            if ( 
preg_match'#[((?:b|i|u|s|strike|font|size|color|background|sup|sub|list|*|url|img|center|left|right|indent|email|code|quote)(s|]|=))#i'$string$matches ) )
            {
                return 
true;
            }
        }
            
        return 
false;
    }
    
    
/**
     * Takes HTML (*not* display) and checks it for built in limits such as quote and IMG
     *
     * @param string $text
     */
    
public function testForParsingLimits$text$check=array('img''quote''emoticons''urls') )
    {
        
$quoteCount $this->getQuoteCount($text);
        
$imageCount $this->getImageCount($text);
        
$emoCount   $this->getEmoticonCount$this->parseEmoticons$text ) );

        
/* IMG CHECK */
        
if ( ( is_numeric$this->settings['max_images'] ) ) && ( $check == 'all' || in_array'all'$check ) || in_array'img'$check ) ) )
        {
            if ( 
$imageCount $this->settings['max_images'] )
            {
                
$this->_addParsingError'too_many_img' );
            }
        }
        
        
/* QUOTE CHECK */
        
if ( ( is_numeric$this->settings['max_quotes_per_post'] ) ) && ( $check == 'all' || in_array'all'$check ) || in_array'quote'$check ) ) )
        {
            if ( 
$quoteCount $this->settings['max_quotes_per_post'] )
            {
                
$this->_addParsingError'too_many_quotes' );
            }
        }
        
        
/* EMO CHECK */
        
if ( ( is_numeric$this->settings['max_emos'] ) ) && ( $check == 'all' || in_array'all'$check ) || in_array'emoticons'$check ) ) )
        {
            if ( 
$emoCount $this->settings['max_emos'] )
            {
                
$this->_addParsingError'too_many_emoticons' );
            }
        }
        
        
/* IMG EXT CHECK */
        
preg_match_all'#<img([^>]+?)?>#i'$text$matches );
        foreach( 
$matches[1] as $id => $match )
        {
            if ( 
stristr$match'src=' ) && ! stristr$match'class="bbc_emoticon"' ) )
            {
                
preg_match'#src=['"]([^'"]+?)['"]#i'$match$url );
                
                if ( 
$this->isAllowedImgUrl$url[1] ) !== true )
                {
                    
$this->_addParsingError'invalid_ext' );
                    break;
                }
                
                if ( 
$this->isAllowedUrl$url[1] ) !== true )
                {
                    
$this->_addParsingError'domain_not_allowed' );
                    break;
                }
            }
        }
        
        
/* A HREF CHECK */
        
if ( $check == 'all' || in_array'all'$check ) || in_array'urls'$check ) )
        {
            
preg_match_all'#<a([^>]+?)?>#i'$text$matches );
            foreach( 
$matches[1] as $id => $match )
            {
                if ( 
stristr$match'href=' ) )
                {
                    
preg_match'#href=['"]([^'"]+?)['"]#i'$match$url );
            
                    if ( 
$this->isAllowedUrl$url[1] ) !== true )
                    {
                        
$this->_addParsingError'domain_not_allowed' );
                        break;
                    }
                }
            }
        }
        
        return ( 
count$this->_errors ) ) ? false true;
    }
    
    
/**
     * Get number of quotes
     * @param    string
     */
    
public function getQuoteCount$text )
    {
        return 
substr_count$text'<blockquote' );
    }
    
    
    
/**
     * Get the number of images
     * @param string $text
     */
    
public function getImageCount$text )
    {
        
$count 0;
        
preg_match_all'#<img([^>]+?)?>#i'$text$matches );
        
        foreach( 
$matches[1] as $id => $match )
        {
            if ( ! 
stristr$match'class="bbc_emoticon"' ) )
            {
                
$count++;
            }
        }
        
        return 
$count;
    }
    
    
/**
     * Get the number of URLs
     * @param string $text
     */
    
public function getUrlCount$text )
    {
        
$count 0;
        
preg_match_all'#<a([^>]+?)?>#i'$text$matches );
    
        foreach( 
$matches[1] as $id => $match )
        {
            if ( 
stristr$match'href' ) )
            {
                
$count++;
            }
        }
    
        return 
$count;
    }
    
    
/**
     * Get the number of images
     * @param string $text
     * @param    boolean    $parseTest
     */
    
public function getEmoticonCount$text )
    {
        
$count 0;
        
$text str_replace"<#EMO_DIR#>"""$text );
        
        
preg_match_all'#<img([^>]+?)?>#i'$text$matches );
    
        foreach( 
$matches[1] as $id => $match )
        {
            if ( 
stristr$match'class="bbc_emoticon"' ) || stristr$match"class='bbc_emoticon'" ) )
            {
                
$count++;
            }
        }
        
        return 
$count;
    }
    
    
/**
     * Is an allowed URL type
     * @param string $url
     * @return boolean
     */
    
public function isAllowedImgUrl$url )
    {
        if ( 
$this->settings['img_ext'] )
        {
            
$path    = @parse_urlhtml_entity_decodetrim$url ) ), PHP_URL_PATH );
            
$pieces    explode'.'$path );
            
$ext    array_pop$pieces );
            
$ext    strtolower$ext );

            if ( ! 
in_array$extexplode','str_replace'.'''strtolower($this->settings['img_ext']) ) ) ) )
            {
                return 
false;
            }
        }

        return 
true;
    }
    
    
/**
     * Is allowed URL
     * @param string $url
     * @return boolean
     */
    
public function isAllowedUrl$url )
    {
        if ( 
$this->settings['ipb_use_url_filter'] )
        {
            
$list_type $this->settings['ipb_url_filter_option'] == "black" "blacklist" "whitelist";
                
            if( 
$this->settings['ipb_url_' $list_type ] )
            {
                
$list_values     = array();
                
$list_values     explode"n"str_replace"r"""$this->settings['ipb_url_' $list_type ] ) );
        
                if( 
$list_type == 'whitelist' )
                {
                    
$list_values[]    = "http://{$_SERVER['HTTP_HOST']}/*";
                }
        
                if ( 
count$list_values ) )
                {
                    
$good_url 0;
                        
                    foreach( 
$list_values as $my_url )
                    {
                        if( !
trim($my_url) )
                        {
                            continue;
                        }
        
                        
$my_url preg_quote$my_url'/' );
                        
$my_url str_replace'*'"(.*?)"$my_url );
        
                        if ( 
$list_type == "blacklist" )
                        {
                            if( 
preg_match'/' $my_url '/i'$url ) )
                            {
                                return 
false;
                            }
                        }
                        else
                        {
                            if ( 
preg_match'/' $my_url '/i'$url ) )
                            {
                                
$good_url 1;
                            }
                        }
                    }
                        
                    if ( ! 
$good_url AND $list_type == "whitelist" )
                    {
                        return 
false;
                    }
                }
            }
        }
        
        return 
true;
    }
    
    
/**
     * Replace bad words
     *
     * @param    string    Raw text
     * @return    string    Converted text
     */
    
public function parseBadWords$text='' )
    {
        
/* @link http://community.invisionpower.com/resources/bugs.html/_/ip-board/report-center-bypass-word-filter-r40719 */
        
if( self::$Perms['memberData']['member_group_id'] AND !self::$Perms['memberData']['g_id'] )
        {
            
self::$Perms['memberData']    = array_mergeself::$Perms['memberData'], $this->caches['group_cache'][ self::$Perms['memberData']['member_group_id'] ] );

            if( 
self::$Perms['memberData']['mgroup_others'] )
            {
                
self::$Perms['memberData']    = ips_MemberRegistry::setUpSecondaryGroupsself::$Perms['memberData'] );
            }
        }

        
/* Empty text or bypass? */
        
if ( $text == '' || self::$Perms['memberData']['g_bypass_badwords'] )
        {
            return 
$text;
        }
    
        
$badwords  $this->cache->getCache('badwords');
        
$temp_text $text;
        
$urls      = array();
    
        
/* Got any naughty words? */
        
if ( ! is_array$badwords ) OR ! count$badwords ) )
        {
            return 
$text;
        }
    
        
/* strip out URLs so replacements aren't made */
        
preg_match_all'#((http|https|news|ftp)://(?:[^<>)["s]+|[a-zA-Z0-9/._-!&#;,%+?:=]+))#is'$text$matches );
    
        foreach( 
$matches[0] as $m )
        {
            
$c count$urls );
            
$urls$c ] = $m;
                
            
$text str_replace$m'<!--url{' $c '}-->'$text );
        }

        
//-----------------------------------------
        // Convert back entities
        //-----------------------------------------
            
        
for( $i 65$i <= 90$i++ )
        {
            
$text str_replace"&#" $i ";"chr($i), $text );
        }
    
        for( 
$i 97$i <= 122$i++ )
        {
            
$text str_replace"&#" $i ";"chr($i), $text );
        }
    
        
//-----------------------------------------
        // Go all loopy
        //-----------------------------------------

        
foreach( $badwords as $r )
        {
            
$r['type']    = str_replace'&''&amp;'IPSText::UNhtmlspecialchars$r['type'] ) );

            if ( 
$this->parseType != 'topics' )
            {
                
$r['swop'] = strip_tags$r['swop'] );
            }
    
            
$replace    $r['swop'] ? $r['swop'] : '######';

            if ( 
$r['m_exact'] )
            {
                
$r['type']    = preg_quote$r['type'], "/" );
    
                
/* Link */
//                 if ( IPS_DOC_CHAR_SET == 'UTF-8' && IPSText::isUTF8( $text ) )
//                 {
//                     $text = preg_replace( '/(^|p{L}|s)' . $r['type'] . '(p{L}|!|?|.|,|$)/i', "\1{$replace}\2", $text );
//                 }
//                 else
//                 {
                // b does not work well because it matches word boundary, which is technically a w to W shift
                // @see http://stackoverflow.com/questions/6531724/how-exactly-do-regular-expression-word-boundaries-work-in-php
                // What we really want to look for is a non-word character on either side, so this works
                // Bad word filter for $!^& becomes $!^&amp;.  Submitted in a post that is <p>$!^&amp;</p> and </ is not a shift from non-word to word character
                    
if ( IPS_DOC_CHAR_SET == 'UTF-8' && IPSText::isUTF8$text ) )
                    {
                        
$text preg_replace'/(^|W)' $r['type'] . '(W|$)/iu'"\1" $replace "\2"$text );
                    }
                    else
                    {
                        
$text preg_replace'/(^|W)' $r['type'] . '(W|$)/i'"\1" $replace "\2"$text );
                    }
                    
                    
/* I'd retest that for a dollar! */
                    
if ( strstr$r['type'], '$' ) )
                    {
                        
$test preg_replace'#(\\)?$#''$'$r['type'] );
                        
                        if ( 
DOC_IPS_CHAR_SET == 'UTF-8' && IPSText::isUTF8$test ) )
                        {
                            
$text preg_replace'/(^|W)' preg_quote$test ) . '(W|$)/iu'"\1" $replace "\2"$text );
                        }
                        else
                        {
                            
$text preg_replace'/(^|W)' preg_quote$test ) . '(W|$)/i'"\1" $replace "\2"$text );
                        }
                    }
                    
//                }
            
}
            else
            {
                
//----------------------------
                // 'ass' in 'class' kills css
                //----------------------------
    
                
if( strtolower$r['type'] ) == 'ass' )
                {
                    
$text        preg_replace"/(?<!cl)" $r['type'] . "/i"$replace$text );
                }
                else
                {
                    
$text        str_ireplace$r['type'], $replace$text );
                }
            }
        }

        
/* replace urls */
        
if ( count$urls ) )
        {
            
preg_match_all'#<!--url{(d+?)}-->#is'$text$matches );
                
            for ( 
$i 0$i count($matches[0]); $i++ )
            {
                if ( isset( 
$matches[1][$i] ) )
                {
                    
$text str_replace$matches[0][$i], $urls$matches[1][$i] ], $text );
                }
            }
        }

        return 
$text $text $temp_text;
    }
    
    
/**
     * Parse emoticons in text
     *
     * @param string $txt
     * @return string $txt
     */
    
public function parseEmoticons$txt )
    {
        
/* Sort them in length order first */
        
$this->_sortSmilies();
        
        
$_codeBlocks = array();
        
$_c          0;
        
        
/* Now parse them! */
        
if ( self::$Perms['parseEmoticons'] && ! $this->parse_html )
        {
            
/* Make CODE tags safe... */
            
while( preg_match'/(<pre(.+?(?=</pre>))</pre>)/s'$txt$matches ) )
            {
                
$find    $matches[0];
                
$replace '<!--Cj' $_c 'j-->';
                
                
$_codeBlocks$_c ] = $find;
                
                
$txt str_replace$find$replace$txt );
                
                
$_c++;
            }
            
            
/* Make CODE tags safe... */
            
while( preg_match'/([code(.+?(?=[/code]))[/code])/s'$txt$matches ) )
            {
                
$find    $matches[0];
                
$replace '<!--Cj' $_c 'j-->';
            
                
$_codeBlocks$_c ] = $find;
            
                
$txt str_replace$find$replace$txt );
            
                
$_c++;
            }
        
            
$codes_seen = array();
            
            if ( 
count$this->_sortedSmilies ) > )
            {
                foreach( 
$this->_sortedSmilies as $row )
                {
                    if ( 
is_array$this->registry->output->skin ) and $this->registry->output->skin['set_emo_dir'] and $row['emo_set'] != $this->registry->output->skin['set_emo_dir'] )
                    {
                        continue;
                    }
                    
                    
$code IPSText::UNhtmlspecialchars$row['typed'] );
                    
                    if ( 
in_array$code$codes_seen ) )
                    {
                        continue;
                    }
                    
                    
$codes_seen[] = $code;
                    
                    
// -----------------------------------------
                    // Now, check for the html safe versions
                    // -----------------------------------------
                    
                    
$_emoCode str_replace'<''&lt;'str_replace'>''&gt;'$code ) );
                    
$_emoImage $row['image'];
                    
$emoPosition 0;
                    
                    
/* Cheap check */
                    
if ( ! stristr$txt$_emoCode ) )
                    {
                        continue;
                    }
                    
                    
// -----------------------------------------
                    // These are chars that can't surround the emo
                    // -----------------------------------------
                    
                    
$invalidWrappers "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'"/";
                    
                    // -----------------------------------------
                    // Have any more chars to look at?
                    // -----------------------------------------
                    
                    while ( ( 
$position = stripos( $txt$_emoCode$emoPosition ) ) !== false )
                    {
                        
$lastOpenTagPosition = strrpos( substr( $txt, 0, $position ), '[' );
                        
$lastCloseTagPosition = strrpos( substr( $txt, 0, $position ), ']' );
                        
                        // -----------------------------------------
                        // Are we at the start of the string, or
                        // is the preceeding char not an invalid wrapper?
                        // -----------------------------------------
                        
                        if ( ( 
$position === 0 or stripos( $invalidWrappers, substr( $txt$position - 1, 1 ) ) === false )
    
                        //-----------------------------------------
                        // Are we inside a [tag]
                        //-----------------------------------------
        
                        AND ( ( 
$lastOpenTagPosition === FALSE || $lastCloseTagPosition === FALSE ) or ( $lastCloseTagPosition !== FALSE and $lastCloseTagPosition > $lastOpenTagPosition ) )
        
                        //-----------------------------------------
                        // Are we at the end of the string or is the
                        // next char not an invalid wrapper?
                        //-----------------------------------------
        
                        AND ( strlen( 
$txt ) == ( $position + strlen( $_emoCode ) ) or stripos( $invalidWrappers, substr( $txt, ( $position + strlen( $_emoCode ) ), 1 ) ) === false ) )
                        {
                            // -----------------------------------------
                            // Replace the emoticon and increment position
                            // counter
                            // -----------------------------------------
                            
                            
$replace = $this->_retrieveSmiley$_emoCode$_emoImage );
                            
$txt = substr_replace( $txt$replace$position, strlen( $_emoCode ) );
                            
                            
$position += strlen( $replace );
                        }
                        
                        
$emoPosition = $position + 1;
                        
                        if ( 
$emoPosition > strlen( $txt ) )
                        {
                            break;
                        }
                    }
                }
            }
            
            /* Put alt tags in */
            if ( is_array( 
$this->emoticon_alts ) && count( $this->emoticon_alts ) )
            {
                foreach( 
$this->emoticon_alts as $r )
                {
                    
$txt = str_replace( $r[0]$r[1]$txt );
                }
            }
            
            /* Convert code tags back... */
            while( preg_match( '/<!--Cj(d+?)j-->/', 
$txt$matches ) )
            {
                
$find    = $matches[0];
                
$replace = $_codeBlocks[ $matches[1] ];
                                
                
$txt = str_replace( $find$replace$txt );
            }
        }
    
        return 
$txt;
    }
    
    /**
     * Remove quotes
     * @param    string 
$txt
     * @param    bool    If true, only quotes created by us will be stripped
     */
    public function stripQuotes( 
$txt$onlyStripIpsQuotes=true )
    {
        if ( stristr( 
$txt, '[quote' ) )
        {
            
$txt = $this->stripBbcode( 'quote', $txt );
        }
        
        if ( stristr( 
$txt, '<blockquote' ) )
        {
            /* PRE: Fetch paired opening and closing tags */
            
$data = $this->getTagPositions$txt, 'blockquote', array( '<' , '>' ) );
            
            if ( is_array( 
$data['openWithTag'] ) )
            {
                foreach( 
$data['openWithTag'] as $id => $val )
                {
                    if ( 
$onlyStripIpsQuotes )
                    {
                        
$tag = substr( $txt$data['openWithTag'][ $id ], ( $data['open'][ $id ] - $data['openWithTag'][ $id ] ) );
                        if ( strpos( 
$tag, 'ipsBlockquote' ) === false )
                        {
                            continue;
                        }
                    }
                
                    
$o = $data['openWithTag'][ $id ];
                    
$c = $data['closeWithTag'][ $id ] - $o;
                        
                    
$slice = substr( $txt$o$c );
                        
                    /* Need to bump up lengths of opening and closing */
                    
$_origLength = strlen( $slice );
                        
                    /* Remove */
                    
$slice = '';
            
                    
$_newLength  = strlen( $slice );
                        
                    
$txt = substr_replace( $txt$slice$o$c );
                        
                    /* Bump! */
                    if ( 
$_newLength != $_origLength )
                    {
                        foreach( 
$data['openWithTag'] as $_id => $_val )
                        {
                            
$_o = $data['openWithTag'][ $_id ];
                                
                            if ( 
$_o > $o )
                            {
                                
$data['openWithTag'][ $_id ]  += ( $_newLength - $_origLength );
                                
$data['closeWithTag'][ $_id ] += ( $_newLength - $_origLength );
                            }
                        }
                    }
                }
            }
        }
        
        return 
$txt;
    }
    
    /**
     * Removes bbcode tag + contents within the tag
     *
     * @access public
     * @param
     *            string        Tag to strip
     * @param
     *            string        Raw text
     * @return string text
     */
    public function stripBbcode( 
$tag$txt )
    {
        // -----------------------------------------
        // Protect against endless loops
        // -----------------------------------------
        static 
$iteration = array();
        
        if ( isset( 
$iteration[$tag] ) and $iteration[$tag] > $this->settings['max_bbcodes_per_post'] )
        {
            return 
$txt;
        }
        
        
$iteration[$tag] = isset( $iteration[$tag] ) ? $iteration[$tag] ++ : 1;
        
        // Got Quotes (tm)? or any tag really
        if ( stripos( 
$txt, '[' . $tag ) !== false )
        {
            // -----------------------------------------
            // First grab start and end positions
            // -----------------------------------------
            
            
$start_position = stripos( $txt, '[' . $tag );
            
$end_position = stripos( $txt, '[/' . $tag . ']', $start_position );
            
            // -----------------------------------------
            // If no end position or start position,
            // we have a mismatched bbcode...return
            // -----------------------------------------
            
            if ( 
$start_position === false or $end_position === false )
            {
                return 
$txt;
            }
            
            // -----------------------------------------
            // Then extract the content inside the bbcode
            // -----------------------------------------
            
            
$inner_content = substr( $txt, stripos( $txt, ']', $start_position ) + 1, $end_position - ( stripos( $txt, ']', $start_position ) + 1 ) );
            
            // -----------------------------------------
            // Is this bbcode nested in the inner content
            // -----------------------------------------
            
            
$extra_closers = substr_count( $inner_content, '[' . $tag );
            
            // -----------------------------------------
            // If so we need to move to the last ending tag
            // -----------------------------------------
            
            if ( 
$extra_closers > 0 )
            {
                for( 
$done = 0 ; $done < $extra_closers ; $done ++ )
                {
                    
$end_position = stripos( $txt, '[/' . $tag . ']', $end_position + 1 );
                }
            }
            
            // -----------------------------------------
            // Get rid of the bbcode opening + content + closing
            // -----------------------------------------
            
            
$txt = substr_replace( $txt, '', $start_position$end_position - $start_position + strlen( '[/' . $tag . ']' ) );
            
            // -----------------------------------------
            // And parse recursively
            // -----------------------------------------
            
            return 
$this->stripBbcode$tag, trim( $txt ) );
        }
        else
        {
            return 
$txt;
        }
    }
    
    /**
     * Remove ALL tags
     *
     * @access public
     * @param
     *            string        Raw text
     * @param
     *            boolean        Whether or not to run through pre-edit-parse first
     * @return string text
     */
    public function stripAllTags( 
$txt )
    {
        
$txt = $this->stripBbcode( 'quote', $txt );
        
        foreach( 
$this->cache->getCache( 'bbcode' ) as $bbcode )
        {
            
$txt = preg_replace( "#[{$bbcode['bbcode_tag']}](.+?)[/{$bbcode['bbcode_tag']}]#is", "\1 ", $txt );
            
$txt preg_replace"#[{$bbcode['bbcode_tag']}=([^]]+?)](.+?)[/{$bbcode['bbcode_tag']}]#is""\2 "$txt );
            
$txt str_ireplace"[{$bbcode['bbcode_tag']}]"''$txt );
            
$txt str_ireplace"[/{$bbcode['bbcode_tag']}]"''$txt );
            
            
// -----------------------------------------
            // Strip single bbcodes properly
            // -----------------------------------------
            
            
if ( $bbcode['bbcode_single_tag'] )
            {
                
$regex $bbcode['bbcode_single_tag'];
                
                
// -----------------------------------------
                // If this has option, adjust regex
                // -----------------------------------------
                
                
if ( $bbcode['bbcode_useoption'] )
                {
                    
$regex .= '=([^]]+?)';
                }
                
                
$txt preg_replace"#[{$regex}]#is"" "$txt );
            }
        }
        
        
// $txt = preg_replace( "#[(.+?)]#is", " ", $txt );
        
$txt preg_replace'#[([^]]+?)=([^]]+?)]#is'" "$txt );
        
$txt preg_replace'#[/([^]]+?)]#is'" "$txt );
        
$txt preg_replace'#[attachment=(.+?)]#is'" "$txt );
        
$txt str_replace'[*]'''$txt );
        
        return 
$txt;
    }
    
    
/**
     * Remove raw smilies
     *
     * @access public
     * @param
     *            string        Raw text
     * @return string with smiley codes removed
     */
    
public function stripEmoticons$txt )
    {
        
$codes_seen = array();
        
        if ( 
count$this->cache->getCache'emoticons' ) ) > )
        {
            foreach( 
$this->cache->getCache'emoticons' ) as $row )
            {
                if ( 
is_array$this->registry->output->skin ) and $this->registry->output->skin['set_emo_dir'] and $row['emo_set'] != $this->registry->output->skin['set_emo_dir'] )
                {
                    continue;
                }
                
                
$code $row['typed'];
                
                if ( 
in_array$code$codes_seen ) )
                {
                    continue;
                }
                
                
$codes_seen[] = $code;
                
                
// -----------------------------------------
                // Now, check for the html safe versions
                // -----------------------------------------
                
                
$_emoCode str_replace'<''&lt;'str_replace'>''&gt;'$code ) );
                
$_emoImage $row['image'];
                
$emoPosition 0;
                
$invalidWrappers "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
                
                
/* Cheap check */
                
if ( ! stristr$txt$_emoCode ) )
                {
                    continue;
                }
                
                while ( ( 
$position strpos$txt$_emoCode$emoPosition ) ) !== false )
                {
                    if ( 
strpos$invalidWrapperssubstr$txt$position 1) ) === false and strpos$invalidWrapperssubstr$txt, ( $position strlen$_emoCode ) ), ) ) === false )
                    {
                        
$txt substr_replace$txt''$positionstrlen$_emoCode ) );
                        
                        
$position += strlen$_emoCode );
                    }
                    
                    
$emoPosition $position 1;
                    
                    if ( 
$emoPosition strlen$txt ) )
                    {
                        break;
                    }
                }
            }
        }
        
        return 
$txt;
    }
    
    
/**
     * Strip shared media
     *
     * @param     string            Raw posted text
     * @return    string            Raw text with no shared media
     */
    
public function stripSharedMedia$txt )
    {
        
$txt    preg_replace'#[sharedmedia=([^]]+?)]#is'" "$txt );
    
        return 
$txt;
    }
    
    
/**
     * Strip images
     *
     * @param    string
     * @return    string
     */
    
public function stripImages$txt )
    {
        
$txt preg_replace'#<img([^>]+?)>#i'''$txt );
        
        return 
$txt;
    }
    
    
/**
     * Convert IMG codes into text smilies
     *
     * @param text $txt
     * @return text $txt
     */
    
public function emoticonImgtoCode$txt )
    {
        if ( 
count$this->cache->getCache'emoticons' ) ) > )
        {
            
$emoDir IPSText::getEmoticonDirectory();

            
$txt    str_replace'<#EMO_DIR#>'$this->registry->output->skin['set_emo_dir'], $txt );
            
            foreach( 
$this->cache->getCache'emoticons' ) as $row )
            {
                if ( 
$row['emo_set'] != $emoDir )
                {
                    continue;
                }

                
/* This can shave a lot of loading time off of a site if there is a lot of large text being parsed,
                    especially if there are a lot of emoticons too */
                
if( strpos$txt$row['image'] ) === false )
                {
                    continue;
                }
                
                
/* BBCode */
                
$txt preg_replace'#(s)?[img]' preg_quote$this->settings['public_cdn_url'] . 'style_emoticons/' $this->registry->output->skin['set_emo_dir'] . '/' $row['image'], '#' ) . '[/img]#'' ' $row['typed'], $txt );
                
                
/* HTML */
                
$txt preg_replace'#(s)?<img([^>]+?)src=(?:['"])' . preg_quote( $this->settings['public_cdn_url'] . 'style_emoticons/' . $this->registry->output->skin['set_emo_dir'] . '/' . $row['image'], '#' ) . '(?:['"])(?:[^>]+?)?>#', ' ' . $row['typed'], $txt );
            }
        }
        
        return $txt;
    }
    
    /**
     * Returns an array of tags this user is not allowed
     * to use.
     * @return array
     */
    public function getDisabledTags()
    {
        $disabled    = array();
        $coreTags = array( "font", "size", "img", "url", "code", "quote", "color" );
        $bbcodeCache = $this->cache->getCache('bbcode');

        foreach( $coreTags as $tag )
        {
            if ( !in_array( $tag, array_keys( $bbcodeCache ) ) )
            {
                $disabled[] = $tag;
            }
        }
        
        foreach( $bbcodeCache as $bbcode )
        {
            /* Allowed this BBCode? */
            if ( $bbcode['bbcode_sections'] != 'all' || $bbcode['bbcode_groups'] != 'all' )
            {
                $sections    = explode( ',', $bbcode['bbcode_sections'] );
                $groups     = array_diff( explode( ',', $bbcode['bbcode_groups'] ), array( '' ) );
                $mygroups   = array( self::$Perms['memberData']['member_group_id'] );
                $group_pass       = false;
                $section_pass     = false;
                
                if ( self::$Perms['memberData']['mgroup_others'] )
                {
                    $mygroups = array_diff( array_merge( $mygroups, explode( ',', IPSText::cleanPermString( self::$Perms['memberData']['mgroup_others'] ) ) ), array( '' ) );
                }
                
                /* Perms */
                if ( $bbcode['bbcode_groups'] != 'all' )
                {
                    foreach( $groups as $g_id )
                    {
                        if ( in_array( $g_id, $mygroups ) )
                        {
                            $group_pass = true;
                        }
                    }
                }
                else
                {
                    $group_pass = true;
                }
                
                /* Sections */
                if ( $bbcode['bbcode_sections'] != 'all' )
                {
                    foreach( $sections as $section )
                    {
                        if ( $section == self::$Perms['parseArea'] )
                        {
                            $section_pass = true;
                        }
                    }
                }
                else
                {
                    $section_pass = true;
                }
                
                if ( $section_pass == false || $group_pass == false )
                {
                    $disabled[] = $bbcode['bbcode_tag'];
                }
            }
        }
        
        return $disabled;
    }
    
    /**
     * Return paired opening and closing positions.
     * @param string $txt
     * @param string $tag
     * @return array
     */
    public function getTagPositions( $txt, $tag, $brackets=array('[',']') )
    {
        $close_tag = $brackets[0] . '/' . $tag . $brackets[1];
        $open_tag  = $brackets[0] . $tag;
        $map      = array();
        $iteration = 0;
    
        /* Pick through bit of code */
        while( ( $curPos = stripos( $txt, $open_tag, $curPos ) ) !== false )
        {
            if ( $iteration > 1000 )
            {
                break;
            }
            
            /* Make sure the next character is either ] = or a space */
            $nextChar = substr( $txt, $curPos + strlen( $tag ) + 1, 1 );
                
            if ( $nextChar != $brackets[1] && $nextChar != '=' && $nextChar != ' ' )
            {
                if ( $curPos > strlen($txt) )
                {
                    $curPos    = 0;
                    break;
                }

                $curPos++;
                continue;
            }
            
            $map['openWithTag'][ $iteration ] = $curPos;
            $map['open'][ $iteration ]        = $curPos + strlen( $open_tag );
                
            $new_pos = stripos( $txt, $brackets[1], $curPos ) ? stripos( $txt, $brackets[1], $curPos ) : $curPos + 1;
                
            /* Got an option, grab that */
            $_option = substr( $txt, $curPos + strlen($open_tag), (stripos( $txt, $brackets[1], $curPos ) - ($curPos + strlen($open_tag) )) );
            
            $map['open'][ $iteration ] += intval( strlen( $_option ) ) + 1;
            
            /* Got a closing tag? */
            $closingTagPos = stripos( $txt, $close_tag, $new_pos );
                
            if ( $closingTagPos !== false )
            {
                $map['close'][ $iteration ]        = $closingTagPos;
                $map['closeWithTag'][ $iteration ] = $closingTagPos + strlen( $close_tag );
                
                /* What content do we believe we have between the opening and closing tags? */
                $_content  = substr( $txt, ($curPos + strlen( $open_tag )  + strlen($_option) + 1), ($closingTagPos - ($curPos + strlen($open_tag) + strlen($_option) + 1)) );

                /* Did we have an opening tag in that mess? */
                if ( $_content && stristr( $_content, $open_tag ) )
                {
                    /* How many opening tags did we find...probably just 1 */
                    $count = substr_count( strtolower( $_content ), strtolower( $open_tag) );

                    /* Found N opening tags in portion of text */
                    if ( $count > 0 )
                    {
                        /* So now find Nth closing tag */
                        $_nPos = $closingTagPos + strlen( $close_tag );

                        /* While we have opening tags to inspect... */
                        while( $count > 0 )
                        {
                            $_closePos = stripos( $txt, $close_tag, $_nPos );
                                
                            if ( $_closePos !== false )
                            {
                                $map['close'][ $iteration ]        = $_closePos;
                                $map['closeWithTag'][ $iteration ] = $_closePos + strlen( $close_tag );

                                $_content    = substr( $txt, ($curPos + strlen( $open_tag )  + strlen($_option) + 1), ($_closePos - ($curPos + strlen($open_tag) + strlen($_option) + 1)) );
                                $count        = substr_count( strtolower( $_content ), strtolower( $open_tag ) );
                                $ccount        = substr_count( strtolower( $_content ), strtolower( $close_tag ) );

                                if( $count == $ccount )
                                {
                                    $count    = 0;
                                }

                                $_nPos = $_closePos + strlen( $close_tag );
    
                                if ( $_nPos >= strlen( $txt ) )
                                {
                                    $count == 0;
                                }
                            }
                            else
                            {
                                $count    = 0;
                            }
                        }
                    }
                }
            }
                
            $iteration++;
                
            $curPos = $closingTagPos ? $closingTagPos : $curPos + 1;
    
            if ( $curPos > strlen($txt) )
            {
                $curPos    = 0;
                break;
            }
        }
    
        return $map;
    }
    
    /**
     * Build a quote tag
     * @param string $content
     * @param string $author
     * @param string $date
     * @param int $pid
     */
    public function buildQuoteTag( $content, $author='', $date='', $collapsed=0, $pid=0 )
    {
        $ops = array();
        
        if ( $author )
        {
            $ops[] = 'data-author="' . $author . '"';
        }
        
        if ( $pid )
        {
            $ops[] = 'data-cid="' . $pid . '"';
        }
        
        if ( $date )
        {
            if ( strlen( $date ) == 10 && intval( $date ) == $date )
            {
                $ops[] = 'data-time="' . $date . '"';
            }
            else
            {
                $ops[] = 'data-date="' . $date . '"';
            }
        }
        
        if ( $collapsed )
        {
            $ops[] = 'data-collapsed="' . $collapsed . '"';
        }
        
        /* Parse out attachments and make into links */
        preg_match_all( '#[attachment=(.+?):(.+?)]#', $content, $_matches );
    
        if( is_array( $_matches[1] ) && count( $_matches[1] ) )
        {
            foreach( $_matches[1] as $idx => $attach_id )
            {
                $content = str_replace( "[attachment={$attach_id}:{$_matches[2][$idx]}]", $this->registry->getClass('output')->getReplacement('post_attach_link') . " <a href='{$this->settings['board_url']}/index.php?app=core&amp;module=attach&amp;section=attach&amp;attach_rel_module=post&amp;attach_id={$attach_id}' target='_blank'>{$_matches[2][$idx]}</a>", $content );
            }
        }
        
        /* Convert if we need to */
        if ( $this->isBBCode( $content ) )
        {
            $content = $this->BBCodeToHtml( $content );
        }
        
        /* ARGH MY EYES ARGH MY EYES SOMEONE DID A BAD WORD */
        $content = $this->parseBadWords( $content );

        /* We need the wrapping <div> here - _stripParagraphWrap removes the first and last <p> tags if they exist, but that means content like this remains unchanged:
            <p>something</p>
            <br />
            <p>something else</p>
            and there's no wrapper container, which is necessary in the editor for our javascript.  This fixes these reports:
            @link http://community.invisionpower.com/resources/bugs.html/_/ip-board/cannot-go-down-using-keyboard-arrow-in-some-cases-r41735
            @link http://community.invisionpower.com/resources/bugs.html/_/ip-board/cite-in-firefox-problematic-r41737
            @link http://community.invisionpower.com/resources/bugs.html/_/ip-board/quote-inappropriately-split-r41736
            @link http://community.invisionpower.com/resources/bugs.html/_/ip-board/two-lines-sometimes-still-removed-r41738
            @link http://community.invisionpower.com/resources/bugs.html/_/ip-board/cant-make-new-line-in-editor-r41722 */
        return "<p>&nbsp;</p><blockquote class='ipsBlockquote'" . implode( ' ', $ops ) . '><div><p>' . $this->_stripParagraphWrap( $content ) . '</p></div></blockquote><p>&nbsp;</p>';
    }
    
    /**
     * Parses the bbcode to be shown in the polls.
     * Parses img and url, if enabled
     *
     * @param     string            Raw input text to parse
     * @return    string            Parsed text ready to be displayed
     */
    public function parsePollTags( $text )
    {
        if ( stristr( $text, '[img' ) || stristr( $text, '[url' ) || stristr( $text, '[sharedmedia' ) )
        {
            $classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/text/parser/bbcode.php', 'class_text_parser_bbcode' );
            $bbcode = new $classToLoad();
            
            $text = $this->display( $bbcode->_parseBBCode( $text, 'display', array( 'img', 'url', 'sharedmedia' ) ) );
        }
        
        return $text;
    }
    
    /**
     * To fix legacy issues, all HTML entities are parsed into HTML tags
     * So this function restores code boxes
     * @param     string     $html
     * @param    string    $tag
     * @param    array    $brackets
     */
    public function HtmlAllowedPreContents( $html, $tag, $brackets )
    {
        /* PRE: Fetch paired opening and closing tags */
        $data = $this->getTagPositions( $html, $tag, $brackets );
        
        if ( is_array( $data['open'] ) )
        {
            $count = count( $data['open'] );
            
            foreach( range( 0, $count ) as $id )
            {
                $o = $data['open'][ $id ] ;
                $c = $data['close'][ $id ] - $o;
                    
                $slice = substr( $html, $o, $c );
                
                /* Need to bump up lengths of opening and closing */
                $_origLength = strlen( $slice );
                
                /* Fix up <p>[code]</p>....<p>[/code]</p> */
                $slice = preg_replace( '#^</p>#', '', $slice );
                $slice = trim( preg_replace( '#<p>$#', '', trim( $slice ) ) );
                
                /* Extra conversion for BBCODE>HTML mode */
                $slice = str_replace( "<", "&lt;", $slice );
                $slice = str_replace( ">", "&gt;", $slice );
    
                $_newLength  = strlen( $slice );
                    
                $html = substr_replace( $html, $slice, $o, $c );
                    
                /* Bump! */
                if ( $_newLength != $_origLength )
                {
                    foreach( $data['open'] as $_id => $_val )
                    {
                        $_o = $data['open'][ $_id ];
                            
                        if ( $_o > $o )
                        {
                            $data['open'][ $_id ]  += ( $_newLength - $_origLength );
                            $data['close'][ $_id ] += ( $_newLength - $_origLength );
                        }
                    }
                }
            }
        }
        
        return $html;
    }
    
    /**
     * Get attributes from a tag (lazy)
     * @param string $tag
     */
    public function getTagAttributes( $tag )
    {
        $processedTag = str_replace( array( '&quot;' ), '"', $tag );
        $processedTag = str_replace( array( '&#039;', '&#39;' ), ''', $processedTag );
        
        $processedTag = str_replace( '&nbsp;', ' ', $processedTag );
        $attributes   = array();
        
        /* http://community.invisionpower.com/resources/bugs.html/_/ip-board/apostrophe-in-rte-in-quote-name-is-cut-off-r41322 */
        preg_match_all( '#(S+?)=(?:['"])(.+?)(?:['"])(s|>|]$)#', $processedTag, $matches, PREG_SET_ORDER );
    
        foreach( $matches as $val )
        {
            $attributes[ trim( $val[1] ) ] = $val[2];
        }
        
        return $attributes;
    }
    
    /**
     * Expand the acronyms for SEO
     * @param string $txt
     */
    protected function _seoAcronymExpansion( $txt )
    {
        if ( $txt == '' )
        {
            return $txt;
        }

        $acronyms = $this->cache->getCache('ipseo_acronyms');

        if( !is_array($acronyms) OR !count($acronyms) )
        {
            return $txt;
        }

        $temp_text = $txt;
        $urls      = array();
        $tags      = array();
        $txt       = str_replace( '<#EMO_DIR#>', '-#-#-#EMO_DIR#-#-#-', $txt );
        
        /* Grab images */
        preg_match_all( '#<img([^>]+?)>#i', $txt, $matches );
        
        foreach( $matches[0] as $m )
        {
            $c = count( $urls );
            $urls[ $c ] = $m;
        
            $txt = str_replace( $m, '<!--url{' . $c . '}-->', $txt );
        }
        
        /* Grab <a> */
        preg_match_all( '#<a([^>]+?)>#i', $txt, $matches );
        
        foreach( $matches[0] as $m )
        {
            $c = count( $urls );
            $urls[ $c ] = $m;
        
            $txt = str_replace( $m, '<!--url{' . $c . '}-->', $txt );
        }
        
        /* Grab all other tags */
        preg_match_all( '#<(?:[/a-z]{1,})([^>]+?)>#i', $txt, $matches );
        
        foreach( $matches[0] as $m )
        {
            $c = count( $tags );
            $tags[ $c ] = $m;
        
            $txt = str_replace( $m, '<!--tag{' . $c . '}-->', $txt );
        }
        
        /* Grab non linked URLs */
        preg_match_all( '#((http|https|news|ftp)://(?:[^<>)["s]+|[a-zA-Z0-9/._-!&#;,%+?:=]+))#is', $txt, $matches );
        
        foreach( $matches[0] as $m )
        {
            $c = count( $urls );
            $urls[ $c ] = $m;
        
            $txt = str_replace( $m, '<!--url{' . $c . '}-->', $txt );
        }
        
        //-----------------------------------------
        // Convert back entities
        //-----------------------------------------
        
        for( $i = 65; $i <= 90; $i++ )
        {
            $txt = str_replace( "&#" . $i . ";", chr($i), $txt );
        }
        
        for( $i = 97; $i <= 122; $i++ )
        {
            $txt = str_replace( "&#" . $i . ";", chr($i), $txt );
        }
        
        //-----------------------------------------
        // Go all loopy
        //-----------------------------------------

        if ( is_array($acronyms) && count($acronyms) )
        {
            foreach( $acronyms as $r )
            {
                $this->_currentAcronym = $r;
                
                /*                                                                                                vv Ticket #835804 */
                $wordModifier    = ( IPS_DOC_CHAR_SET == 'UTF-8' && IPSText::isUTF8( $txt ) ) ? '[^-_&;<>p{L}d]|b' : '[^-<>w_&;]';
                $caseModifier    = empty($r['a_casesensitive']) ? 'i' : '';
                $r['a_short']    = preg_quote( $r['a_short'], "/" );

                $txt            = preg_replace_callback( '/(^|'. $wordModifier . ')(' . $r['a_short'] . ')(' . $wordModifier . '|$)/' . $caseModifier, array( $this, '_replaceAcronym' ), $txt );
            }
        }

        /* replace urls */
        if ( count( $urls ) )
        {
            foreach( $urls as $k => $v )
            {
                $txt = str_replace( "<!--url{" . $k . "}-->", $v, $txt );
            }
        }
        
        /* replace tags */
        if ( count( $tags ) )
        {
            foreach( $tags as $k => $v )
            {
                $txt = str_replace( "<!--tag{" . $k . "}-->", $v, $txt );
            }
        }
        
        $txt = str_replace( '-#-#-#EMO_DIR#-#-#-', '<#EMO_DIR#>', $txt );
        
        return $txt ? $txt : $temp_text;
    }
    
    /**
     * Callback function to replace a found acronym
     *
     * @param    array        $matches        Array of matches
     * @return    @e string    Replaced text
     */
    private function _replaceAcronym( $matches=array() )
    {
        return $this->_currentAcronym['a_semantic'] ? "{$matches[1]}<acronym title='{$this->_currentAcronym['a_long']}' class='bbc ipSeoAcronym'>{$matches[2]}</acronym>{$matches[3]}" : $matches[1] . $this->_currentAcronym['a_long'] . $matches[3];
    }
    
    /**
     * Strip paragraph wrap tags
     * @param string $txt
     * @return string
     */
    protected function _stripParagraphWrap( $txt )
    {
        $txt = trim( $txt );

        /* Clean up */
        $txt = preg_replace( '#^(<br([^>]+?)?>){1,}#i', '', $txt );
        
        $txt = trim( $this->_stripEmptyLeadingAndTrailingParagraphTags( $txt ) );
    
        if ( substr( $txt, 0, 3 ) == '<p>' && substr( $txt, -4 ) == '</p>' )
        {
            $txt = substr( $txt, 3, -4 );
        }
        
        $txt = trim( $txt );
        
        return $txt;
    }
    
    /**
     * Strips off blank or empty P tags
     * @param string $txt
     * @return string
     */
    protected function _stripEmptyLeadingAndTrailingParagraphTags( $txt )
    {
        /* Strip leading Ps */
        while( preg_match( '#^<p([^>]+?)?' . '>((&nbsp;|s)+?)?</p>#i', $txt, $match ) )
        {
            $txt = trim( preg_replace( '#^<p([^>]+?)?' . '>((&nbsp;|s)+?)?</p>#i', '', $txt ) );
        }
        
        /* Strip trailing Ps */
        while( preg_match( '#<p([^>]+?)?' . '>((&nbsp;|s)+?)?</p>$#i', $txt, $match ) )
        {
            $txt = trim( preg_replace( '#<p([^>]+?)?' . '>((&nbsp;|s)+?)?</p>$#i', '', $txt ) );
        }
        
        /* Strip trailing <br /> */
        while( preg_match( '#<br([^>]+?)?' . '/>((&nbsp;|s)+?)?$#i', $txt, $match ) )
        {
            $txt = trim( preg_replace( '#<br([^>]+?)?' . '/>((&nbsp;|s)+?)?$#i', '', $txt ) );
        }
        
        /* Strip trailing <div /> */
        while( preg_match( '#<div([^>]+?)?' . '>((&nbsp;|s)+?)?</div>$#i', $txt, $match ) )
        {
            $txt = trim( preg_replace( '#<div([^>]+?)?' . '>((&nbsp;|s)+?)?</div>$#i', '', $txt ) );
        }
        
        return $txt;
    }
    
    /**
     * Check and make safe embedded codes
     * @param array $matches
     */
    protected function _preserveCodeBoxes( $txt )
    {
        $map = array();

        /* CODE: Fetch paired opening and closing tags */
        $data = $this->getTagPositions( $txt, 'code', array( '[' , ']' ) );
    
        if ( is_array( $data['open'] ) )
        {
            foreach( $data['open'] as $id => $val )
            {
                $o = $data['open'][ $id ];
                $c = $data['close'][ $id ] - $o;
                
                /* Prevent unclosed tags from breaking this */
                if ( $o < 1 || $c < 1 )
                {
                    continue;
                }
                
                $slice = substr( $txt, $o, $c );
    
                /* Need to bump up lengths of opening and closing */
                $_origLength = strlen( $slice );
    
                /* Extra conversion for BBCODE>HTML mode */
                $slice = str_replace( "[", "&#91;", $slice );
                $slice = preg_replace( '#(https|http|ftp)://#' , '1&#58;//', $slice );
                #$slice = str_replace( "]", "&#93;", $slice );
                $slice = str_replace( "n", "<!-preserve.newline-->", $slice );
                
                /* Stop (r) (tm) and (c) from switching out */
                $slice = preg_replace( '#((tm|r|c))#i', '&#40;$1&#41;', $slice );
                
                $_newLength  = strlen( $slice );
    
                $txt = substr_replace( $txt, $slice, $o, $c );
    
                /* Bump! */
                if ( $_newLength != $_origLength )
                {
                    foreach( $data['open'] as $_id => $_val )
                    {
                        $_o = $data['open'][ $_id ];
                            
                        if ( $_o > $o )
                        {
                            $data['open'][ $_id ]  += ( $_newLength - $_origLength );
                            $data['close'][ $_id ] += ( $_newLength - $_origLength );
                        }
                    }
                }
            }
        }
        
        /* PRE: Fetch paired opening and closing tags */
        $data = $this->getTagPositions( $txt, 'pre', array( '<' , '>' ) );
    
        if ( is_array( $data['open'] ) )
        {
            foreach( $data['open'] as $id => $val )
            {
                $o = $data['open'][ $id ] ;
                $c = $data['close'][ $id ] - $o;
                    
                $slice = substr( $txt, $o, $c );
                    
                /* Need to bump up lengths of opening and closing */
                $_origLength = strlen( $slice );
                    
                /* Extra conversion for BBCODE>HTML mode */
                $slice = str_replace( "[", "&#91;", $slice );
                $slice = str_replace( "]", "&#93;", $slice );
                $slice = preg_replace( '#(https|http|ftp)://#' , '1&#58;//', $slice );
                $slice = str_replace( "n", "<!-preserve.newline-->", $slice );
                
                /* Stop (r) (tm) and (c) from switching out */
                $slice = preg_replace( '#((tm|r|c))#i', '&#40;$1&#41;', $slice );
                
                $_newLength  = strlen( $slice );
                    
                $txt = substr_replace( $txt, $slice, $o, $c );
                    
                /* Bump! */
                if ( $_newLength != $_origLength )
                {
                    foreach( $data['open'] as $_id => $_val )
                    {
                        $_o = $data['open'][ $_id ] + strlen( '<pre' );
                            
                        if ( $_o > $o )
                        {
                            $data['open'][ $_id ]  += ( $_newLength - $_origLength );
                            $data['close'][ $_id ] += ( $_newLength - $_origLength );
                        }
                    }
                }
            }
        }
        
        return $txt;
    }
    
    /**
     * Sort the smilies nicely in order of length.
     */
    protected function _sortSmilies()
    {
        $emoticons = array();
    
        if ( ! count( $this->_sortedSmilies ) )
        {
            /* Sort them! */
            $this->_sortedSmilies = $this->cache->getCache('emoticons');
                
            usort( $this->_sortedSmilies, array( $this, '_thisUsort' ) );
        }
    }
    
    /**
     * Custom sort operation
     *
     * @param    string        A
     * @param    string        B
     * @return    integer
     */
    protected static function _thisUsort($a, $b)
    {
        if ( strlen($a['typed']) == strlen($b['typed']) )
        {
            return 0;
        }
    
        return ( strlen($a['typed']) > strlen($b['typed']) ) ? -1 : 1;
    }
    
    /**
     * Add to errors
     * @param string $error
     */
    protected function _addParsingError( $error )
    {
        if ( $error && is_string( $error ) )
        {
            $this->_errors[] = $error;
        
            /* Legacy @todo remove in 4 */
            $this->error = $error;
        }
    }
    
    /**
     * Retrieve the proper emoticon image code
     *
     * @access    protected
     * @param    string        Emoticon code we are replacing (i.e. :D)
     * @param    string        Emoticon image to display (i.e. 'biggrin.png')
     * @return    string        Converted text
     */
    protected function _retrieveSmiley( $_emoCode, $_emoImage )
    {
        //-----------------------------------------
        // INIT
        //-----------------------------------------
    
        if ( ! $_emoCode or ! $_emoImage )
        {
            return '';
        }
    
        $this->emoticon_count++;
    
        $this->emoticon_alts[] = array( "#EMO_ALT_{$this->emoticon_count}#", $_emoCode );
        
        /**
         * <#EMO_DIR#> can end up not replaced at this point in specific situations.
         * @link http://community.invisionpower.com/resources/bugs.html/_/ip-board/emoticons-bug-with-pm-archives-r42796
         */
        if ( strstr( $this->settings['emoticons_url'], '<#EMO_DIR#>' ) AND $this->registry->output->skin['set_emo_dir'] )
        {
            $this->settings['emoticons_url'] = str_replace( '<#EMO_DIR#>', $this->registry->output->skin['set_emo_dir'], $this->settings['emoticons_url'] );
        }
    
        //-----------------------------------------
        // Return
        //-----------------------------------------
    
        return "<img src='" . $this->settings['emoticons_url'] . "/{$_emoImage}' class='bbc_emoticon' alt='#EMO_ALT_{$this->emoticon_count}#' />";
    }
    
    /**
     * Prevent auto parsing codes
     */
    protected function _processNoParseCodes( $content, $passLevel=1, $allowCodeTag=false )
    {
        if ( $passLevel == 3 )
        {
            /* At this point bbcode has been converted... */
            return preg_replace( '#(https|http|ftp)-~~-//#', '1://', $content );
        }
        
        $noParse = array();
    
        /* Find no parse codes */
        foreach( $this->cache->getCache('bbcode') as $bbcode )
        {
            /* Allowed this BBCode? */
            if ( $bbcode['bbcode_no_parsing'] )
            {
                /* CODE is a special case */
                if ( $allowCodeTag === false && $bbcode['bbcode_tag'] == 'code' )
                {
                    continue;
                }
                
                $noParse[ $bbcode['bbcode_tag'] ] = $bbcode['bbcode_tag'];
                            
                if ( $bbcode['bbcode_aliases'] )
                {
                    $tmp = explode( ',', $bbcode['bbcode_aliases'] );
                    
                    foreach( $tmp as $bc )
                    {
                        if ( trim( $bc ) )
                        {
                            $noParse[ $bc ] = $bc;
                        }
                    }
                }
            }
        }
    
        /* Got anything? */
        if ( ! count( $noParse ) )
        {
            return $content;
        }
        
        /* Sort by key length so that IMGTEST parses before IMG, for example */
        uksort( $noParse, create_function('$a,$b', 'return strlen($a) < strlen($b);') );
        
        foreach( $noParse as $tag )
        {
            if( $tag == 'img' )
            {
                continue;
            }

            /* Fetch paired opening and closing tags */
            $data = $this->getTagPositions( $content, $tag, array( '[', ']' ) );

            if ( is_array( $data ) && count( $data ) )
            {
                foreach( $data['open'] as $id => $val )
                {
                    $o = $data['open'][ $id ];
                    $c = $data['close'][ $id ] - $o;
                    
                    /* Prevent unclosed tags from breaking this */
                    if ( $o < 1 || $c < 1 )
                    {
                        continue;
                    }
                    
                    $slice = substr( $content, $o, $c );
    
                    $_origLen = strlen( $slice );
                        
                    if ( $passLevel == 2 )
                    {
                        //* No linkify */
                        preg_match_all( '#<a(?:[^>]+?)href=["']([^"']+?)["']([^>]+?)?>(.+?)(</a>|$)#is', $slice, $urlMatches );
                
                        /* Finish up URLs and such */
                        for( $i = 0 ; $i < count( $urlMatches[0] ) ; $i++ )
                        {
                            $raw  = $urlMatches[0][ $i ];
                            $url  = $urlMatches[1][ $i ];
                            $text = $urlMatches[3][ $i ];
                            
                            /* Some posts end up with the closing tag as part of the URL */
                            $url  = str_replace( '%5B/' . $tag . '%5D', '', $url );
                            
                            $slice = str_replace( $raw, preg_replace( '#(https|http|ftp)://#' , '1-~~-//', $url ), $slice );
                        }
                    }
                    else
                    {
                        $slice = preg_replace( '/[/', '&#91;', $slice );
                        $slice = preg_replace( '//(w+?)]/', '/1&#93;', $slice );
                        $slice = preg_replace( '#((r|tm|c))#', '&#40;1&#41;', $slice );
                        $slice = str_replace( "{parse", "&#123;parse", $slice );
                        $slice = preg_replace( '#(https|http|ftp)://#' , '1-~~-//', $slice );
                    }
                        
                    $_newLen  = strlen( $slice );
    
                    if ( $_newLen != $_origLen )
                    {
                        /* Bump up next */
                        foreach( $data['open'] as $_id => $_val )
                        {
                            $_o = $data['open'][ $_id ];
    
                            /* Ascii face alert */
                            if ( $_o > $o )
                            {
                                $data['open'][ $_id ]  += ( $_newLen - $_origLen );
                                $data['close'][ $_id ] += ( $_newLen - $_origLen );
                            }
                        }
                    }
    
                    $content = substr_replace( $content, $slice, $o, $c );
                }
            }
        }
    
        return $content;
    }
    
}
Онлайн: 0
Реклама