Вход Регистрация
Файл: IPBMafia.ru_IPB_3.4.6_Final_Rus _Nulled/board/upload/admin/sources/base/core.php
Строк: 4823
<?php
/**
 * <pre>
 * Invision Power Services
 * IP.Board v3.4.6
 * Static Classes for IP.Board 3
 *
 * These classes are not required as objects. We have grouped
 * together several singletons to prevent multiple file loads
 * Last Updated: $Date: 2013-09-26 22:37:28 -0400 (Thu, 26 Sep 2013) $
 * </pre>
 *
 * @author         $Author: rashbrook $
 * @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        12th March 2002
 * @version        $Revision: 12370 $
 *
 * @author    Matt
 */


/* Non setting settings defaults.
 * Do not edit here, edit them in your conf_global.php */
$_ipsPowerSettings = array( 'status_sidebar_show_x'            => 5,
                            
'ipb_disable_group_psformat'    => 0,
                            
'tags_max_truncated_len'        => 35,
                            
'max_bbcodes_per_post'            => 500,
                            
'postpage_contents'                => '5,10,15,20,25,30,35,40',
                            
'topicpage_contents'            => '5,10,15,20,25,30,35,40',
                            
'member_photo_crop'                => 100,
                            
'posting_allow_rte'                => 1# Will look to redirect old editor methods to new and will remove this (legacy compatibility)
                            
'like_notifications_limit'        => 1000,
                            
'actidx_override'                => 0,
                            
'signature_line_length'            => 200,
                            
'show_x_page_link'                => 2,
                            
'post_order_column'                => 'pid'// Other valid values: 'post_date'
                            
'assume_css_written'            => 0// Assume CSS has been written even with safe mode skins enabled - prevents lookup to see if file exists
                            
'forum_search_groupby'            => 0// If enabled, forum searches will perform a group by which returns more/better results, but is much more resource hungry and may cause problems on large boards
                            
);

class 
IPSAdCodeDefault
{
    
/**
     * Registry Object Shortcuts
     *
     * @var        object
     */
    
public $registry;
    public 
$DB;
    public 
$settings;
    public 
$request;
    public 
$lang;
    public 
$member;
    public 
$memberData;
    public 
$cache;
    public 
$caches;
    
    
/**
     * Ad code to overwrite the global header code
     *
     * @var        string
     */
    
public $headerCode '';
    
    
/**
     * Ad code to overwrite the global footer code
     *
     * @var        string
     */
    
public $footerCode '';
    
    public function 
__constructipsRegistry $registry )
    {
        
/* Make object */
        
$this->registry   =  $registry;
        
$this->DB         =  $this->registry->DB();
        
$this->settings   =& $this->registry->fetchSettings();
        
$this->request    =& $this->registry->fetchRequest();
        
$this->lang       =  $this->registry->getClass('class_localization');
        
$this->member     =  $this->registry->member();
        
$this->memberData =& $this->registry->member()->fetchMemberData();
        
$this->cache      =  $this->registry->cache();
        
$this->caches     =& $this->registry->cache()->fetchCaches();
    }
    
    
/**
     * Basic functionality
     */
    
public function userCanViewAds()
    {
        return 
false;
    }
}

/**
 * Deletion Log Class
 */
class IPSDeleteLog
{
    
/**
     * Add entry to the delete log
     *
     * @param    int            Object ID
     * @param    string        Object Type
     * @param    string        Reason for addition
     * @param    array        Array of member data of user adding log entry
     */
    
public static function addEntry$id$type$reason$memberData )
    {
        if ( 
$id AND $type AND is_array$memberData ) AND $memberData['member_id'] )
        {
            
ipsRegistry::DB()->replace'core_soft_delete_log', array( 'sdl_obj_id'        => $id,
                                                                       
'sdl_obj_key'       => $type,
                                                                       
'sdl_obj_reason'    => $reason,
                                                                       
'sdl_obj_member_id' => $memberData['member_id'],
                                                                       
'sdl_obj_date'      => time(),
                                                                       
'sdl_locked'           => ), array( 'sdl_obj_id''sdl_obj_key' ) );
                                                                       
            return 
TRUE;
        }
        
        return 
FALSE;
    }
    
    
/**
     * Remove entres to the delete log
     *
     * @param    array        Object IDs
     * @param    string        Object Type
     * @param    boolean        Force deletion (used when deleting topics/posts/etc)
     */
    
public static function removeEntries$ids$type$forceDelete=false )
    {
        if ( 
is_array$ids ) AND count$ids ) AND $type )
        {
            
$ids   IPSLib::cleanIntArray$ids );
            
            
/* if we're not a global mod, then lock these not remove unless we're deleting stuff */
            
if ( ! ipsRegistry::member()->getProperty('g_is_supmod') AND $forceDelete === false )
            {
                
ipsRegistry::DB()->update'core_soft_delete_log', array( 'sdl_locked' => ), 'sdl_obj_id IN (' implode','$ids ) . ') AND sdl_obj_key='' . $type . ''' );
            }
            else
            {
                
ipsRegistry::DB()->delete'core_soft_delete_log''sdl_obj_id IN (' implode','$ids ) . ') AND sdl_obj_key='' . $type . ''' );
            }
            
            return 
TRUE;
        }
        
        return 
FALSE;
    }
    
    
/**
     * Fetch entries from the delete log
     *
     * @param    array        Object IDs
     * @param    string        Object Type
     * @param    boolean        Parse Member Data
     */
    
public static function fetchEntries$ids$type$parseMember=true )
    {
        
$return = array();
        
        if ( 
is_array$ids ) AND count$ids ) AND $type )
        {
            
$ids IPSLib::cleanIntArray$ids );
            
            
ipsRegistry::DB()->build( array( 'select'   => 'l.*',
                                             
'from'     => array( 'core_soft_delete_log' => 'l' ),
                                             
'where'    => 'sdl_obj_id IN (' implode','$ids ) . ') AND sdl_obj_key='' . $type . ''',
                                             
'add_join' => array( array( 'select' => 'm.*',
                                                                          
'from'   => array( 'members' => 'm' ),
                                                                          
'where'  => 'l.sdl_obj_member_id=m.member_id' ),
                                                                   array( 
'select' => 'p.*',
                                                                            
'from'      => array( 'profile_portal' => 'p' ),
                                                                            
'where'  => 'l.sdl_obj_member_id=p.pp_member_id' ) ) ) );
                                                                            
            
$i ipsRegistry::DB()->execute();
                                                                            
            while( 
$row ipsRegistry::DB()->fetch$i ) )
            {
                if ( 
$parseMember )
                {
                    
$row['member'] = IPSMember::buildDisplayData$row );
                }
                
                
$return$row['sdl_obj_id'] ] = $row;
            }
            
            return 
$return;
        }
        
        return array();
    }
}

class 
IPSContentCache
{
    
/**
     * Keep track of what tables are linked to which key
     *
     * @var        array
     */
    
protected static $_tables = array( 'post' => 'content_cache_posts',
                                       
'sig'  => 'content_cache_sigs' );

    
/**
     * Keep track of what settings are linked to which key
     *
     * @var        array
     */
    
protected static $_settings = array( 'post' => 'cc_posts',
                                       
'sig'  => 'cc_sigs' );
                            
    
/**
     * Check to see whether content caching is enabled
     *
     * @return    boolean
     */
    
static public function isEnabled()
    {
        return ( 
ipsRegistry::$settings['cc_on'] AND ( ipsRegistry::$settings['cc_posts'] OR ipsRegistry::$settings['cc_sigs'] ) ) ? TRUE FALSE;
    }

    
/**
     * Check to see whether we have a valid type
     *
     * @param    string        Content Type (post/sig/etc)
     * @return    boolean
     */
    
static public function isValidType$type )
    {
        return isset( 
self::$_tables$type ] ) ? TRUE FALSE;
    }
    
    
/**
     * Fetch correct table name based on type
     * Assumes isValidType has been run
     *
     * @param    string        Content Type (post/sig/etc)
     * @return    boolean
     */
    
static public function fetchTableName$type )
    {
        return 
self::$_tables$type ];
    }
    
    
/**
     * Fetch correct setting value based on type
     * Assumes isValidType has been run
     *
     * @param    string        Content Type (post/sig/etc)
     * @return    boolean
     */
    
static public function fetchSettingValue$type )
    {
        return 
ipsRegistry::$settingsself::$_settings$type ] ];
    }

    
/**
     * Add data to the cache
     *
     * @param    int            Content ID
     * @param    string        Content Type (post/sig/etc)
     * @param    string        Content
     * @param    boolean        Already had preDb/preDisplay run. It FALSE, assumed preDb has been run and no HTML will be parsed but smilies and bbcode will be
     * @return    bool
     */
    
static public function update$id$type$content$parsed=TRUE )
    {
        
/* Enabled?? */
        
if ( ! self::isEnabled() )
        {
            return 
FALSE;
        }
        
        
/* Valid type?? */
        
if ( ! self::isValidType$type ) )
        {
            return 
FALSE;
        }
        
        
/* Search engine? */
        
if ( ipsRegistry::member()->is_not_human === TRUE )
        {
            return 
FALSE;
        }
        
        
/* Init */
        
$parsingSection 'topics';
        
        if ( 
$content AND $parsed !== TRUE )
        {
            
/* What are we parsing? */
            
switch( $type )
            {
                case 
'post':
                    
$parsingSection 'topics';
                break;
                case 
'sig':
                    
$parsingSection 'signatures';
                break;
            }
                    
            
/* Set up parser */
            
IPSText::getTextClass'bbcode' )->parse_smilies         1;
            
IPSText::getTextClass'bbcode' )->parse_html             0;
            
IPSText::getTextClass'bbcode' )->parse_nl2br             1;
            
IPSText::getTextClass'bbcode' )->parse_bbcode             1;
            
IPSText::getTextClass'bbcode' )->parsing_section         $parsingSection;
            
IPSText::getTextClass'bbcode' )->parsing_mgroup         ipsRegistry::member()->getProperty'member_group_id' );
            
IPSText::getTextClass'bbcode' )->parsing_mgroup_others ipsRegistry::member()->getProperty'mgroup_others' );

            
/* Format */
            
$content IPSText::getTextClass'bbcode' )->preDisplayParseIPSText::getTextClass'bbcode' )->preDbParse$content ) );
        }
        
        if ( 
$content )
        {
            
ipsRegistry::DB()->setDataType'cache_content''string' );
                                                
            
ipsRegistry::DB()->replaceself::fetchTableName$type ), array( 'cache_content_id' => $id,
                                                                              
'cache_content'    => $content,
                                                                              
'cache_updated'    => time() ), array( 'cache_content_id' ) );
        }
        else
        {
            
/* No content, then drop it */
            
self::drop$type$id );
        }
        
        return 
TRUE;
    }
    
    
/**
     * Drop data from the cache
     * If no ID is passed, it'll drop all caches for the supplied 'type'
     *
     * @param    string        Content Type (post/sig/etc)
     * @param    int/array    [Content ID]
     * @return    bool
     */
    
static public function drop$type$id=)
    {
        if ( ! 
self::isEnabled() )
        {
            return 
FALSE;
        }
        
        
/* Valid type?? */
        
if ( ! self::isValidType$type ) )
        {
            return 
FALSE;
        }
        
        if ( 
$id )
        {
            if ( 
is_array$id ) )
            {
                
$id implode','$id );
            }
            
            
ipsRegistry::DB()->deleteself::fetchTableName$type ), "cache_content_id IN (" $id ")" );
        }
        else
        {
            
ipsRegistry::DB()->deleteself::fetchTableName$type ) );
        }
        
        return 
TRUE;
    }
    
    
/**
     * Remove all "type" data from the cache
     *
     * @param    string        [Content Type (post/sig/etc)]
     * @return    int            Number of rows affected
     */
    
static public function truncate$type='' )
    {
        if ( ! 
self::isEnabled() )
        {
            return 
0;
        }
        
        
/* Valid type?? */
        
if ( $type AND ! self::isValidType$type ) )
        {
            return 
0;
        }
        
        
$affected 0;
        
        if ( 
$type )
        {
            
$count ipsRegistry::DB()->buildAndFetch( array( 'select' => 'COUNT(*) as total''from' => self::fetchTableName$type ) ) );
            
            
ipsRegistry::DB()->deleteself::fetchTableName$type ) );
            
$affected $count['total']; // ipsRegistry::DB()->getAffectedRows(); - With no where clause, mysql_affected_rows always returns 0
        
}
        else
        {
            foreach( 
self::$_tables as $type => $name )
            {
                
$count ipsRegistry::DB()->buildAndFetch( array( 'select' => 'COUNT(*) as total''from' => $name ) );
                
ipsRegistry::DB()->delete$name );
                
$affected += $count['total']; // $affected += ipsRegistry::DB()->getAffectedRows(); - With no where clause, mysql_affected_rows always returns 0
            
}
        }
        
        return 
intval$affected );
    }
    
    
/**
     * Count the number of cached items
     *
     * @param    string        [Content Type (post/sig/etc)]
     * @return    int            Combined number of items
     */
    
static public function count$type='' )
    {
        if ( ! 
self::isEnabled() )
        {
            return 
FALSE;
        }
        
        
/* Valid type?? */
        
if ( $type AND ! self::isValidType$type ) )
        {
            return 
FALSE;
        }
        
        
$count 0;
        
        if ( 
$type )
        {
            
$row   ipsRegistry::DB()->buildAndFetch( array( 'select' => 'COUNT(*) as c''from' => self::fetchTableName$type ) ) );
            
$count intval$row['c'] );
        }
        else
        {
            foreach( 
self::$_tables as $type => $name )
            {
                
$row    ipsRegistry::DB()->buildAndFetch( array( 'select' => 'COUNT(*) as c''from' => $name ) );
                
$count += intval$row['c'] );
            }
        }
        
        return 
intval$count );
    }
    
    
/**
     * Prune items back to X days
     *
     * If no type is supplied, all types are pruned
     *
     * @param    string        [Content Type (post/sig/etc)]
     * @return    int            Number of rows affected
     */
    
static public function prune$type='' )
    {
        if ( ! 
self::isEnabled() )
        {
            return 
FALSE;
        }
        
        
/* Valid type?? */
        
if ( $type AND ! self::isValidType$type ) )
        {
            return 
FALSE;
        }
        
        
$affected 0;
        
        if ( 
$type )
        {
            
$time time() - ( self::fetchSettingValue$type ) * 86400 );
            
            
ipsRegistry::DB()->deleteself::fetchTableName$type ), "cache_updated <" $time );
            
$affected ipsRegistry::DB()->getAffectedRows();
        }
        else
        {
            foreach( 
self::$_tables as $type => $name )
            {
                
$time time() - ( self::fetchSettingValue$type ) * 86400 );
                
                
ipsRegistry::DB()->deleteself::fetchTableName$type ), "cache_updated <" $time );
                
$affected += ipsRegistry::DB()->getAffectedRows();
            }
        }
        
        return 
intvalipsRegistry::DB()->getAffectedRows() );
    }
    
    
/**
     * Fetch table join
     *
     * Cheap way of grabbing the join on the cache table so that your code
     * doesn't have to check for whether we're using the cache or not, etc
     *
     * @param    string    Content Type (post/sig/etc)
     * @param    string    Join field (eg 'p.pid')
     * @param    string    [Table alias - default 'cca']
     * @param    string    [Table join type - default 'left']
     * @param    string    [Custom select so that fields can be aliased, etc]
     * @return    bool
     */
    
static public function join$type$joinField$alias='cca'$joinType='left'$customSelect='' )
    {
        if ( ! 
self::isEnabled() )
        {
            return 
FALSE;
        }
        
        
/* Valid type?? */
        
if ( ! self::isValidType$type ) )
        {
            return 
FALSE;
        }
        
        return array( 
'select' => ( $customSelect ) ? $customSelect $alias.'.*',
                      
'from'   => array( self::fetchTableName$type ) => $alias ),
                      
'where'  => $alias '.cache_content_id=' $joinField,
                      
'type'   => $joinType );
    }
}
    
    
/**
 * Experimental class for storing options as bitwise
 *
 * @author    Matt
 */
class IPSBWOptions
{
    
/**
     * Convert a bit field into an array of options
     *
     * @param    int        Bitwise option
     * @param    string    Type of options to decipher (user / groups / etc)
     * @param    string    App
     * @return    array
     * <code>$options = IPSBWOptions::thawOptions( 18, 'user', 'forums' );</code>
     */
    
static public function thaw$bitfield$type$app='global' )
    {
        
/* INIT */
        
$bitfield intval($bitfield);
        
$array    = array();
        
        
/* Generate bitwise array */
        
$bitArray self::_getBitWiseArray$type$app );
        
        if ( ! 
$bitArray OR ! count$bitArray ) )
        {
            return array();
        }
        
        
/* Build options */
        
foreach( $bitArray as $key => $bitvalue )
        {
            if ( 
$bitfield intval$bitvalue ) )
            {
                
$array$key ] = 1;
            }
            else
            {
                
$array$key ] = 0;
            }
        }
        
        return 
$array;
    }
    
    
/**
     * Build an SQL query bit
     *
     * @param    string        Field (field name as assigned by thaw)
     * @param    string        SQL field
     * @param    string        Type (members, groups, etc )
     * @param    string        App (global, forums, etc)
     * @param    string        Type of SQL query (add/remove/has)
     * @return    string        Formatted SQL field
     */
    
static public function sql$bitField$sqlField$type$app='global'$sql='has' )
    {
        
/* Generate int sign */
        
switch( $sql )
        {
            default:
            case 
'has':
                
$_sign '&';
            break;
            case 
'remove':
                
$_sign '-';
            break;
            case 
'add':
                
$_sign '+';
            break;
            case 
'invert':
                
$_sign '^';
            break;
        }
        
        
/* Generate bitwise array */
        
$bitArray self::_getBitWiseArray$type$app );

        
/* Do it.. .*/
        
if ( in_array$bitFieldarray_keys$bitArray ) ) )
        {
            if ( 
$sql == 'has' )
            {
                return 
'( ' $sqlField ' ' $_sign ' ' $bitArray$bitField ] . ' ) != 0';
            }
            else
            {
                return 
'( ' $sqlField ' ' $_sign ' ' $bitArray$bitField ] . ' )';
            }
        }
        else
        {
            return 
FALSE;
        }
    }
    
    
/**
     * Freeze options
     * Converts an array of options array( 'key' => 0 ... ) into an int for saving in a DB field
     *
     * @param    array         Array of key => values to save
     * @param    string        Type of options to save
     * @param    string        App
     * @return    int
     */
    
static public function freeze$toSave$type$app='global' )
    {
        
/* INIT */
        
$int 0;
        
        
/* Generate bitwise array */
        
$bitArray self::_getBitWiseArray$type$app );
        
        if ( ! 
$bitArray OR ! count$bitArray ) )
        {
            return 
0;
        }
        
        foreach( 
$bitArray as $key => $value )
        {
            if ( isset( 
$toSave$key ] ) )
            {
                if ( 
$toSave$key ] == )
                {
                    
$int += $value;
                }
            }
        }
        
        return 
intval$int );
    }

    
/**
     * Fetch and build the bitwise array
     *
     * @param    string        Array key to return
     * @return    array
     */
    
static protected function _getBitWiseArray$type$app )
    {
        
$bitArray   = array();
        
$allOptions ipsRegistry::fetchBitWiseOptions$app );
        
        if ( 
is_array$allOptions ) )
        {
            if ( isset( 
$allOptions$type ] ) AND is_array$allOptions$type ] ) )
            {
                
$n 1;
                
                foreach( 
$allOptions$type ] as $key )
                {
                    
$bitArray$key ] = $n;
                    
                    
$n *= 2;
                }
            }
        }
        
        return 
$bitArray;
    }
}

/**
 * Time Class
 *
 * Class for handling timestamps
 *
 */
class IPSTime
{
   
/**
    * Current timestamp
    *
    * @var        integer
    */
    
protected static $timestamp    IPS_UNIX_TIME_NOW;

   
/**
    * Number of seconds in a minute
    *
    * @var        integer
    */
    
protected static $minute        60;

   
/**
    * Number of seconds in a hour
    *
    * @var        integer
    */
    
protected static $hour        3600;

   
/**
    * Number of seconds in a day
    *
    * @var        integer
    */
    
protected static $day            86400;

   
/**
    * Number of seconds in a week
    *
    * @var        integer
    */
    
protected static $week        604800;

   
/**
    * Number of seconds in a year
    *
    * @var        integer
    */
    
protected static $year        220752000;

   
/**
    * Months with 31 days
    *
    * @var        array
    */
    
protected static $months_31 = array( 01030507, 08, 1012 );
    
   
/**
    * Months with 30 days
    *
    * @var        array
    */
    
protected static $months_30 = array( 0406, 09, 11 );

    
/**
     * time_class::dmy_format()
     * Generates a time stamp for the day/month/year
     *
     * @param    integer    [$ts]    Timestamp to format, self::$timestamp used if none specified
     * @return    @e void
     */
    
static public function dmy_format$ts=)
    {
        
/* Set the timestamp */
        
$_ts = ( $ts ) ? $ts self::$timestamp;

        
/* Break it into dmy format */
        
$_ts date"m,d,Y"$_ts );
        
$_ts explode","$_ts );

        
/* Return the timestamp */
        
return mktime000$_ts[0], $_ts[1], $_ts[2] );
    }

    
/**
     * time_class::time_ago()
     * Returns how long ago the specified time stamp was
     *
     * @param    integer    $ts    Timestamp to format
     * @return    @e void
     */
    
static public function time_ago$ts )
    {
        if( 
$ts == time() )
        {
            return 
'--';
        }
        if( 
$ts 60 )
        {
            
$plural = ( $ts == ) ? '' 's';
            return 
sprintf"%0d"$ts ) . " second$plural";
        }
        else if( 
$ts self::$hour )
        {
            
$plural = ( sprintf("%0d", ( $ts self::$minute ) ) == ) ? '' 's';
            return 
sprintf("%0d", ( $ts self::$minute ) ) . " minute$plural";
        }
        else if( 
$ts self::$day )
        {
            
$plural = ( sprintf("%0d", ( $ts self::$hour ) ) == ) ? '' 's';
            return 
sprintf("%0d", ( $ts self::$hour ) ) . " hour$plural";
        }
        else
        {
            
$plural = ( sprintf("%0d", ( $ts self::$day ) ) == ) ? '' 's';
            return 
sprintf("%0d", ( $ts self::$day ) ) . " day$plural";
        }
    }

    
/**
     * Set the timestamp
     *
     * @param    int        New timestamp
     * @return    @e void
     */
    
static public function setTimestamp$time )
    {
        
self::$timestamp $time;
    }
    
    
/**
     * Get the timestamp
     *
     * @return    int        Timestamp
     */
    
static public function getTimestamp()
    {
        return 
self::$timestamp;
    }
    
    
/**
     * time_class::add_minutes()
     * Adds the specified number of minutes to the timestamp
     *
     * @param    integer    [$num]    Number of minutes to add, 1 by default
     * @return    @e void
     */
    
static public function add_minutes$num=)
    {
        
self::$timestamp += self::$minute $num;
    }

    
/**
     * time_class::add_hours()
     * Adds the specified number of hours to the timestamp
     *
     * @param    integer    [$num]    Number of hours to add, 1 by default
     * @return    @e void
     */
    
static public function add_hours$num=)
    {
        
self::$timestamp += self::$hour $num;
    }

    
/**
     * time_class::add_days()
     * Adds the specified number of days to the timestamp
     *
     * @param    integer    [$num]    Number of days to add, 1 by default
     * @return    @e void
     */
    
static public function add_days$num=)
    {
        
self::$timestamp += self::$day $num;
    }

    
/**
     * time_class::add_weeks()
     * Adds the specified number of weeks to the timestamp
     *
     * @param    integer    [$num]    Number of weeks to add, 1 by default
     * @return    @e void
     */
    
static public function add_weeks$num=)
    {
        
self::$timestamp += self::$week $num;
    }

    
/**
     * time_class::add_month()
     * Adds a single month to the current timestamp, takes into account leap years
     *
     * @return    @e void
     */
    
static public function add_month()
    {
        
$daysInMonth date't'self::$timestamp );
        
        
self::$timestamp += self::add_daysintval$daysInMonth ) );
    }

    
/**
     * time_class::add_months()
     * Adds the specified number of months to the timestamp
     *
     * @param    integer    $num    Number of months to add, 1 by default
     * @return    @e void
     */
    
static public function add_months$num=)
    {
        for( 
$i 0$i $num$i++ )
        {
            
self::add_month();
        }
    }

    
/**
     * time_class::add_years()
     * Adds the specified number of years to the timestamp
     *
     * @param    integer    $num    Number of years to add, 1 by default
     * @return    @e void
     */
    
static public function add_years$num=)
    {
        for( 
$i 0$i $num$i++ )
        {
            
self::add_months12 );
            
//self::remove_days( 1 ); http://community.invisionpower.com/resources/bugs.html/_/ip-nexus/1-year-subscription-lose-one-day-r41507
        
}
    }

    
/**
     * time_class::remove_days()
     * Removes the specified number of days to the timestamp
     *
     * @param    integer    [$num]    Number of days to remove, 1 by default
     * @return    @e void
     */
    
static public function remove_days$num=)
    {
        
self::$timestamp -= self::$day $num;
    }

    
/**
     * Convert unix timestamp into: (no leading zeros)
     * array( 'day' => x, 'month' => x, 'year' => x, 'hour' => x, 'minute' => x );
     * Written into separate static public function to allow for timezone to be used easily
     *
     * @param    integer    [$unix]    Timestamp
     * @return    array     Date parts
     */
    
static public function unixstamp_to_human$unix=)
    {
        
$tmp gmdate'j,n,Y,G,i'$unix );

        list( 
$day$month$year$hour$min ) = explode','$tmp );

        return array( 
'day'    => $day,
                      
'month'  => $month,
                      
'year'   => $year,
                      
'hour'   => $hour,
                      
'minute' => $min );
    }

    
/**
     * Convert unix timestamp into mmddyyyy
     *
     * @param    integer    [$unix]    Timestamp
     * @param    string    [$sep]    Separator
     * @return    string    mm/dd/yyyy
     */
    
static public function unixstamp_to_mmddyyyy$unix=0$sep='/' )
    {
        if ( ! 
$unix )
        {
            return 
"";
        }

        
$date self::unixstamp_to_human$unix );

        return 
sprintf("%02d{$sep}%02d{$sep}%04d"$date['month'], $date['day'], $date['year'] );
    }

    
/**
     * Convert mmddyyyy into unix timestamp
     *
     * @param    string    [$date]            Date
     * @param    string    [$sep]            Separator
     * @param    bool    [$checkdate]    Whether to validate date or not
     * @return    integer    Timestamp
     */
    
static public function mmddyyyy_to_unixstamp$date=''$sep='/'$checkdate=true )
    {
        if ( ! 
$date )
        {
            return 
"";
        }

        list( 
$month$day$year ) = explode$sep$date );

        if ( 
$checkdate )
        {
            if ( ! 
checkdate$month$day$year ) )
            {
                return 
"";
            }
        }

        return 
self::human_to_unixstamp$day$month$year0);
    }

    
/**
     * Wrapper for gmmktime (separated for timezone management)
     *
     * @param    integer    $day    Day
     * @param    integer    $month    Month
     * @param    integer $year    Year
     * @param    integer    $hour    Hour
     * @param    integer    $minute    Minute
     * @return    integer    Timestamp
     */
    
static public function human_to_unixstamp$day$month$year$hour$minute )
    {
        return 
gmmktimeintval($hour), intval($minute), 0intval($month), intval($day), intval($year) );
    }

    
/**
     * My gmmktime() - PHP func seems buggy
     *
     * @param    integer    $hour    Hour
     * @param    integer    $min    Minute
     * @param    integer $sec    Second
     * @param    integer $month    Month
     * @param    integer $day    Day
     * @param    integer $year    Year
     * @return    integer    Timestamp
     * @since    2.0
     */
    
static public function date_gmmktime$hour=0$min=0$sec=0$month=0$day=0$year=)
    {
        
// Calculate UTC time offset
        
$offset date'Z' );

        
// Generate server based timestamp
        
$time   mktime$hour$min$sec$month$day$year );

        
// Calculate DST on / off
        
$dst    intvaldate'I'$time ) - date'I' ) );

        return 
$offset + ($dst 3600) + $time;
    }

    
/**
     * Hand rolled GETDATE method
     *
     * getdate doesn't work apparently as it doesn't take into account
     * the offset, even when fed a GMT timestamp.
     *
     * @param    integer    Unix date
     * @return    array    0, seconds, minutes, hours, mday, wday, mon, year, yday, weekday, month
     * @since    2.0
     */
    
static public function date_getgmdate$gmt_stamp )
    {
        
//$tmp = gmdate( 'j,n,Y,G,i,s,w,z,l,F,W,M', $gmt_stamp );
        
$format    '%e,%m,%Y,%H,%M,%S,%u,%j,%A,%B,%W,%b';
        
        
//-----------------------------------------
        // Some flags not available on Windows
        // @see http://www.php.net/manual/en/function.strftime.php#53340
        //-----------------------------------------
        
        
if( strposstrtolowerPHP_OS ), 'win' ) === )
        {
            
$mapping = array(
                            
'%e'    => sprintf("%' 2d"date("j"$gmt_stamp)),
                            
'%u'    => ($w date("w"$gmt_stamp)) ? $w 7,
                            );

            
$format str_replacearray_keys($mapping), array_values($mapping), $format );
        }
        
        
$tmp gmstrftime$format$gmt_stamp );

        list( 
$day$month$year$hour$min$seconds$wday$yday$weekday$fmon$week$smon ) = explode','$tmp );

        return array(  
0         => $gmt_stamp,
                       
"seconds" => $seconds//    Numeric representation of seconds    0 to 59
                       
"minutes" => $min,     //    Numeric representation of minutes    0 to 59
                       
"hours"     => $hour,      //    Numeric representation of hours    0 to 23
                       
"mday"     => trim($day),     //    Numeric representation of the day of the month    1 to 31
                       
"wday"     => $wday,    //    Numeric representation of the day of the week    0 (for Sunday) through 6 (for Saturday)
                       
"mon"     => $month,   //    Numeric representation of a month    1 through 12
                       
"year"     => $year,    //    A full numeric representation of a year, 4 digits    Examples: 1999 or 2003
                       
"yday"     => $yday,    //    Numeric representation of the day of the year    0 through 365
                       
"weekday" => $weekday//    A full textual representation of the day of the week    Sunday through Saturday
                       
"month"     => $fmon,    //    A full textual representation of a month, such as January or Mar
                       
"week"    => $week,    //    Week of the year
                       
"smonth"  => $smon,
                       
"smon"    => $smon
                    
);
    }
}

/**
* IPSLib
*
* Dumping ground for functions that don't fit anywhere else
*
*/
class IPSLib
{
    
/**
     * FURL Templates
     *
     * @param    array
     */
    
static protected $_furlTemplates = array();
    
    
/**
     * Search configs
     *
     * @param    array
     */
    
static protected $_searchConfigs = array();
    
    
/**
     * Log in methods
     *
     * @param    array
     */
    
static protected $_lims            = array();
    
    
/**
     * Returns the class name to be instantiated, the class file will already be included 
     *
     * @param    string     $filePath        File location of the class (leave an empty string if you've already loaded the main file)
     * @param    string    $className        Name of the class
     * @param    string    $app            Application (defaults to 'core')
     * @param    bool    $supressErrors    If true, will require file with @ operator. Useful for third-party libraries which may throw PHP notices
     * @return    string    Class Name
     */
    
static public function loadLibrary$filePath$className$app='core'$supressErrors=false )
    {
        
/* Get the class */
        
if ( $filePath != '' )
        {
            if ( 
$supressErrors )
            {
                @require_once( 
$filePath );/*noLibHook*/
            
}
            else
            {
                require_once( 
$filePath );/*noLibHook*/
            
}
        }
        
        
/* Check for hooks */
        
$hooksCache    ipsRegistry::cache()->getCache('hooks');

        if( isset( 
$hooksCache['libraryHooks'][ $app ][ $className ] ) AND is_array$hooksCache['libraryHooks'][ $app ][ $className ] ) AND count$hooksCache['libraryHooks'][ $app ][ $className ] ) )
        {
            foreach( 
$hooksCache['libraryHooks'][ $app ][ $className ] as $classOverloader )
            {
                
/* Hooks: Do we have the hook file? */
                
if( is_fileIPS_HOOKS_PATH $classOverloader['filename'] ) )
                {
                    require_once( 
IPS_HOOKS_PATH $classOverloader['filename'] );/*noLibHook*/
                
                    
if( class_exists$classOverloader['className'] ) )
                    {
                        
/* Hooks: We have the hook file and the class exists - reset the classname to load */
                        
$className $classOverloader['className'];
                    }
                }
            }
        }
        
        
/* Return Class Name */
        
return $className;
    }
    
    
/**
     * Returns the class name to be instantiated, the class file will already be included 
     *
     * @param    string     $filePath        File location of the class
     * @param    string    $className        Name of the class
     * @return    string    Class Name
     */
    
static public function loadActionOverloader$filePath$className )
    {
        
/* Get the class */
        
require_once( $filePath );/*noLibHook*/
        
        /* Hooks: Are we overloading this class? */
        
$hooksCache    ipsRegistry::cache()->getCache('hooks');

        if( isset( 
$hooksCache['commandHooks'][ $className ] ) AND is_array$hooksCache['commandHooks'][ $className ] ) AND count$hooksCache['commandHooks'][ $className ] ) )
        {
            foreach( 
$hooksCache['commandHooks'][ $className ] as $classOverloader )
            {
                
/* Hooks: Do we have the hook file? */
                
if( is_fileIPS_HOOKS_PATH $classOverloader['filename'] ) )
                {
                    require_once( 
IPS_HOOKS_PATH $classOverloader['filename'] );/*noLibHook*/
                
                    
if( class_exists$classOverloader['className'] ) )
                    {
                        
/* Hooks: We have the hook file and the class exists - reset the classname to load */
                        
$className $classOverloader['className'];
                    }
                }
            }
        }
        
        
/* Return Class Name */
        
return $className;
    }
    
    
/**
     * Checks if there are any data hooks to run
     *
     * @param    array     $dataArray        Data to be passed into the hooks
     * @param    string    $hookLocation    Location the data was sent from
     * @return    @e void
     */
    
static public function doDataHooks( &$dataArray$hookLocation )
    {
        
/* Loop through the cache */
        
$hooksCache ipsRegistry::cache()->getCache'hooks' );

        if( isset(
$hooksCache['dataHooks'][ $hookLocation ]) AND is_array$hooksCache['dataHooks'][ $hookLocation ] ) AND count$hooksCache['dataHooks'][ $hookLocation ] ) )
        {
            foreach( 
$hooksCache['dataHooks'][ $hookLocation ] as $r )
            {
                
/* Check for hook file */
                
if( is_fileIPS_HOOKS_PATH $r['filename'] ) )
                {
                    
/* Check for hook class */
                    
require_once( IPS_HOOKS_PATH $r['filename'] );/*noLibHook*/
                    
                    
if( class_exists$r['className'] ) )
                    {
                        
/* Create and run the hook */
                        
$_hook        = new $r['className'];
                        
$newArray    $_hook->handleData$dataArray );
                        
                        
/* Make sure the array isn't wiped out */
                        
if( is_array$newArray ) && count$newArray ) )
                        {
                            
$dataArray $newArray;
                        }
                    }
                }
            }
        }
    }
    
    
/**
     * Returns an array of data hook locations
     *
     * @return    array
     */
    
static public function getDataHookLocations()
    {
        
$_locations = array();
        
        
/* Loop all apps and get back our locations! */
        
foreach( ipsRegistry::$applications as $app_dir => $application )
        {
            
$dataHookLocations = array();

            if( 
is_fileIPSLib::getAppDir$app_dir ) . '/extensions/dataHookLocations.php' ) )
            {
                require_once( 
IPSLib::getAppDir$app_dir ) . '/extensions/dataHookLocations.php' );/*noLibHook*/
                
                
if ( is_array($dataHookLocations) && count($dataHookLocations) )
                {
                    
$_locations array_merge$_locations$dataHookLocations );
                }
            }
        }
        
        return 
$_locations;
    }
    
    
/**
     * Checks to see if there is a template hook installed at the specified location
     *
     *
     * @param    string    $group
     * @param    array    $id
     * @return    bool
     */
    
static public function locationHasHooks$group$ids )
    {
        
/* Return right away if we don't have an ids to check */
        
if( ! is_array$ids ) || ! count$ids ) )
        {
            return 
false;
        }

        
/* Reformat the cache on the first call, to save processing later */
        
static $formattedCache    = array();

        if( !isset(
$formattedCache$group ]) )
        {
            
$formattedCache$group ] = array();
                                    
            
$hookCache ipsRegistry::cache()->getCache'hooks' );

            if ( isset( 
$hookCache['templateHooks'][ $group ] ) AND is_array($hookCache['templateHooks'][ $group ]) AND count($hookCache['templateHooks'][ $group ]) )
            {
                foreach( 
$hookCache['templateHooks'][ $group ] as $_hook )
                {
                    
$formattedCache$group ][] = $_hook['id'];
                }
            }
        }

        
/* Use formatted cache to check */
        
if( count$formattedCache$group ] ) )
        {
            foreach( 
$ids as $id )
            {
                if( 
in_array$id$formattedCache$group ] ) )
                {
                    return 
true;
                }
            }
        }

        return 
false;
    }
    
    
/**
     * Quickly determines if we've got VK enabled and set up
     *
     * @access    public
     * @return    boolean
     */
    
static public function vkontakte_enabled()
    {
        return ( 
ipsRegistry::$settings['vk_enabled'] AND ipsRegistry::$settings['vk_api_id'] AND ipsRegistry::$settings['vk_secret'] ) ? TRUE FALSE;
    }

    
/**
     * Central setlocale method so we can adjust as needed
     *
     * @param    string        Locale to set
     * @return    @e void
     * @link    http://community.invisionpower.com/tracker/issue-16386-language-locale-gives-error/
     * @link    http://community.invisionpower.com/tracker/issue-18424-change-lang-locale/
     */
    
static public function setlocale$locale='' )
    {
        if( !
$locale )
        {
            return;
        }
        
        
$dodgy = array( 'tr_''az_''crh_''ku_''tt_''turkish' );
        
        foreach( 
$dodgy as $lc )
        {
            if ( 
stripos$locale$lc ) !== FALSE )
            {
                
setlocaleLC_COLLATE$locale );
                
setlocaleLC_MONETARY$locale );
                
setlocaleLC_NUMERIC$locale );
                
setlocaleLC_TIME$locale );
                @
setlocaleLC_MESSAGES$locale );

                return;
            }
        }
        
        
setlocaleLC_ALL$locale );
    }
    
    
/**
     * Quickly determines if we've got FB enabled and set up
     *
     * @return    boolean
     */
    
static public function fbc_enabled()
    {
        return ( 
ipsRegistry::$settings['fbc_enable'] AND ipsRegistry::$settings['fbc_appid'] AND ipsRegistry::$settings['fbc_secret'] ) ? TRUE FALSE;
    }
    
    
/**
     * Quickly determines if we've got twitter enabled and set up
     *
     * @return    boolean
     */
    
static public function twitter_enabled()
    {
        return ( 
ipsRegistry::$settings['tc_enabled'] AND ipsRegistry::$settings['tc_token'] AND ipsRegistry::$settings['tc_secret'] ) ? TRUE FALSE;
    }
    
    
/**
     * Quickly determines if we've got other log in enabled and set up
     *
     * @return    boolean
     */
    
static public function loginMethod_enabled$method )
    {
        if ( ! 
countself::$_lims ) )
        {
            if ( 
is_arrayipsRegistry::cache()->getCache('login_methods') ) )
            {
                
$cache ipsRegistry::cache()->getCache('login_methods');
                
                foreach( 
$cache as $lim )
                {
                    
self::$_lims$lim['login_folder_name'] ] = $lim['login_folder_name'];
                }
            }
        }
        
        switch( 
$method )
        {
            case 
'vkontakte':
                return 
self::vkontakte_enabled();
            break;
            case 
'facebook':
                return 
self::fbc_enabled();
            break;
            case 
'twitter':
                return 
self::twitter_enabled();
            break;
            default:
                    return 
in_array$methodself::$_lims ) ? true false;
            break;
        }
    }
    
    
/**
     * Loop through the input request and create an array of ids based on a string prefix
     *
     * @param    string        Prefix
     * @return    array
     */
    
static public function fetchInputAsArray$prefix='' )
    {
        
//-----------------------------------------
        // INIT
        //-----------------------------------------

        
$ids = array();

        if( !
$prefix )
        {
            return 
$ids;
        }
        
        
//-----------------------------------------
        // GET from checkboxes
        //-----------------------------------------

        
foreach ( ipsRegistry::$request as $k => $v )
        {
            if ( 
$v && preg_match"/^" $prefix '(d+)$/'$k$match ) )
            {
                
$ids[] = $match[1];
            }
        }

        
$ids self::cleanIntArray$ids );
        
        return 
$ids;
    }
    
    
/**
     * Little function to return the version number data
     *
     * Handy to use when dealing with IN_DEV, etc
     * Uses the constant where available
     *
     * @param    string  App ( Default 'core')
     * @return    array  array( 'long' => x, 'human' => x )
     */
    
static public function fetchVersionNumber$app='core' )
    {
        if ( ! 
definedIPB_VERSION ) OR ! definedIPB_LONG_VERSION ) )
        {
            require_once( 
IPS_ROOT_PATH 'setup/sources/base/setup.php' );/*noLibHook*/
            
            
$XMLVersions IPSSetUp::fetchXmlAppVersions$app );
            
$tmp         $XMLVersions;
            
krsort$tmp );

            foreach( 
$tmp as $long => $human )
            {
                
$return = array( 'long' => $long'human' => $human );
                break;
            }
        }
        else
        {
            
$return = array( 'long' => IPB_LONG_VERSION'human' => IPB_VERSION );
        }
        
        return 
$return;
    }
    
    
/**
     * Cheeky little function to locate group table fields from other apps
     *
     * @return array     Array of fields from different apps
     */
    
static function fetchNonDefaultGroupFields()
    {
        
$fields = array();
        
        foreach( array( 
'calendar''gallery''blog''downloads''ccs''ipchat''nexus''ipseo' ) as $app )
        {
            
$_file IPSLib::getAppDir$app ) . '/setup/versions/install/sql/' $app '_mysql_tables.php';
            
            if ( 
is_file$_file ) )
            {
                
$TABLE = array();
                
                require( 
$_file );/*noLibHook*/
                
                
foreach( $TABLE as $t )
                {
                    if ( 
preg_match'#^ALTER TABLEs+?groupss+?ADDs+?(S+?)s#i'$t$match ) )
                    {
                        
$fields[] = $match[1];
                    }
                }
            }
        }
        
        return 
$fields;
    }
    
    
/**
     * Update settings
     *
     * @param    array        array('conf_key' => 'new value')
     * @return    true/false
     */
    
static public function updateSettings$update=array(), $parseEditorValues=FALSE )
    {    
        
//-----------------------------------------
        // Load the settings we need to update
        //-----------------------------------------
    
        
$fields array_keys$update );
        
ipsRegistry::DB()->build( array( 'select' => '*''from' => 'core_sys_conf_settings''where' => "conf_key IN ('" implode"','"$fields ) . "')" ) );
        
ipsRegistry::DB()->execute();
        
$db_fields = array();
        while ( 
$r ipsRegistry::DB()->fetch() )
        {
            
$db_fields$r['conf_key']  ] = $r;
        }
        
        if ( empty( 
$db_fields ) )
        {
            return 
false;
        }

        
/* We have to examine $_POST as the custom PHP for some settings
        still sets that rather than $value */            
        
$oldPostData $_POST;
        
        
//-----------------------------------------
        // Update values
        //-----------------------------------------
        
        
foreach( $db_fields as $key => $data )
        {
            
$value null;
            
            
/* Init */
            
if ( !is_array$update$key ] ) )
            {
                if ( 
$update$key ] != $data['conf_default'] )
                {
                    
$value str_replace"&#39;""'"IPSText::stripslashes$update$key ] ) );
                    
$value $value == '' "{blank}" $value;
                }
                else
                {
                    
$value $data['conf_default'];
                }
            }

            
/* Evaluate PHP */
            
if ( $data['conf_evalphp'] )
            {
                
$data['conf_evalphp']    = str_replace'&#092;''\', $data['conf_evalphp'] );
                $save                = 1;

                $data['
conf_evalphp'] = str_replace( '$this->registry', 'ipsRegistry::instance()', $data['conf_evalphp'] );
                $data['
conf_evalphp'] = str_replace( '$this->cache', 'ipsRegistry::cache()', $data['conf_evalphp'] );
                $data['
conf_evalphp'] = str_replace( '$this->DB', 'ipsRegistry::DB()', $data['conf_evalphp'] );
                $data['
conf_evalphp'] = str_replace( '$this->settings', 'ipsRegistry::$settings', $data['conf_evalphp'] );
                $data['
conf_evalphp'] = str_replace( '$this->lang', 'ipsRegistry::getClass('class_localization')', $data['conf_evalphp'] );
                
                eval( $data['
conf_evalphp'] );
                
                if ( $_POST[ $key ] !== $oldPostData[ $key ] )
                {
                    $value = $_POST[ $key ];
                }
                
                /* Was value set? */
                if ( $value === null )
                {
                    $value = str_replace( "&#39;", "'", IPSText::stripslashes( 
$update[ $key ] ) );
                    
$value = $value == '' ? "{blank}" : $value;
                }
            }

            /* Parse */
            if ( 
$parseEditorValues and $data['conf_type'] == 'editor' )
            {
                IPSText::getTextClass('bbcode')->bypass_badwords    = 1;
                IPSText::getTextClass('bbcode')->parse_smilies        = 1;
                IPSText::getTextClass('bbcode')->parse_html            = 1;
                IPSText::getTextClass('bbcode')->parse_bbcode        = 1;
                IPSText::getTextClass('bbcode')->parse_nl2br        = 1;
                                 
                if( trim(
$value) == '<br>' OR trim($value) == '<br />' )
                {
                    
$value    = '';
                }
                else
                {
                    
$classToLoad  = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/editor/composite.php', 'classes_editor_composite' );
                    
$editor = new $classToLoad();
                    
$value = $editor->process$value );
                }
            }
            
            /* Strip new lines */
            
$value    = str_replace( "r", "", $value );
            
            /* Save */
            if ( 
$value != $data['conf_default'] )
            {
                ipsRegistry::DB()->update( 'core_sys_conf_settings', array( 'conf_value' => 
$value ), 'conf_id=' . $data['conf_id'] );
            }
            else if( ( isset( 
$update[ $key ] ) && $update[ $key ] == '' ) || ( $update[ $key ] == $data['conf_default'] ) || $data['conf_value'] != '' )
            {    
                ipsRegistry::DB()->update( 'core_sys_conf_settings', array( 'conf_value' => '' ), 'conf_id=' . 
$data['conf_id'] );
            }
        }

        //-----------------------------------------
        // Recache
        //-----------------------------------------
        
        ipsRegistry::DB()->build( array( 'select' => '*', 'from' => 'core_sys_conf_settings', 'where' => 'conf_add_cache=1' ) );
        
$info = ipsRegistry::DB()->execute();
    
        while ( 
$r = ipsRegistry::DB()->fetch($info) )
        {    
            
$value = $r['conf_value'] != "" ?  $r['conf_value'] : $r['conf_default'];
            
            if ( 
$value == '{blank}' )
            {
                
$value = '';
            }

            
$settings[ $r['conf_key'] ] = $value;
        }
        
        ipsRegistry::cache()->setCache( 'settings', 
$settings, array( 'array' => 1 ) );
        
        //-----------------------------------------
        // Return
        //-----------------------------------------
        
        return true;
    }
    
    /**
     * Retrieve the default language
     *
     * @return    string        Default language id (most likely a number)
     */
    static public function getDefaultLanguage()
    {
        
$cache    = ipsRegistry::cache()->getCache('lang_data');
        
        if( !count(
$cache) OR !is_array($cache) )
        {
            ipsRegistry::getClass('class_localization')->rebuildLanguagesCache();
            
            
$cache    = ipsRegistry::cache()->getCache('lang_data');
        }
        
        
$_default    = 1;
        
        foreach( 
$cache as $_lang )
        {
            if( 
$_lang['lang_default'] )
            {
                
$_default    = $_lang['lang_id'];
                break;
            }
        }
        
        return 
$_default;
    }
    
    /**
     * Build a global caches array from the database
     *
     * @return  @e array Array of global caches
     */
    static public function buildGlobalCachesArray()
    {
        /* INIT */
        
$globalCaches = array();
        
        /* Get apps with global caches */
        ipsRegistry::DB()->build( array( 'select' => 'app_global_caches',
                                         'from'   => 'core_applications',
                                         'where'  => "
app_enabled=AND app_global_caches != '' AND app_global_caches " . ipsRegistry::DB()->buildIsNull( false )
                                 )        );
        ipsRegistry::DB()->execute();
        
        while( 
$gc = ipsRegistry::DB()->fetch() )
        {
            
$caches = explode( ',', $gc['app_global_caches'] );
            
            foreach( 
$caches as $key )
            {
                if ( 
$key )
                {
                    
$globalCaches[] = $key;
                }
            }
        }
        
        /* Get hooks with global caches */
        ipsRegistry::DB()->build( array( 'select' => 'hook_global_caches',
                                         'from'   => 'core_hooks',
                                         'where'  => "
hook_enabled=AND hook_global_caches != '' AND hook_global_caches " . ipsRegistry::DB()->buildIsNull( false )
                                 )        );
        ipsRegistry::DB()->execute();
        
        while( 
$gc = ipsRegistry::DB()->fetch() )
        {
            
$caches = explode( ',', $gc['hook_global_caches'] );
            
            foreach( 
$caches as $key )
            {
                if ( 
$key )
                {
                    
$globalCaches[] = $key;
                }
            }
        }
        
        /* Let's have only unique values and return */
        
$globalCaches = array_unique( $globalCaches );
        
        return 
$globalCaches;
    }
    
    /**
     * Cache global caches
     *
     * @return    @e boolean
     * 
     * @exceptions
     * CANNOT_WRITE            Cannot write to cache file
     * NO_DATA_TO_WRITE        No data to write
     */
    static public function cacheGlobalCaches()
    {
        /* Init vars */
        
$data            = '';
        
$globalCaches    = self::buildGlobalCachesArray();
        
        /* Got any caches? */
        if ( count( 
$globalCaches ) )
        {
            
$_date = gmdate( 'r', time() );
            
$_var  = var_export( $globalCaches, TRUE );
            
$data  = <<<EOF
<?php
/**
 * Global Caches cache. Do not attempt to modify this file.
 * Please modify the relevant setting for each application or hook
 *
 * Written: 
{$_date}
 *
 * Why? Because Tera says so this time :P
 */

$GLOBAL_CACHES = {$_var};

EOF;
        }
        
        /* Got data to write? */
        if ( 
$data )
        {
            if ( @file_put_contents( GLOBAL_CACHE_PATH, 
$data ) )
            {
                return TRUE;
            }
            else
            {
                throw new Exception( 'CANNOT_WRITE' );
            }
        }
        else
        {
            /* No data? Delete any current file as well */
            @unlink( GLOBAL_CACHE_PATH );
            throw new Exception( 'NO_DATA_TO_WRITE' );
        }
    }
    
    /**
     * Build furl templates from FURL extensions
     *
     * @return  array
     */
    static public function buildFurlTemplates()
    {
        /* INIT */
        
$apps            = array();
        
$_SEOTEMPLATES    = array();
        static 
$_apps    = array();
        
        /* Done this already? */
        if ( self::
$_furlTemplates )
        {
            return self::
$_furlTemplates;
        }
        
        /**
         * Get app data and cache - 1 query is better than 1 per app
         */
        ipsRegistry::DB()->build( array( 'select' => 'app_directory, app_public_title, app_enabled', 'from' => 'core_applications' ) );
        ipsRegistry::DB()->execute();
        
        while( 
$_r = ipsRegistry::DB()->fetch() )
        {
            
$_apps[ $_r['app_directory'] ]    = $_r;
        }

        /* Because this is called before the cache is unpacked, we need to expensively grab all app dirs */
        foreach( array( 'applications', 'applications_addon/ips', 'applications_addon/other' ) as 
$folder )
        {
            try
            {
                foreach( new DirectoryIterator( IPS_ROOT_PATH . 
$folder ) as $file )
                {
                    if ( ! 
$file->isDot() AND $file->isDir() )
                    {
                        
$_name = $file->getFileName();
                        
                        if ( substr( 
$_name, 0, 1 ) != '.' )
                        {
                            /* Check if this app is enabled before including the templates.. */
                            
$_check = isset($_apps[ $_name ]) ? $_apps[ $_name ] : array( 'app_public_title' => '', 'app_enabled' => 0 );

                            if ( 
$_check['app_public_title'] && $_check['app_enabled'] )
                            {
                                
$apps[ $folder . '/' . $_name ] = $_name;
                            }
                        }
                    }
                }
            } catch ( Exception 
$e ) {}
        }
        
        /* First, add in core stuffs */
        ipsRegistry::_loadCoreVariables();
        
$templates = ipsRegistry::_fetchCoreVariables('templates');
        
        if ( is_array( 
$templates ) )
        {
            foreach( 
$templates as $key => $data )
            {
                self::
$_furlTemplates[ $key ] = $data;
            }
        }
        
        /* Loop over the applications and build */
        foreach( 
$apps as $path => $app_dir )
        {
            
$furltemplatesfile = IPS_ROOT_PATH . $path;
            if(IPB_USE_ONLY_ID_FURL && is_file( 
$furltemplatesfile.'/extensions/furlTemplatesID.php'))
            {
                
$furltemplatesfile.='/extensions/furlTemplatesID.php';
            }
            else
            {
                
$furltemplatesfile.='/extensions/furlTemplates.php';
            }
            if ( is_file( 
$furltemplatesfile ) )
            {
                
$_SEOTEMPLATES = array();
                
                require( 
$furltemplatesfile );/*noLibHook*/
                
                if ( is_array( 
$_SEOTEMPLATES ) && count( $_SEOTEMPLATES ) )
                {
                    foreach( 
$_SEOTEMPLATES as $key => $data )
                    {
                        self::
$_furlTemplates[ $key ] = $data;
                    }
                }
            }
        }
        
        /* Return for anyone else */
        return self::
$_furlTemplates;
    }
    
    /**
     * Cache templates from FURL extensions
     *
     * @return  boolean
     * @exceptions
     * CANNOT_WRITE        Cannot write to cache file
     * NO_DATA_TO_WRITE    No data to write
     */
    static public function cacheFurlTemplates()
    {
        if ( ! count( self::
$_furlTemplates ) )
        {
            self::buildFurlTemplates();
        }

        if ( count( self::
$_furlTemplates ) )
        {
            
$_date = gmdate( 'r', time() );
            
$_var  = var_export( self::$_furlTemplates, TRUE );
            
$data  = <<<EOF
<?php
/**
 * FURL Templates cache. Do not attempt to modify this file.
 * Please modify the relevant 'furlTemplates.php' file in /{app}/extensions/furlTemplates.php
 * and rebuild from the Admin CP
 *
 * Written: 
{$_date}
 *
 * Why? Because Matt says so.
 */
 
$templates = {$_var};

?>
EOF;
        
            if ( ! @file_put_contents( FURL_CACHE_PATH, 
$data ) )
            {
                throw new Exception( 'CANNOT_WRITE' );
            }
        }
        else
        {
            throw new Exception( 'NO_DATA_TO_WRITE' );
        }
        
        return TRUE;
    }
    
    /**
     * Recursively cleans keys and values and
     * inserts them into the input array
     *
     * @param    mixed        Input data
     * @param    array        Storage array for cleaned data
     * @param    integer        Current iteration
     * @return    array         Cleaned data
     */
    static public function parseIncomingRecursively( &
$data$input=array(), $iteration = 0 )
    {
        // Crafty hacker could send something like &foo[][][][][][]....to kill Apache process
        // We should never have an input array deeper than 20..

        if ( 
$iteration >= 20 )
        {
            return 
$input;
        }

        if ( ! is_array( 
$data ) )
        {
            return 
$input;
        }
        
        foreach( 
$data as $k => $v )
        {
            if ( is_array( 
$v ) )
            {
                
$input[ $k ] = self::parseIncomingRecursively( $data[ $k ], array(), $iteration + 1 );
            }
            else
            {
                
$k = IPSText::parseCleanKey( $k );
                
$v = IPSText::parseCleanValue( $v, false );
                
                
$input[ $k ] = $v;
            }
        }

        return 
$input;
    }

    /**
     * Recursively cleans values after settings have been loaded.
     * Necessary for certain functions (such as whether to strip space chars or not)
     *
     * @param    mixed        Input data
     * @param    integer        Current iteration
     * @return    array         Cleaned data
     */
    static public function postParseIncomingRecursively( 
$request$iteration = 0 )
    {
        // Crafty hacker could send something like &foo[][][][][][]....to kill Apache process
        // We should never have an input array deeper than 20..

        if ( 
$iteration >= 20 OR !is_array($request) )
        {
            return 
$request;
        }

        foreach( 
$request as $k => $v )
        {
            if ( is_array( 
$v ) )
            {
                
$request[ $k ] = self::postParseIncomingRecursively( $v, ++$iteration );
            }
            else
            {
                
$v = IPSText::postParseCleanValue( $v );

                
$request[ $k ] = $v;
            }
        }

        return 
$request;
    }
    
    /**
     * Performs basic cleaning, Null characters, etc
     *
     * @param    array     Input data
     * @return    array     Cleaned data
     */
    static public function cleanGlobals( &
$data$iteration = 0 )
    {
        // Crafty hacker could send something like &foo[][][][][][]....to kill Apache process
        // We should never have an input array deeper than 10..

        if ( 
$iteration >= 10 )
        {
            return;
        }
                
        foreach( 
$data as $k => $v )
        {
            if ( is_array( 
$v ) )
            {
                self::cleanGlobals( 
$data[ $k ], ++$iteration );
            }
            else
            {
                # Null byte characters
                
$v = str_replace( chr('0') , '', $v );
                
$v = str_replace( ""    , '', $v );
                
$v = str_replace( "x00"  , '', $v );

                // @link    http://community.invisionpower.com/tracker/issue-21188-post-processor-eating-characters/
                //
$v = str_replace( '%00'   , '', $v );

                # File traversal
                
$v = str_replace( "../", "&#46;&#46;/", $v );
                
                /* RTL override */
                
$v str_replace'&#8238;'''$v );
                
                
$data$k ] = $v;
            }
        }
    }
    
    
/**
     * Fetch emoticons as JSON for editors, etc
     *
     * @param    string        Directory for emos [optional]
     * @param    bool        Include emoticons not marked clickable
     * @return    string        JSON
     */
    
static public function fetchEmoticonsAsJson$emoDir=''$nonClickable=false )
    {
        
$emoDir    = ( $emoDir ) ? $emoDir ipsRegistry::getClass('output')->skin['set_emo_dir'];
        
$emoArray  = array();
        
$emoString '';
        
$smilie_id 0;

        foreach( 
ipsRegistry::cache()->getCache('emoticons') as $elmo )
        {
            if ( 
$elmo['emo_set'] != $emoDir )
            {
                continue;
            }
            
            if ( ! 
$elmo['clickable'] AND !$nonClickable )
            {
                continue;
            }

            
$smilie_id++;
            
            
//-----------------------------------------
            // Make single quotes as URL's with html entites in them
            // are parsed by the browser, so ' causes JS error :o
            //-----------------------------------------
            
            
if ( strstr$elmo['typed'], "&#39;" ) )
            {
                
$in_delim  '"';
            }
            else
            {
                
$in_delim  "'";
            }
            
            
$emoArray[] = $in_delim addslashes($elmo['typed']) . $in_delim ' : "' $smilie_id ','.$elmo['image'].'"';
        
        }
        
        
//-----------------------------------------
        // Finish up smilies...
        //-----------------------------------------
        
        
if ( count$emoArray ) )
        {
            
$emoString implode",n"$emoArray );
        }
        
        return 
$emoString;
    }
    
    
/**
     * Fetch bbcode as JSON for editors, etc
     *
     * @return    string        JSON
     */
    
static public function fetchBbcodeAsJson$filter=array() )
    {
        
$bbcodes            = array();
        
$currentBbcodes        ipsRegistry::cache()->getCache('bbcode');
        
        if ( 
$filter['noParseOnly'] == )
        {
            
/* Find no parse codes */
            
$noParse = array();
                
            foreach( 
$currentBbcodes as $bbcode )
            {
                
/* Allowed this BBCode? */
                
if ( $bbcode['bbcode_no_parsing'] )
                {
                    
/* CODE is a special case */
                    
if ( $bbcode['bbcode_tag'] != 'code' )
                    {
                        
$noParse[] = $bbcode['bbcode_tag'];
                    }
                }
            }

            return 
json_encode($noParse);
        }
        
        
/* Normal method */
        
$protectedBbcodes    = array('right''left''center''b''i''u''url''img''quote''indent''snapback',
                                    
'list''strike''sub''sup''email''color''size''font' );
        
        
/* Remove protected bbcodes */
        
foreach( $protectedBbcodes as $_key )
        {
            unset( 
$currentBbcodes$_key ] );
        }
         
        
/* Get all others */
        
foreach( $currentBbcodes as $bbcode )
        {
            if ( 
$bbcode['bbcode_groups'] != 'all' )
            {
                
$pass        false;
                
$groups        array_diffexplode','$bbcode['bbcode_groups'] ), array('') );
                
$mygroups    = array( ipsRegistry::member()->getProperty('member_group_id') );
                
$mygroups    array_diffarray_merge$mygroupsexplode','IPSText::cleanPermStringipsRegistry::member()->getProperty('mgroup_others') ) ) ), array('') );
                
                foreach( 
$groups as $g_id )
                {
                    if( 
in_array$g_id$mygroups ) )
                    {
                        
$pass true;
                        break;
                    }
                }
                
                if ( ! 
$pass )
                {
                    continue;
                }
            }
            
            if ( ! empty( 
$filter['skip'] ) && is_array$filter['skip'] ) )
            { 
                if ( 
in_array$bbcode['bbcode_tag'], $filter['skip'] ) )
                {
                    continue;
                }
            }
            
            
$bbcodes$bbcode['bbcode_tag'] ]    = array(
                                                        
'id'                => $bbcode['bbcode_id'],
                                                        
'title'                => $bbcode['bbcode_title'],
                                                        
'desc'                => $bbcode['bbcode_desc'],
                                                        
'tag'                => $bbcode['bbcode_tag'],
                                                        
'useoption'            => $bbcode['bbcode_useoption'],
                                                        
'example'            => $bbcode['bbcode_example'],
                                                        
'switch_option'        => $bbcode['bbcode_switch_option'],
                                                        
'menu_option_text'    => $bbcode['bbcode_menu_option_text'],
                                                        
'menu_content_text'    => $bbcode['bbcode_menu_content_text'],
                                                        
'single_tag'        => $bbcode['bbcode_single_tag'],
                                                        
'optional_option'    => $bbcode['bbcode_optional_option'],
                                                        
'aliases'            => explode','$bbcode['bbcode_aliases'] ),
                                                        
'image'                => $bbcode['bbcode_image'],
                                                        );
        }
        
        return 
IPSText::simpleJsonEncode($bbcodes);
    }

    
/**
     * Runs the specified member sync module, takes a variable number of arguments.
     *
     * @param    string    $module        The module to run, ex: onCreateAccount, onRegisterForm, etc
     * @param    mixed    ...            Remaining params should match the module being called. ex: array of member data for onCreateAccount,
     *                                or an id and email for onEmailChange
     * @return    @e void
     */
    
static public function runMemberSync$module )
    {
        
/* ipsRegistry::$applications only contains apps with a public title #15785 */
        
$app_cache ipsRegistry::cache()->getCache('app_cache');
        
        
/* Params */
        
$params func_get_args();
        
array_shift$params );

        
/* Loop through applications */
        
foreach( $app_cache as $app_dir => $app )
        {
            
/* Only if app enabled... */
            
if ( $app['app_enabled'] )
            {
                
/* Setup */
                
$_file  self::getAppDir$app['app_directory'] ) . '/extensions/memberSync.php';
                
                
/* Check for the file */
                
if ( is_file$_file ) )
                {
                    
/* Get the file */
                    
$_class self::loadLibrary$_file$app['app_directory'] . 'MemberSync'$app['app_directory'] );
                    
                    
/* Check for the class */
                    
if ( class_exists$_class ) )
                    {
                        
/* Create an object */
                        
$_obj = new $_class();

                        
/* Check for the module */
                        
if ( method_exists$_obj$module ) )
                        {
                            
/* Call it */
                            
call_user_func_array( array( $_obj$module ), $params );
                            
IPSDebug::addLogMessage$app_dir '-' $module'mem' );
                        }
                    }
                }
            }
        }
                
        
/* IPS Connect Group Change */
        
if ( $module == 'onCompleteAccount' )
        {
            
$classToLoad IPSLib::loadLibraryIPS_ROOT_PATH 'sources/handlers/han_login.php''han_login' );
            
$han_login   = new $classToLoadipsRegistry::instance() );
            
$han_login->init();
            
$han_login->validateAccount$params[0] );
        }
    }

    
/**
     * Pick the highest number from an array
     * Used in classItemMarking.. figured it might be useful elsewhere...
     *
     * @param    array         Array of numbers
     * @return    integer        Highest number in the array
     */
    
static public function fetchHighestNumber$array )
    {
        if ( 
is_array$array ) )
        {
            
$_array = array();

            foreach( 
$array as $number )
            {
                
$_array[] = intval$number );
            }

            
sort$_array );

            return 
intvalarray_pop$_array ) );
        }
        else
        {
            return 
0;
        }
    }

    
/**
     * Hand-rolled 'is_writable' function to overcome
     * annoyances with the PHP in built version
     * Based on user notes at php.net (is_writable function comments)
     *
     * @param    string        Path to check
     * @return    boolean
     */
    
static public function isWritable$path )
    {
        if ( 
substr$path, -) == '/' )
        {
            return 
self::isWritable$path uniqidmt_rand() ) . '.tmp');
        }
        else if ( 
is_dir$path ) )
        {
            return 
self::isWritable$path.'/'.uniqidmt_rand() ) . '.tmp');
        }

        
$e file_exists$path );
        
$f = @fopen$path'a' );

        if ( 
$f === FALSE )
        {
            return 
FALSE;
        }

        
fclose ($f );

        if ( 
$e === FALSE )
        {
            
unlink($path);
        }

        return 
TRUE;
    }

    
/**
     * Acts like PHPs next() but if the pointer is at the end of the array or it finds a false
     * value, then it rewinds the array and starts over
     *
     * @param    array         Reference to an array
     * @return    mixed        Next value in the array
     */
    
static public function next( &$array )
    {
        if ( ! 
is_array$array ) )
        {
            return 
FALSE;
        }

        
$next next$array );

        if ( ! 
$next || $next === FALSE )
        {
            
reset$array );
            
$next next$array );
        }

        return 
$next;
    }
    
    
/**
     * Function to naturally sort an array by keys
     *
     * @param    array         Array to sort
     * @return    array         Sorted array
     */
    
static public function knatsort$array )
    {
        
$_a array_keys$array );
        
$_b = array();
        
        
natsort$_a );
        
        foreach( 
$_a as $__a )
        {
            
$_b$__a ] = $array$__a ];
        }
        
        return 
$_b;
    }

    
/**
     * Merges arrays like array_merge_recursive but replaces indentical keys
     *
     * @param    array         Array to merge
     * @param    array         Array to merge
     * @param    array         ...
     * @return    array         Merged array
     */
    
static public function arrayMergeRecursive()
    {
        
$arrays func_get_args();
          
$base   array_shift$arrays );
          
          if ( ! 
is_array($base) )
          {
              
$base = empty($base) ? array() : array($base);
          }
          
          foreach( 
$arrays as $append )
          {
            if ( ! 
is_array$append ) )
            {
                
$append = array( $append );
            }
            
            foreach( 
$append as $key => $value)
            {
                  if ( ! 
array_key_exists$key$base ) and ! is_numeric$key ) )
                  {
                    
$base[$key] = $append[$key];
                    continue;
                  }
                  
                  if ( 
is_array($value) or is_array$base[$key] ) )
                  {
                    
$base[$key] = self::arrayMergeRecursive$base[$key], $append[$key] );
                  }
                  else if ( 
is_numeric$key ) )
                  {
                    if ( ! 
in_array$value$base ) )
                    {
                        
$base[] = $value;
                    }
                  }
                  else
                  {
                       
$base[$key] = $value;
                  }
            }
          }
          
         return 
$base;
    }
    
    
/**
     * Merge two arrays.  Unlike array_merge, however, duplicate keys are ignored rather than overwritten.
     *
     * @param    array
     * @param    array
     * @return    array
     */
    
static public function mergeArrays$array1$array2 )
    {
        
$returnArray    $array1;
        
        foreach( 
$array2 as $_key => $_value )
        {
            if( !
array_key_exists$_key$returnArray ) )
            {
                
$returnArray$_key ]    = $_value;
            }
        }
        
        return 
$returnArray;
    }
    
    
/**
     * arraySearchLoose
     *
     * @param    string        "Needle"
     * @param    array         Array of text to search
     * @return    mixed        Key of array, or false on failure
     */
    
static public function arraySearchLoose$needle$haystack )
    {
        if( !
is_array$haystack ) OR !count($haystack) OR ! $needle )
        {
            return 
false;
        }
        
        foreach( 
$haystack as $k => $v )
        {
            if( 
$v AND stripos$v$needle ) !== false )
            {
                return 
$k;
            }
        }
        
        return 
false;
    }
    
    
/**
     * Get the application title.  Uses lang file keys if present.
     *
     * @param    string        application
     * @param    bool        Force public title
     * @return    string        Text to show for application title
     */
    
static public function getAppTitle$app$forcePublic=false )
    {
        if ( ! 
$app )
        {
            return 
'';
        }

        return isset( 
ipsRegistry::getClass('class_localization')->words$app '_display_title' ] ) ? 
                
ipsRegistry::getClass('class_localization')->words$app '_display_title' ] :
                ( ( 
IN_ACP AND !$forcePublic ) ? ipsRegistry::$applications$app ]['app_title'] : ipsRegistry::$applications$app ]['app_public_title'] );
    }

    
/**
     * Generates the app [ -> module ] path. The module is optional, if module is not
     * specified then just the app dir is returned. If this is called from the ACP and module
     * is present, then it'll return modules_admin, otherwise modules_public
     *
     * @param    string        application
     * @param    string        module (optional)
     * @return    mixed        Directory to app or module (or false if error)
     */
    
static public function getAppDir$app$module='' )
    {
        
$location '';

        if ( ! 
$app OR !is_string($app) )
        {
            return 
FALSE;
        }

        
/* Ok, chicken and egg scenario. Applications has not been set up - most likely because
           we're using this function before the caches have been loaded and unpacked.
           So we guess based on folder names.... */
        
if ( ! is_arrayipsRegistry::$applications ) OR ! countipsRegistry::$applications ) OR ! isset( ipsRegistry::$applications$app ] ) )
        {
            
$location self::extractAppLocationKey$app );
        }
        else
        {
            
$location ipsRegistry::$applications$app ]['app_location'];
        }

        
$pathBit IPS_ROOT_PATH 'applications';

        switch ( 
$location )
        {
            default:
            case 
'root':
                
$pathBit .= '/' $app;
            break;
            case 
'ips':
                
$pathBit .= '_addon/ips/' $app;
            break;
            case 
'other':
                
$pathBit .= '_addon/other/' $app;
            break;
        }

        if ( 
$module )
        {
            
$modulesFolder = ( IPS_AREA != 'admin' ) ? 'modules_public' 'modules_admin';
            
            return 
$pathBit "/" $modulesFolder "/" $module;
        }
        else
        {
            return 
$pathBit;
        }
    }
    
    
/**
     * Extracts app_location from app key
     *
     * @param    string        File path
     * @return    string        root, ips, other
     */
    
static public function extractAppLocationKey$app )
    {
        
/* Test core apps first... */
        
if ( is_dirIPS_ROOT_PATH 'applications/' $app ) )
        {
            
$location 'root';
        }
        else if ( 
is_dirIPS_ROOT_PATH 'applications_addon/ips/' $app ) )
        {
            
$location 'ips';
        }
        else
        {
            
$location 'other';
        }
        
        return 
$location;
    }

    
/**
     * Generates the app folder name, either "applications" or "applications_addon"
     *
     * @param    string        application
     * @return    mixed        Directory to app or module (or false if error)
     */
    
static public function getAppFolder$app )
    {
        if ( ! 
$app OR ! isset(ipsRegistry::$applications$app ]) )
        {
            return 
FALSE;
        }

        switch ( 
ipsRegistry::$applications$app ]['app_location'] )
        {
            default:
            case 
'root':
                
$pathBit 'applications';
            break;
            case 
'ips':
                
$pathBit 'applications_addon/ips';
            break;
            case 
'other':
                
$pathBit 'applications_addon/other';
            break;
        }

        return 
$pathBit;
    }
    
    
/**
     * Determines if the application can be searched
     *
     * @param    string    $app    Application key
     * @return    bool
     */
    
static public function appIsSearchable$app$type='search' )
    {
        
/* Init */
        
$_ck   '';
        
        
/* map config */
        
switch( strtolower$type ) )
        {
            default:
            case 
'search':
                
$_ck 'can_search';
            break;
            case 
'vnc':
            case 
'newcontent':
            case 
'viewnewcontent':
                
$_ck 'can_viewNewContent';
            break;
            case 
'active':
            case 
'activecontent':
                
$_ck 'can_activeContent';
            break;
            case 
'usercontent':
            case 
'users':
            case 
'user':
                
$_ck 'can_userContent';
            break;
            case 
'vncwithfollowfilter':
                
$_ck 'can_vnc_filter_by_followed';
            break;
            case 
'vncwithunreadcontent':
                
$_ck 'can_vnc_unread_content';
            break;
            case 
'tags':
                
$_ck 'can_searchTags';
            break;
        }

        
/* got anything? */
        
if ( ! is_arrayself::$_searchConfigs ) OR ! countself::$_searchConfigs ) )
        {
            
$_needRebuild    false;

            foreach( 
ipsRegistry::$applications as $_app => $data )
            {
                
/* use the cache if we can */
                
if ( ! IN_DEV AND isset( ipsRegistry::$applications[$_app]['search'] ) AND is_arrayipsRegistry::$applications[$_app]['search'] ) AND countipsRegistry::$applications[$_app]['search'] ) )
                {
                    if( 
IPSLib::appIsInstalled$_app ) )
                    {
                        
self::$_searchConfigs$_app ] = ipsRegistry::$applications[$_app]['search'];
                    }
                }
                else
                {
                    
$_file IPSLib::getAppDir$_app ) . '/extensions/search/config.php';
                    
                    if ( 
IPSLib::appIsInstalled$_app ) AND is_file$_file ) )
                    {
                        
$CONFIG = array();
                        require( 
$_file );/*noLibHook*/
                        
                        
if ( is_array$CONFIG ) AND count$CONFIG ) )
                        {
                            
self::$_searchConfigs$_app ] = $CONFIG;
                        
                            unset( 
$CONFIG );
                            
                            
$_needRebuild    true;
                        }
                    }
                }
            }
        }
        
        
/* Do we need to rebuild application cache because we checked for search configs manually? */
        
if( $_needRebuild )
        {
            
ipsRegistry::cache()->rebuildCache'app_cache''global' );
        }
            
        
/* return */
        
if ( isset( self::$_searchConfigs$app ] ) AND is_arrayself::$_searchConfigs$app ] ) AND countself::$_searchConfigs$app ] ) )
        {
            return ( 
self::$_searchConfigs$app ][ $_ck ] ) ? true false;
        }
        
        return 
false;
    }

    
/**
     * Checks to see if the given application is currently installed and enabled
     *
     * @param    string    $app
     * @return    bool
     */
    
static public function appIsInstalled$app$checkEnabled=true )
    {
        if ( isset( 
ipsRegistry::$applications[$app] ) )
        {
            if( 
$checkEnabled )
            {
                if( 
ipsRegistry::$applications[$app]['app_enabled'] )
                {
                    return 
TRUE;
                }
            }
            else
            {
                return 
TRUE;
            }
        }

        return 
FALSE;
    }

    
/**
     * Verify an app supports an extension
     *
     * @param    string    Application
     * @param    array    Array of extensions to check
     * @return  bool
     */
    
static public function appSupportsExtension$app$extensions=array() )
    {
        if( !
$app )
        {
            return 
false;
        }
        
        if( !
count($extensions) )
        {
            return 
true;
        }
        
        
$application    ipsRegistry::$applications$app ];
        
        if( 
$application['app_directory'] )
        {
            
$c 0;
            
            foreach( 
$extensions as $e )
            {
                if ( ! empty( 
$application['extensions'][ $e ] ) )
                {
                    
$c++;
                }
            }
            
            if ( 
$c == count$extensions ) )
            {
                return 
true;
            }
        }
        
        return 
false;
    }
    
    
/**
     * Get all enabled applications
     * 
     * @param    array    Array of extensions an application must have to be returned
     * @return  Array
     */
    
static public function getEnabledApplications$extensions=array(), $forceNotFromCache=FALSE )
    {
        
$apps         = array();
        
$extensions is_string$extensions ) ? array( $extensions ) : ( is_array$extensions ) ? $extensions : array() );
        
        static 
$cache    = array();
        
$_key            md5implode','$extensions ) );
        
        if( 
$cache$_key ] and !$forceNotFromCache )
        {
            return 
$cache$_key ];
        }
        
        if ( ! 
is_arrayipsRegistry::$applications ) OR ! countipsRegistry::$applications ) )
        {
            return array();
        }
        
        foreach( 
ipsRegistry::$applications as $appDir => $appData )
        {
            if ( 
self::appIsInstalled$appDirtrue ) )
            {
                if ( 
count($extensions) )
                {
                    if ( 
is_array$appData['extensions'] ) )
                    {
                        
$c 0;
                        
                        foreach( 
$extensions as $e )
                        {
                            if ( ! empty( 
$appData['extensions'][ $e ] ) )
                            {
                                
$c++;
                            }
                        }
                        
                        if ( 
$c == count$extensions ) )
                        {
                            
$apps$appDir ] = $appData;
                        }
                    }
                }
                else
                {
                    
$apps$appDir ] = $appData;
                }
            }
        }
        
        
$cache$_key ]    = $apps;
        
        return 
$cache$_key ];
    }
    
    
/**
     * Check to see if the givem module is currently installed and enabled
     *
     * @param    string    $module    module_key
     * @param    string    [$app]    app_key, current application by default
     * @return    bool
     */
    
static public function moduleIsEnabled$module$app='' )
    {
        
$app $app $app ipsRegistry::$current_application;
        
        if ( !empty( 
ipsRegistry::$modules[$app] ) )
        {
            foreach( 
ipsRegistry::$modules[$app] as $_m )
            {
                if ( 
$_m['sys_module_key'] == $module )
                {
                    return 
$_m['sys_module_visible'] == 1;
                }
            }
        }
        
        
// Undefined, retun true
        
return TRUE;
    }

    
/**
     * Grab max post upload
     *
     * @return    integer    Max post size
     */
    
static public function getMaxPostSize()
    {
        
$max_file_size 16777216;
        
$tmp           0;

        
$_post   = @ini_get('post_max_size');
        
$_upload = @ini_get('upload_max_filesize');

        if ( 
$_upload $_post )
        {
            
$tmp $_post;
        }
        else
        {
            
$tmp $_upload;
        }

        if ( 
$tmp )
        {
            
$max_file_size $tmp;
            unset(
$tmp);

            
preg_match'#^(d+)(w+)$#'strtolower($max_file_size), $match );
            
            if( 
$match[2] == 'g' )
            {
                
$max_file_size intval$max_file_size ) * 1024 1024 1024;
            }
            else if ( 
$match[2] == 'm' )
            {
                
$max_file_size intval$max_file_size ) * 1024 1024;
            }
            else if ( 
$match[2] == 'k' )
            {
                
$max_file_size intval$max_file_size ) * 1024;
            }
            else
            {
                
$max_file_size intval$max_file_size );
            }
        }

        return 
$max_file_size;
    }

    
/**
     * Convert strlen to bytes
     *
     * @param    integer        string length (no chars)
     * @return    integer        Bytes
     * @since    2.0
     */
    
static public function strlenToBytes$strlen=)
    {
        
$dh pow(100);

        return 
round$strlen / ( pow(10240) / $dh ) ) / $dh;
    }

    
/**
     * Takes a number of bytes and formats in k or MB as required
     *
     * @param    string         Size, in bytes
     * @param    boolean        TRUE = no language class avaiable (during start up, debug, etc)
     * @return    string        Size, in MB, KB or bytes, whichever is closest
     */
    
static public function sizeFormat($bytes=""$noLang=FALSE)
    {
        
$retval "";
        
        if ( 
$noLang === FALSE )
        {
            
$lang['sf_gb']    = ipsRegistry::getClass('class_localization')->words['sf_gb']    ? ipsRegistry::getClass('class_localization')->words['sf_gb']    : 'gb';
            
$lang['sf_mb']    = ipsRegistry::getClass('class_localization')->words['sf_mb']    ? ipsRegistry::getClass('class_localization')->words['sf_mb']    : 'mb';
            
$lang['sf_k']     = ipsRegistry::getClass('class_localization')->words['sf_k']     ? ipsRegistry::getClass('class_localization')->words['sf_k']     : 'kb';
            
$lang['sf_bytes'] = ipsRegistry::getClass('class_localization')->words['sf_bytes'] ? ipsRegistry::getClass('class_localization')->words['sf_bytes'] : 'b';
        }
        else
        {
            
$lang['sf_gb']    = 'gb';
            
$lang['sf_mb']    = 'mb';
            
$lang['sf_k']     = 'kb';
            
$lang['sf_bytes'] = 'b';
        }
        
        if ( 
$bytes >= 1073741824 )
        {
            
$retval round($bytes 1073741824 100 ) / 100 $lang['sf_gb'];
        }
        else if (
$bytes >= 1048576)
        {
            
$retval round($bytes 1048576 100 ) / 100 $lang['sf_mb'];
        }
        else if (
$bytes  >= 1024)
        {
            
$retval round($bytes 1024 100 ) / 100 $lang['sf_k'];
        }
        else
        {
            
$retval $bytes $lang['sf_bytes'];
        }

        return 
$retval;
    }

    
/**
     * Makes int based arrays safe
     * XSS Fix: Ticket: 24360 (Problem with cookies allowing SQL code in keys)
     *
     * @param    array        Array
     * @return    array        Array (Cleaned)
     * @since    2.1.4(A)
     */
    
static public function cleanIntArray$array=array() )
    {
        
$return = array();

        if ( 
is_array$array ) and count$array ) )
        {
            foreach( 
$array as $k => $v )
            {
                
$returnintval($k) ] = intval($v);
            }
        }

        return 
$return;
    }

    
/**
     * Loads an interface. Abstracted incase we change location / method
     * of loading an interface
     *
     * @param    string        File name
     * @return    @e void
     * @since    3.0.0
     */
    
static public function loadInterface$filename )
    {
        
//-----------------------------------------
        // Very simple, currently.
        //-----------------------------------------

        
require_once( IPS_ROOT_PATH 'sources/interfaces/' $filename );/*noLibHook*/
    
}
    
    
/**
     * Scale a remote image
     *
     * @param    string        URL
     * @param    int            Max width
     * @param    int            Max height
     * @return    string        width='#' height='#' string
     */
    
static public function getTemplateDimensions$image$width$height )
    {
        if( empty( 
$width ) AND empty( $height ) )
        {
            return;
        }
        
        if( !
$image )
        {
            return;
        }

        
//-----------------------------------------
        // Checking image dimensions via disk instead
        // of http is faster...can we try that..?
        //-----------------------------------------

        
if( strpos$imageipsRegistry::$settings['board_url'] ) === )
        {
            
$image DOC_IPS_ROOT_PATH str_replaceipsRegistry::$settings['board_url'], ''$image );
        }

        
//-----------------------------------------
        // Dimensions
        // If set maxwidth and no maxheight, then we want the script to
        //    reduce based on width only.  And vice-versa.
        //-----------------------------------------
        
        
$maxWidth    = ( $width ) ? intval($width) : 1000000000;
        
$maxHeight    = ( $height ) ? intval($height) : 1000000000;
        
        
//-----------------------------------------
        // Existing dims
        //-----------------------------------------
        
        
$_dims        = @getimagesize$image );

        if( !
$_dims[0] )
        {
            return;
        }
        
        
$_newDims    IPSLib::scaleImage( array( 
                                                
'cur_width'        => $_dims[0],
                                                
'cur_height'    => $_dims[1],
                                                
'max_width'        => $maxWidth,
                                                
'max_height'    => $maxHeight,
                                        )        );

        
//-----------------------------------------
        // Process the tag and return the data
        //-----------------------------------------

        
return " width='{$_newDims['img_width']}' height='{$_newDims['img_height']}'";
    }

    
/**
     * Given current dimensions + max dimensions, return scaled image dimensions constrained to maximums
     *
     * @param    array    Current dimensions + max dimensions
     * @return    array    New image dimensions
     * @since    2.0
     */
    
public static function scaleImage($arg)
    {
        
// max_width, max_height, cur_width, cur_height

        
$ret = array(
                      
'img_width'  => $arg['cur_width'],
                      
'img_height' => $arg['cur_height']
                    );

        if ( 
$arg['cur_width'] > $arg['max_width'] )
        {
            
$ret['img_width']  = $arg['max_width'];
            
$ret['img_height'] = ceil( ( $arg['cur_height'] * ( ( $arg['max_width'] * 100 ) / $arg['cur_width'] ) ) / 100 );
            
$arg['cur_height'] = $ret['img_height'];
            
$arg['cur_width']  = $ret['img_width'];
        }

        if ( 
$arg['cur_height'] > $arg['max_height'] )
        {
            
$ret['img_height']  = $arg['max_height'];
            
$ret['img_width']   = ceil( ( $arg['cur_width'] * ( ( $arg['max_height'] * 100 ) / $arg['cur_height'] ) ) / 100 );
        }

        return 
$ret;
    }
    
    
/**
     * Retrieve all IP addresses a user (or multiple users) have used
     *
     * @param     string        Where clause for ip address
     * @param    string        Defaults to 'All', otherwise specify which tables to check (comma separated)
     * @return    array        Multi-dimensional array of found IP addresses in which sections
     */
    
static public function findIPAddresses$ip_where$tables_to_check='all' )
    {
        
//-----------------------------------------
        // INIT
        //-----------------------------------------

        
$ip_addresses     = array();
        
$tables            = array(
                            
'admin_logs'            => array( 'member_id''ip_address''ctime' ),
                            
'dnames_change'            => array( 'dname_member_id''dname_ip_address''dname_date' ),
                            
'members'                => array( 'member_id''ip_address''joined' ),
                            
'message_posts'            => array( 'msg_author_id''msg_ip_address''msg_date' ),
                            
'moderator_logs'        => array( 'member_id''ip_address''ctime' ),
                            
'posts'                    => array( 'author_id''ip_address''post_date', array( 'pid' ) ),
                            
'member_status_updates'    => array( 'status_author_id''status_author_ip''status_date' ),
                            
'profile_ratings'        => array( 'rating_by_member_id''rating_ip_address''' ),
                            
//'sessions'                => array( 'member_id', 'ip_address', 'running_time' ),
                            
'topic_ratings'            => array( 'rating_member_id''rating_ip_address''' ),
                            
'validating'            => array( 'member_id''ip_address''entry_date' ),
                            
'voters'                => array( 'member_id''ip_address''vote_date', array( 'tid' ) ),
                            
'error_logs'            => array( 'log_member''log_ip_address''log_date' ),
                            );

        
//-----------------------------------------
        // Check apps
        // @see http://forums.invisionpower.com/tracker/issue-16966-members-download-manag/
        //-----------------------------------------
        
        
foreach( ipsRegistry::$applications as $app_dir => $data )
        {
            if( 
is_fileIPSLib::getAppDir$app_dir ) . "/extensions/coreExtensions.php") )
            {
                
$classX IPSLib::loadLibraryIPSLib::getAppDir$app_dir ) . "/extensions/coreExtensions.php"$app_dir '_findIpAddress'$app_dir );
                
                if( 
class_exists$classX ) )
                {
                    
$ipLookup    = new $classXipsRegistry::instance() );
                    
                    if( 
method_exists$ipLookup'getTables' ) )
                    {
                        
$tables        array_merge$tables$ipLookup->getTables() );
                    }
                }
            }
        }

        
//-----------------------------------------
        // Got tables?
        //-----------------------------------------

        
$_tables explode','$tables_to_check );

        if( !
is_array($_tables) OR !count($_tables) )
        {
            return array();
        }

        
//-----------------------------------------
        // Loop through them and grab the IPs
        //-----------------------------------------

        
foreach( $tables as $tablename => $fields )
        {
            if( 
$tables_to_check == 'all' OR in_array$tablename$_tables ) )
            {
                
$extra '';
                
$ids   = array();
                
                if( 
$tablename == 'members' )
                {
                    if( 
$fields[2] )
                    {
                        
$extra    ', ' $fields[2] . ' as date';
                    }
                    
                    if( 
$fields[3] AND is_array($fields[3]) )
                    {
                        
$extra    .= ', ' implode', '$fields[3] );
                    }

                    
ipsRegistry::DB()->build( array(
                                                    
'select'    => $fields[1] . $extra ', member_id'
                                                    
'from'        => $tablename
                                                    
'where'        => $fields[1] . $ip_where,
                                                    
'group'        => 'member_id, ip_address, joined',
                                                    
'order'        => 'joined DESC',
                                                    
'limit'        => array( 250 ),
                                            )        );
                }
                else
                {
                    if( 
$fields[2] )
                    {
                        
$extra    ', c.' $fields[2] . ' as date';
                    }
                    
                    if( 
$fields[3] AND is_array($fields[3]) )
                    {
                        
$extra    .= ', c.' implode', c.'$fields[3] );
                    }
                    
                    
$extra .= ', c.' $fields[1] . ' as ip_address, c.' $fields[0];
                
                    
ipsRegistry::DB()->build( array(
                                                    
'select'    => 'c.' $fields[1] . $extra
                                                    
'from'        => array( $tablename => 'c' ), 
                                                    
'where'        => 'c.' $fields[1] . $ip_where,
                                                    
'order'        => $fields[2] ? 'c.' $fields[2] . ' DESC' 'c.' $fields[0] . ' DESC',
                                                    
'group'        => 'c.' $fields[0] . ', c.' $fields[1],
                                                    
'limit'        => array( 250 ),
                                                    
'add_join'    => array(
                                                                        array(
                                                                            
'select'    => 'm.member_id, m.members_display_name, m.email, m.posts, m.joined',
                                                                            
'from'        => array( 'members' => 'm' ),
                                                                            
'where'        => 'm.member_id=c.' $fields[0],
                                                                            
'type'        => 'left',
                                                                            )
                                                                        )
                                            )        );
                }

                
ipsRegistry::DB()->execute();
                
                
$i 0;
                
                while( 
$r ipsRegistry::DB()->fetch() )
                {
                    if ( 
$r$fields[0] ] )
                    {
                        
$ids[] = $r$fields[0] ];
                    }
                    
                    if( 
$r$fields[1] ] )
                    {
                        
$rawData[ ++$i ]    = $r;
                    }
                }
                
                
/* Get members */
                
$members IPSMember::load$ids'core' );
                
                if ( 
is_array$rawData ) and count $rawData ) )
                {
                    foreach( 
$rawData as $idx => $data )
                    {
                        if ( 
$data$fields[0] ] && is_array$members$data$fields[0] ] ] ) )
                        {
                            
$ip_addresses$tablename ][ $idx ] = array_merge$data$members$data$fields[0] ] ] );
                        }
                    }
                }
            }
        }

        
//-----------------------------------------
        // Here are your IPs kind sir.  kthxbai
        //-----------------------------------------

        
return $ip_addresses;
    }
    
    
/**
     * Display a strip of share links
     *
     * @param    string        Document title (can be left blank and it will attempt to self-discover)
     * @param    array        Addition params: url, cssClass, group [string template group], bit [string template bit], skip [array of share_keys to skip]
     * @return    string        HAITHTEEEMEL
     */
    
static public function shareLinks$title=''$params=array() )
    {
        
$url      = isset( $params['url'] )         ? $params['url']         : '';
        
$cssClass = isset( $params['cssClass'] )    ? $params['cssClass']    : 'topic_share left';
        
$group    = isset( $params['group'] )       ? $params['group']       : 'global';
        
$bit      = isset( $params['bit'] )         ? $params['bit']         : 'shareLinks';
        
$skip     = isset( $params['skip'] )        ? $params['skip']        : array();
        
$override = isset( $params['overrideApp'] ) ? $params['overrideApp'] : '';
        
        
/* Disabled? */
        
if ( ! ipsRegistry::$settings['sl_enable'] )
        {
            return 
'';
        }
        
        
/* Disable for bots */
        
if( ipsRegistry::member()->is_not_human )
        {
            return 
'';
        }
        
        
$canon  ipsRegistry::getClass('output')->fetchRootDocUrl();
        
$url    = ( $url ) ? $url ipsRegistry::$settings['this_url'];
        
$raw    $url;
        
$canon  IPSText::base64_encode_urlSafe( ( $canon ) ? $canon $url );
        
$title  IPSText::base64_encode_urlSafe$title );
        
$url    IPSText::base64_encode_urlSafe$url );
        
        
$cache ipsRegistry::cache()->getCache('sharelinks');
    
        if ( ! 
$cache OR ! is_array$cache ) )
        {
            
ipsRegistry::cache()->rebuildCache('sharelinks''global' );
            
$cache ipsRegistry::cache()->getCache('sharelinks');
        }
        
        
/* Check for required canonical urls or not */
        
foreach( $cache as $key => $data )
        {
            if ( 
is_array$skip ) AND in_array$key$skip ) )
            {
                unset( 
$cache$key ] );
            }
            else
            {
                
$cache$key ]['_rawUrl']     = $raw;
                
$cache$key ]['overrideApp'] = $override;
                
$cache$key ]['_url']        = $data['share_canonical'] ? $canon $url;
            }
        }
        
        return 
ipsRegistry::getClass('output')->getTemplate$group )->$bit$cache$title$canon$cssClass );
    }
    
    
/**
     * Quick function to see if a string is serialized
     * End up using something similar throughout the code
     *
     * @param    string    String to test
     * @return    boolean
     */
    
static public function isSerialized$string )
    {
        if ( ! 
is_string$string ) OR ! trim$string ) )
        {
            return 
false;
        } 
        
        
/* Don't allow objects */
        
if ( preg_match"#^(i|s|a|d):(.*)#si"$string ) !== false )
        {
            return 
true;
        }
        
        return 
false
    }
    
    
/**
    * mixed safe_unserialize(string $serialized)
    * Safely unserialize, that is only unserialize string, numbers and arrays, not objects
    *
    * @license Public Domain
    * @author dcz (at) phpbb-seo (dot) com
    */
    
static public function safeUnserialize$serialized )
    {
        
// unserialize will return false for object declared with small cap o
        // as well as if there is any ws between O and :
        
if ( is_string$serialized ) && strpos$serialized"" ) === false )
        {
            if ( 
strpos$serialized'O:' ) === false )
            {
                 
// the easy case, nothing to worry about
                 // let unserialize do the job
                 
return @unserialize$serialized );
             }
             else if ( ! 
preg_match('/(^|;|{|})O:[+-0-9]+:"/'$serialized ) )
             {
                 
// in case we did have a string with O: in it,
                 // but it was not a true serialized object
                 
return @unserialize$serialized );
             }
         }
         
         return 
false;
    }
    
    
/**
     * Check for an IPv4 address
     * 
     * @param    string    IP address
     * @return    boolean
     */
    
static public function validateIPv4$IP 
    {        
        
        if (
function_exists('filter_var'))
        {
        return ( 
filter_var$IPFILTER_VALIDATE_IPFILTER_FLAG_IPV4 ) ) ? true false
    } 
        else
        {
            
preg_match'/^(d{1,3}).(d{1,3}).(d{1,3}).(d{1,3})$/'$IP$match );
            return ( 
$match[0] ) ? true false
        }
    } 
    
    
/**
     * Check an IPv6 address
     *
     * @link    http://crisp.tweakblogs.net/blog/3049/ipv6-validation-more-caveats.html
     * @param    string    IP address
     * @return    boolean
     */
    
static public function validateIPv6$IP 
    { 
        if ( 
strlen($IP) < )
        {
            return 
$IP == '::';
        }
    
        if ( 
strpos$IP'.' ) ) 
        { 
            
$lastcolon strrpos($IP':');
             
            if ( ! ( 
$lastcolon && self::validateIPv4substr$IP$lastcolon 1) ) ) )
            {
                return 
false
            }
            
            
$IP substr$IP0$lastcolon ) . ':0:0'
        } 
    
        if ( 
strpos$IP'::' ) === false 
        { 
            return 
preg_match('/A(?:[a-f0-9]{1,4}:){7}[a-f0-9]{1,4}z/i'$IP); 
        } 
    
        
$colonCount substr_count($IP':');
        
        if ( 
$colonCount 
        { 
            return 
preg_match('/A(?::|(?:[a-f0-9]{1,4}:)+):(?:(?:[a-f0-9]{1,4}:)*[a-f0-9]{1,4})?z/i'$IP); 
        } 
    
        
// special case with ending or starting double colon 
        
if ( $colonCount == 
        { 
            return 
preg_match('/A(?:::)?(?:[a-f0-9]{1,4}:){6}[a-f0-9]{1,4}(?:::)?z/i'$IP); 
        } 
    
        return 
false
    }
    
    
/**
     * Has an active license or not
     * @param    boolean    Allow perpetual license to count.
     * @return boolean
     */
    
static public function hasActiveLicense$allowPerpetual=true )
    {
        
//return true;
        
$licenseData ipsRegistry::cache()->getCache'licenseData' );

        if ( ( ! 
$licenseData OR ! $licenseData['key']['_expires'] OR $licenseData['key']['_expires'] < IPS_UNIX_TIME_NOW OR $licenseData['key']['_expires'] == 9999999999 ) )
        {
            return 
false;
        }
        
        if ( 
$allowPerpetual !== true )
        {
            if ( 
stristr$licenseData['name'], 'perpetual' ) )
            {
                return 
false;
            }
        }
        
        return 
true;
    }
    
    
/**
     * Is the archive DB on another server?
     * @return boolean
     */
    
static public function isUsingRemoteArchiveDB()
    {
        return ( 
ipsRegistry::$settings['archive_remote_sql_database'] && ipsRegistry::$settings['archive_remote_sql_user'] ) ? true false;
    }
}

/**
* IPSDebug
*
* Only useful when developing
*/
class IPSDebug
{
    
/**
     * Memory debug array
     *
     * @var        array         Memory debug info
     */
    
static public $memory_debug = array();

    
/**
     * Messages
     *
     * @var        array         Messages
     */
    
static protected $_messages = array();

    
/**
     * Turn off constructor
     *
     * @return    @e void
     */
    
private function __construct() {}

    
/**
     * Start time
     *
     * @var        integer        Start time
     */
    
static protected $_starttime;

    
/**
     * Add message
     *
     * @param    string
     * @return    @e void
     */
    
static public function addMessage$message )
    {
        
self::$_messages[] = $message;
    }
    
    
/**
     * View any string as a hexdump.
     *
     * This is most commonly used to view binary data from streams
     * or sockets while debugging, but can be used to view any string
     * with non-viewable characters.
     *
     * @version     1.3.2
     * @author      Aidan Lister <aidan@php.net>
     * @author      Peter Waller <iridum@php.net>
     * @link        http://aidanlister.com/2004/04/viewing-binary-data-as-a-hexdump-in-php/
     * @param       string  $data        The string to be dumped
     * @param       bool    $htmloutput  Set to false for non-HTML output
     * @param       bool    $uppercase   Set to true for uppercase hex
     * @param       bool    $return      Set to true to return the dump
     */
    
static public function hexdump$data$htmloutput true$uppercase false$return false )
    {
        
// Init
        
$hexi '';
        
$ascii '';
        
$dump = ( $htmloutput === true ) ? '<pre>' '';
        
$offset 0;
        
$len strlen$data );
        
        
// Upper or lower case hexadecimal
        
$x = ( $uppercase === false ) ? 'x' 'X';
        
        
// Iterate string
        
for( $i $j $i $len $i ++ )
        {
            
// Convert to hexidecimal
            
$hexi .= sprintf"%02$x "ord$data[$i] ) );
            
            
// Replace non-viewable bytes with '.'
            
if ( ord$data[$i] ) >= 32 )
            {
                
$ascii .= ( $htmloutput === true ) ? htmlentities$data[$i] ) : $data[$i];
            }
            else
            {
                
$ascii .= '.';
            }
            
            
// Add extra column spacing
            
if ( $j === )
            {
                
$hexi .= ' ';
                
$ascii .= ' ';
            }
            
            
// Add row
            
if ( ++ $j === 16 || $i === $len )
            {
                
// Join the hexi / ascii output
                
$dump .= sprintf"%04$x  %-49s  %s"$offset$hexi$ascii );
                
                
// Reset vars
                
$hexi $ascii '';
                
$offset += 16;
                
$j 0;
                
                
// Add newline
                
if ( $i !== $len )
                {
                    
$dump .= "n";
                }
            }
        }
        
        
// Finish dump
        
$dump .= $htmloutput === true '</pre>' '';
        
$dump .= "n";
        
        
// Output method
        
if ( $return === false )
        {
            echo 
$dump;
        }
        else
        {
            return 
$dump;
        }
    }
    
    
/**
     * Print a var in plain mode
     * @param mixed $var
     */
    
static public function plainPrint$var )
    {
        
header'Content-type: text/plain; charset=UTF-8');
        print 
var_export$vartrue );
        exit();
    }
    
    
/**
     * Send a FirePHP message
     *
     * @param    string    $method        Method to call
     * @param    string    $vars        Parameters to pass
     * @return    @e void
     * @link    http://www.firephp.org/HQ/
     */
    
static public function fireBug$method$parameters=array() )
    {
        if ( 
IN_DEV )
        {
            try
            {
                if( !
class_exists'FB' ) )
                {
                    require_once( 
IPS_KERNEL_PATH '/FirePHPCore/fb.php' );/*noLibHook*/
                
}
                
                if( 
$method == 'registerExceptionHandler' )
                {
                    
$firephp FirePHP::getInstance(true);
                    
$firephp->registerExceptionHandler();
                }
                
                if( 
$method == 'registerErrorHandler' )
                {
                    
$firephp FirePHP::getInstance(true);
                    
$firephp->registerErrorHandler();
                }
    
                if( 
method_exists'FB'$method ) )
                {
                    
$function    'FB::' $method;
    
                    
call_user_func_array$function$parameters );
                }
            }
            catch( 
Exception $e ) { }
        }
    }
    
    
/**
     * Prettify a debug backtrace
     *
     * @param    array        Data from backtrace
     * @return    string
     */
    
static public function prettifyBackTrace$debug )
    {
        
$_dString '';

        if ( 
is_array$debug ) and count$debug ) )
        {
            foreach( 
$debug as $idx => $data )
            {
                
/* Remove non-essential items */
                
if ( $data['class'] == 'dbMain' OR $data['class'] == 'ips_DBRegistry' OR $data['class'] == 'ipsRegistry' OR $data['class'] == 'ipsController' OR $data['class'] == 'ipsCommand' OR $data['class'] == 'db_driver_mysql' )
                {
                    continue;
                }
                
                
$_dbString$idx ] = array( 'file'     => $data['file'],
                                            
'line'     => $data['line'],
                                            
'function' => $data['function'],
                                            
'class'    => $data['class'] );
            }
        }
        
        if ( 
count$_dbString ) )
        {
            
$_error_string .= "n .--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------.";
            
$_error_string .= "n | File                                                                       | Function                                                                      | Line No.          |";
            
$_error_string .= "n |----------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------|";
            
            foreach( 
$_dbString as $i => $data )
            {
                if ( 
defined('DOC_IPS_ROOT_PATH') )
                {
                    
$data['file'] = str_replaceDOC_IPS_ROOT_PATH''$data['file'] );
                }
                
                
/* Reset */
                
$data['func'] = "[" $data['class'] . '].' $data['function'];
                
                
/* Pad right */
                
$data['file'] = str_pad$data['file'], 75 );
                
$data['func'] = str_pad$data['func'], 78 );
                
$data['line'] = str_pad$data['line'], 18 );
                
                
$_error_string .= "n | " $data['file'] . "| " $data['func'] . '| ' $data['line'] . '|';
                
$_error_string .= "n '----------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------'";
            }
        }
        
        return 
$_error_string;
    }
    
    
/**
     * Custom error handler
     *
     * @param    integer    Error number
     * @param    string    Error string
     * @param    string    Error file
     * @param    string    Error line number
     * @return    @e void
     */
    
static public function errorHandler$errno$errstr$errfile$errline )
    {
        
/* Did we turn off errors with @? */
        
if ( ! error_reporting() )
        {
            return;
        }
        
        
/* Are we truly debugging? */
        
if ( IPS_ERROR_CAPTURE === FALSE )
        {
            return;
        }

        
$errfile str_replace( @getcwd(), ""$errfile );
        
$log     false;
        
$message "> [$errno$errstrn> > Line: $errlinen> > File: $errfile";
        
        
/* What do we have? */
        
switch ($errno)
        {
              case 
E_ERROR:
                
$log true;
                   echo 
"<b>IPB ERROR</b> [$errno$errstr (Line: $errline of $errfile)<br />n";
                   exit(
1);
               break;
              case 
E_WARNING:
                
$log true;
                   echo 
"<b>IPB WARNING</b> [$errno$errstr (Line: $errline of $errfile)<br />n";
               break;
            case 
E_NOTICE:
                   
$log true;
               break;
             default:
                return 
FALSE;
                   
//Do nothing
               
break;
        }
        
        
/* Logging? */
        
if ( $log )
        {
            if ( 
IPS_ERROR_CAPTURE === TRUE )
            {
                
self::addLogMessage$message"phpNotices"falsetrue );
            }
            else
            {
                foreach( 
explode','IPS_ERROR_CAPTURE ) as $class )
                {
                    if ( 
preg_match'#/' preg_quote$class'#' ) . '.#'$errfile ) )
                    {
                        
self::addLogMessage$message"phpNotices"falsetrue );
                        break;
                    }
                }
            }
        }
    }
    
    
/**
     * Add a message to the log file
     * Handy for __destruct stuff, etc
     *
     * @param    string    Message to add
     * @param    string    Which file to add it to
     * @param    mixed    False, or an array of vars to include in log
     * @param    bool    Force log even if IPS_LOG_ALL is off - handy for on-the-fly debugging
     * @param    bool    Unlink file before writing
     * @return    @e void
     */
    
static public function addLogMessage$message$file='debugLog'$array=FALSE$force=FALSE$unlink=FALSE )
    {
        
/* Make sure IN_DEV is on to prevent logs filling up where people forget to turn it off... */
        
if ( ( defined'IPS_LOG_ALL' ) AND IPS_LOG_ALL === TRUE ) OR $force === TRUE )
        {
            if ( 
$unlink === TRUE )
            {
                @
unlinkDOC_IPS_ROOT_PATH 'cache/' $file '.cgi' );
            }
            
            
/* Array to dump? */
            
if ( is_array$array ) )
            {
                
$message .= "n" var_export$arrayTRUE );
            }
            
            
$message "n" str_repeat'-'80 ) . "n> Time: " time() . ' / ' gmdate'r' ) . "n> URL: " $_SERVER['REQUEST_URI'] . "n> " $message;
            @
file_put_contentsDOC_IPS_ROOT_PATH 'cache/' $file '.cgi'$messageFILE_APPEND );
        }
    }

    
/**
     * Return messages
     *
     * @return     array         Stored messages
     */
    
static public function getMessages()
    {
        return 
self::$_messages;
    }

    
/**
     * Displays a templating error
     * Only used when IN_DEV is on
     *
     * @param    string        Complete PHP error string
     * @param    string        Text evaluated by PHP
     * @return    @e void
     */
    
static public function showTemplateError$errorText$evalCode )
    {
        
$output     = array();
        
$count      0;
        
$openDiv    '<div style="width:95%;text-align:left; margin-auto; padding:10px; white-space:pre;border:1px solid black; background:#eee;font-family:'Courier New', Courier, Geneva;font-size:0.8em">';
        
$lineNumber 0;

        
/* Convert text into lines */
        
$evalCode preg_replace"#r#""n"$evalCode );
        
$lines    explode"n"$evalCode );

        if ( 
count$lines ) )
        {
            foreach( 
$lines as $l )
            {
                
$count++;
                
$output$count ] = htmlspecialchars($l);
            }
        }

        
/* Anything we can deal with? */
        
if ( strstr$errorText"eval()'d code" ) )
        {
            
preg_match'#eval()'d code</bon line <b>(d+?)</b>#', $errorText, $matches );

            
if ( $matches[1] )
            {
                
$lineNumber $matches[1];
                
$output$lineNumber ] = "<span style='background:yellow;color:red;font-weight:bold'>" $output$lineNumber ] . "</span>";

                if ( 
$lineNumber 20 )
                {
                    
$_lineNumber $lineNumber 20;
                    
$output$_lineNumber ] = "<a name='line{$lineNumber}'></a>" $output$_lineNumber ];
                }
            }
        }

        if ( 
count$output ) )
        {
            if ( 
$lineNumber )
            {
                print 
"<h4>Parse Error on line: <a href='#line{$lineNumber}'>" $lineNumber "</a></h4>";
            }
            else
            {
                print 
"<h4>" $errorText "</h4>";
            }

            print 
$openDiv;

            foreach( 
$output as $number => $data )
            {
                print 
"<span style='color:#BBB'>".$number."</span>" ' : ' $data "<br />";
            }

            print 
"</div>";

            exit();
        }

        
/* Still here? */
        
print "<h4>" $errorText "</h4>";
        print 
htmlspecialchars$evalCode );
        exit();
    }

    
/**
     * Get current memory usage
     *
     * @return    integer        Current memory usage
     */
    
static public function getMemoryDebugFlag()
    {
        if ( 
IPS_MEMORY_START AND function_exists'memory_get_usage' ) )
        {
            return 
memory_get_usage();
        }
    }

    
/**
     * Set a memory debug flag
     *
     * @param     string        Comment to set
     * @param    integer        Memory usage to compare against
     * @return    int            Memory used
     */
    
static public function setMemoryDebugFlag$comment$init_usage=)
    {
        if ( 
IPS_MEMORY_START AND function_exists'memory_get_usage' ) )
        {
            
$_END  memory_get_usage();
            
$_USED $_END $init_usage;
            
self::$memory_debug[] = array( $comment$_USED );
            return 
$_USED;
        }
    }

    
/**
     * Start a timer
     *
     * @return    @e void
     */
    
static public function startTimer()
    {
        
$mtime microtime ();
        
$mtime explode (' '$mtime);
        
$mtime $mtime[1] + $mtime[0];
        
self::$_starttime $mtime;
    }

    
/**
     * Stop the timer
     *
     * @return    integer        Length of time
     */
    
static public function endTimer()
    {
        
$mtime microtime ();
        
$mtime explode (' '$mtime);
        
$mtime $mtime[1] + $mtime[0];
        
$endtime $mtime;
        
$totaltime round (($endtime self::$_starttime), 5);
        return 
$totaltime;
    }

    
/**
     * Start a timer (return value instead of storing locally)
     *
     * @return    integer        Time
     */
    
static public function startTimerInstance()
    {
        
$mtime microtime true );
        
$mtime explode (' '$mtime);
        
$mtime = isset( $mtime[1] ) ? $mtime[1] + $mtime[0] : $mtime[0];
        return 
$mtime;
    }

    
/**
     * Stop the timer (compare against provided time instead of stored time)
     *
     * @param    integer        Start time
     * @return    integer        Length of time
     */
    
static public function endTimerInstance$startTime=)
    {
        
$mtime microtime true );
        
$mtime explode (' '$mtime);
        
$mtime = isset( $mtime[1] ) ? $mtime[1] + $mtime[0] : $mtime[0];
        
$endtime $mtime;

        
$totaltime round (($endtime $startTime), 5);
        return 
$totaltime;
    }

    
/**
     * Retrieve server load and update cache if appropriate
     *
     * @return    string    Server load
     */
    
static public function getServerLoad()
    {
        
$load_limit            "--";
        
        
//-----------------------------------------
        // Check cache first...
        //-----------------------------------------
        
        
$cache    ipsRegistry::instance()->cache()->getCache('systemvars');

        if( 
$cache['loadlimit'] )
        {
            
$loadinfo    explode"-"$cache['loadlimit'] );
            
            if ( 
intval($loadinfo[1]) > (time() - 30) )
            {
                
//-----------------------------------------
                // Cache is less than 30 secs old, use it
                //-----------------------------------------

                
$server_load_found    1;
                
$load_limit            $loadinfo[0];
            }
        }
            
        
//-----------------------------------------
        // No cache or it's old, check real time
        //-----------------------------------------
        
        
if( !$server_load_found )
        {
            
# @ supressor stops warning in > 4.3.2 with open_basedir restrictions
            
            
if ( @file_exists('/proc/loadavg') )
            {
                if ( 
$fh = @fopen'/proc/loadavg''r' ) )
                {
                    
$data = @fread$fh);

                    @
fclose$fh );
                    
                    
$load_avg    explode" "$data );
                    
$load_limit    trim($load_avg[0]);
                }
            }
            else if( 
strposstrtolowerPHP_OS ), 'win' ) === )
            {
                
/*---------------------------------------------------------------
                | typeperf is an exe program that is included with Win NT,
                |    XP Pro, and 2K3 Server.  It can be installed on 2K from the
                |    2K Resource kit.  It will return the real time processor
                |    Percentage, but will take 1 second processing time to do so.
                |    This is why we shall cache it, and check only every 2 mins.
                |
                |    Can also be obtained from COM, but it's extremely slow...
                ---------------------------------------------------------------*/
                
                
$serverstats = @shell_exec('typeperf "Processor(_Total)% Processor Time" -sc 1');
                
                if( 
$serverstats )
                {
                    
$server_reply    explode"n"str_replace"r"""$serverstats ) );
                    
$serverstats    array_slice$server_reply2);
                    
$statline        explode","str_replace'"'''$serverstats[0] ) );
                    
$load_limit        round$statline[1], );
                }
            }
            else
            {
                if ( 
$serverstats = @exec("uptime") )
                {
                    
preg_match'/(?:averages)?: ([0-9.]+)(,|)[s]+([0-9.]+)(,|)[s]+([0-9.]+)/'$serverstats$load );

                    
$load_limit $load[1];
                }
            }
            
            
$cache['loadlimit']    = $load_limit "-" time();
            
            if( 
$load_limit )
            {
                
ipsRegistry::instance()->cache()->setCache'systemvars'$cache, array( 'array' => ) );
            }
        }
        
        return 
$load_limit;
    }
}

/**
* IPSCookie
*
* This deals with saving and writing cookies
*/
class IPSCookie
{
    
/**
     * Sensitive cookies
     *
     * @var        array         Sensitive cookies
     */
    
static public $sensitive_cookies = array();
    
    
/**
     * Handle cookies internally
     * so that when you SET one it is available to GET in the same process
     *
     * @var        array
     */
    
static protected $_cookiesSet = array();
    
    
/**
     * Set a cookie.
     *
     * Abstract layer allows us to do some checking, etc
     *
     * @param    string        Cookie name
     * @param    string        Cookie value
     * @param    integer        Is sticky flag
     * @param    integer        Number of days to expire cookie in
     * @param    bool        If false, will set a cookie on the entire domain
     * @param    bool        If $local is false, controls if the cookie prefix should be used
     * @return    @e void
     * @since    2.0
     */
    
static public function set$name$value=""$sticky=1$expires_x_days=0$local=TRUE$usePrefix=FALSE )
    {
        
//-----------------------------------------
        // Check
        //-----------------------------------------

        
if ( !empty( ipsRegistry::$settings['no_print_header'] ) )
        {
            return;
        }
        
        
/* Update internal array */
        
self::$_cookiesSet$name ] = $value;
        
        
//-----------------------------------------
        // Auto serialize arrays
        //-----------------------------------------
        
        
if ( is_array$value ) )
        {
            
$value json_encode$value );
        }

        
//-----------------------------------------
        // Set vars
        //-----------------------------------------

        
if ( $sticky == )
        {
            
$expires time() + 60*60*24*365;
        }
        else if ( 
$expires_x_days )
        {
            
$expires time() + ( $expires_x_days 86400 );
        }
        else
        {
            
$expires FALSE;
        }
        
        
//-----------------------------------------
        // Finish up...
        //-----------------------------------------

        
ipsRegistry::$settings['cookie_domain'] =  ipsRegistry::$settings['cookie_domain'] == "" ""  ipsRegistry::$settings['cookie_domain'] ;
        
ipsRegistry::$settings['cookie_path'] =  ipsRegistry::$settings['cookie_path']   == "" "/" ipsRegistry::$settings['cookie_path'] ;
                
        
$_name = ( ( $local or $usePrefix ) ? ipsRegistry::$settings['cookie_id'] : '' ) . $name;
        
$_path = ( $local ipsRegistry::$settings['cookie_path'] : '/' );
        
        if ( 
$local or substripsRegistry::$settings['cookie_domain'], 0) === '.' )
        {
            
$_domain ipsRegistry::$settings['cookie_domain'];
        }
        else
        {
            
$parsedUrl parse_urlipsRegistry::$settings['board_url'] );
            
            
/* Test to make sure we're not using an IP address or hostname (such as localhost, etc) */
            
if ( ! strstr$parsedUrl['host'], '.' ) || IPSLib::validateIPv4$parsedUrl['host'] ) || IPSLib::validateIPv4$parsedUrl['host'] ) )
            {
                
$_domain '';
            }
            else
            {
                
$_domain = array();
                foreach( 
array_reverseexplode'.'$parsedUrl['host'] ) ) as $bit )
                {
                    
$_domain[] = $bit;
                    
                    if ( ! 
in_array$bit, array( 'aero''asia''biz''cat''com''coop''edu''gov''info''int''jobs''mil''mobi''museum''name''net''org''pro''tel''travel''ac''ad''ae''af''ag''ai''al''am''an''ao''aq''ar''as''at''au''aw''ax''az''ba''bb''bd''be''bf''bg''bh''bi''bj''bl''bm''bn''bo''br''bs''bt''bv''bw''by''bz''ca''cc''cd''cf''cg''ch''ci''ck''cl''cm''cn''co''cr''cu''cv''cx''cy''cz''de''dj''dk''dm''do''dz''ec''ee''eg''eh''er''es''et''eu''fi''fj''fk''fm''fo''fr''ga''gb''gd''ge''gf''gg''gh''gi''gl''gm''gn''gp''gq''gr''gs''gt''gu''gw''gy''hk''hm''hn''hr''ht''hu''id''ie''il''im''in''io''iq''ir''is''it''je''jm''jo''jp''ke''kg''kh''ki''km''kn''kp''kr''kw''ky''kz''la''lb''lc''li''lk''lr''ls''lt''lu''lv''ly''ma''mc''md''me''mg''mh''mk''ml''mm''mn''mo''mp''mq''mr''ms''mt''mu''mv''mw''mx''my''mz''na''nc''ne''nf''ng''ni''nl''no''np''nr''nu''nz''om''pa''pe''pf''pg''ph''pk''pl''pm''pn''pr''ps''pt''pw''py''qa''re''ro''rs''ru''rw''sa''sb''sc''sd''se''sg''sh''si''sj''sk''sl''sm''sn''so''sr''st''su''sv''sy''sz''tc''td''tf''tg''th''tj''tk''tl''tm''tn''to''tp''tr''tt''tv''tw''tz''ua''ug''uk''um''us''uy''uz''va''vc''ve''vg''vi''vn''vu''wf''ws''ye''yt''yu''za''zm''zw' ) ) )
                    {
                        break;
                    }
                }
                
                
$_domain '.' implode'.'array_reverse$_domain ) );
            }
        }
       
        
//-----------------------------------------
        // Set the cookie
        //-----------------------------------------

        
if ( in_array$nameself::$sensitive_cookies ) )
        {
            if ( 
PHP_VERSION 5.2 )
            {
                if ( 
ipsRegistry::$settings['cookie_domain'] )
                {
                    @
setcookie$_name$value$expires$_path$_domain '; HttpOnly' );
                }
                else
                {
                    @
setcookie$_name$value$expires$_path );
                }
            }
            else
            {
                @
setcookie$_name$value$expires$_path$_domainNULLTRUE );
            }
        }
        else
        {
            @
setcookie$_name$value$expires$_path$_domain );
        }
    }
    
    
/**
     * Get a cookie.
     * Abstract layer allows us to do some checking, etc
     *
     * @param    string        Cookie name
     * @param    boolean        Use prefix?
     * @return    mixed
     * @since    2.0
     */
    
static public function get$name$usePrefix true )
    {
        
$_name = ( $usePrefix ipsRegistry::$settings['cookie_id'].$name $name );
        
/* Check internal data first */
        
if ( isset( self::$_cookiesSet$name ] ) )
        {
            return 
self::$_cookiesSet$name ];
        }
        else if ( isset( 
$_COOKIE$_name ] ) )
        {
            
$_value $_COOKIE$_name ];
            
            
$_couldBeJson  stripslashesurldecode$_value ) );
            
$_test         json_decode$_couldBeJsontrue );
            
            if ( 
is_array$_test ) )
            { 
                return 
IPSLib::parseIncomingRecursively$_test );
            }
            else
            {
                return 
IPSText::parseCleanValueurldecode$_value ) );
            }
        }
        else
        {
            return 
FALSE;
        }
    }
}

/**
* IPSText
*
* This deals with cleaning and parsing text items.
*/
class IPSText
{
    
/**
     * Class Convert Object
     *
     * @var        object
     */
    
static protected $classConvertCharset;

    
/**
     * Default document character set
     *
     * @var        string        Character set
     */
    
static public $gb_char_set 'UTF-8';

    
/**
     * Remove dodgy control characters?
     *
     * @var        boolean        Remove emulated spaces (e.g. alt+160)
     */
    
static public $strip_space_chr true;

    
/**
     * Classes
     *
     * @var        array
     */
    
static protected $_internalClasses = array();

    
/**
     * Ensure no one can create this as an object
     *
     * @return    @e void
     */
    
private function __construct() {}

    
/**
     * Cleans/gets a file extension
     *
     * @param string $file
     * @return string
     */
    
static public function getFileExtension$string )
    {
        return ( 
strstr$string'.' ) ) ? strtolowerstr_replace"."""substr$stringstrrpos$string'.' ) ) ) ) : strtolower$string );
    }

    
/**
     * Unconvert smilies
     *
     * @param    string        Raw text
     * @return    string        Converted text
     */
    
public static function unconvertSmilies$txt )
    {
        
//-----------------------------------------
        // Unconvert smilies
        //-----------------------------------------

        
$txt str_replace"<#EMO_DIR#>""&lt;#EMO_DIR&gt;"$txt );

        
preg_match_all"#(<img(?:[^>]+?)class=['"]bbc_emoticon["'](?:[^>]+?)alt=['"](.+?)["'](?:[^>]+?)?>)#is"$txt$matches );

        if( 
is_array($matches[1]) AND count($matches[1]) )
        {
            foreach( 
$matches[1] as $index => $value )
            {                
                if ( 
countipsRegistry::cache()->getCache('emoticons') ) > )
                {
                    foreach( 
ipsRegistry::cache()->getCache('emoticons') as $row )
                    {
                        
$_emoCode str_replace'<''&lt;'str_replace'>''&gt;'$row['typed'] ) );
                        
                        if( 
$matches[2][ $index ] == $_emoCode )
                        {
                            
/* We need to make sure emoticons are wrapped in spaces so they are parsed properly */
                            //$txt = str_replace( $value, ' ' . $_emoCode . ' ', $txt );
                            /* We are no longer matching opening/closing "space" so no need to add it */
                            
$txt str_replace$value$_emoCode$txt );
                            continue 
2;
                        }
                    }
                }
            }
        }

        
$txt str_replace"&lt;#EMO_DIR&gt;""<#EMO_DIR#>"$txt );
        
        return 
$txt;
    }
    
    
/**
     * Simple JSON encode for when its not possible to convert data
     * into UTF-8 (for example polls that display the contents, etc)
     * This should only used for light lifting.
     *
     * @param    array   Simple array
     * @return    object
     */
    
static public function simpleJsonEncode$array )
    {
        
$final = array();

        if ( 
is_array$array ) )
        {
            foreach( 
$array as $k => $v )
            {
                
$k str_replace'"''"'$k );
                
                if ( 
is_array$v ) )
                {
                    
$v self::simpleJsonEncode$v );
                }
                else
                {
                    
$v str_replace'"''"'$v );
                    
$v str_replace"n"'n'str_replace"r"''$v ) );
                    
$v '"' $v '"';
                }
                
                
$final[] = '"' $k '":' $v '';
            }
            
            return 
'{' implode","$final ) . '}';
        }
    }
    
    static public function 
jsonEncodeForTemplate$data )
    {
        
/* Using UTF-8 - it's an easy thing */
        
if ( IPS_DOC_CHAR_SET == 'UTF-8' )
        {
            
$return = @json_encode$data );
            return ( 
$return ) ? $return '{}';
        }
        
        if( 
is_array$data ) )
        {
            
/* convert */
            
array_walk_recursive$data, array( 'IPSText''arrayWalkCallbackConvert' ) );
        }

        
$jsonEncoded json_encode$data );
        
        return 
IPSText::convertCharsets$jsonEncoded"UTF-8"IPS_DOC_CHAR_SET );
    }
    
    
/**
     * Get helper classes
     * Used here to allow classes to be loaded and used as-and-when they're needed
     *
     * @param    mixed        Name of item requested
     * @return    object
     */
    
static public function getTextClass$name )
    {
        if ( isset( 
self::$_internalClasses$name ] ) && is_objectself::$_internalClasses$name ] ) )
        {
            return 
self::$_internalClasses$name ];
        }
        else
        {
            switch( 
$name )
            {
                default:
                case 
'bbcode':
                    
$classToLoad IPSLib::loadLibraryIPS_ROOT_PATH "sources/handlers/han_parse_bbcode.php"'parseBbcode' );
                    
$_class                      =  new $classToLoadipsRegistry::instance() );
                    
$_class->allow_update_caches 1;
                    
$_class->bypass_badwords     ipsRegistry::instance()->member() ? intvalipsRegistry::instance()->member()->getProperty('g_bypass_badwords') ) : 0;
                break;
                case 
'email':
                    
$classToLoad IPSLib::loadLibraryIPS_ROOT_PATH "sources/handlers/han_email.php"'hanEmail' );
                    
$_class = new $classToLoadipsRegistry::instance() );
                    
$_class->init();
                break;
            }

            if ( 
is_object$_class ) )
            {
                
self::$_internalClasses$name ] = $_class;

                return 
self::$_internalClasses$name ];
            }
        }
    }
    
    
/**
     * Encode for saving data into the DB that will be exported to XML
     *
     * Mostly used to ensure that data designed for UTF-8 XML files is actually stored as UTF-8 from
     * 'flat' files that may not be saved as UTF-8.
     *
     * @param    string        Data in
     * @return    string        Data out
     */
    
static public function encodeForXml$string )
    {
        if ( 
function_exists'mb_detect_encoding' ) )
        {
            
$encoding mb_detect_encoding$string );
            if ( 
$encoding != 'UTF-8' )
            {
                
$string IPSText::convertCharsets$string$encoding );
            }
        }
        elseif ( 
strtolowerIPS_DOC_CHAR_SET ) == 'utf-8' )
        {
            
$string utf8_encode$string );
        }
        
        return 
$string;
    }

    
/**
     * Convert russian mounth names to english
     *
     * @access    public
     * @param    string        Date
     * @return    string        Converted date
     */
    
static public function monthNameRu2En$text )
    {
        
         return 
str_replace( array('Январь','Февраль','Март','Апрель','Май','Июнь,','Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'), array('January','February','March','April','May','June','July','August','September','October','November','December'), $text );
    }

    static public function 
transliterate$text )
    {
        return 
str_ireplace( array( '%D0%B0''%D0%B1''%D0%B2''%D0%B3''%D0%B4''%D0%B5''%D1%91''%D0%B6''%D0%B7''%D0%B8''%D0%B9''%D0%BA''%D0%BB''%D0%BC''%D0%BD''%D0%BE''%D0%BF''%D1%80''%D1%81''%D1%82''%D1%83''%D1%84''%D1%85''%D1%86''%D1%87''%D1%88''%D1%89''%D1%8D''%D1%8E''%D1%8F''%D1%8B''%D1%8C''%D1%8A''%D0%90''%D0%91''%D0%92''%D0%93''%D0%94''%D0%95''%D0%81''%D0%96''%D0%97''%D0%98''%D0%99''%D0%9A''%D0%9B''%D0%9C''%D0%9D''%D0%9E''%D0%9F''%D0%A0''%D0%A1''%D0%A2''%D0%A3''%D0%A4''%D0%A5''%D0%A6''%D0%A7''%D0%A8''%D0%A9''%D0%AD''%D0%AE''%D0%AF''%D0%AB''%D0%AC''%D0%AA''а''б''в''г''д''е''ё''ж''з''и''й''к''л''м''н''о''п''р''с''т''у''ф''х''ц''ч''ш''щ''э''ю''я''ы''ь''ъ' ), array('a''b''v''g''d''e''yo''zh''z''i''i''k''l''m''n''o''p''r''s''t''u''f''kh''tc''ch''sh''sch''e''iu''ia''y''''''A''B''V''G''D''E''YO''ZH''Z''I''I''K''L''M''N''O''P''R''S''T''U''F''KH''TC''CH''SH''SCH''E''IU''IA''Y''''''a''b''v''g''d''e''yo''zh''z''i''i''k''l''m''n''o''p''r''s''t''u''f''kh''tc''ch''sh''sch''e''iu''ia''y'''''), $text);
    
    }

    static public function 
makeSeoTransliterate$text )
    {
        if  (!
IPB_USE_SEO_TRANSLIT )
        {
            return 
$text;
        }
        
/*if ( IPB_USE_ONLY_ID_FURL ) 
        {
            return  '-';
        }*/
        
if ((is_array($text)) && (count($text)))
        { 
            foreach( 
$text as $k => $v)
            {
                
$text[$k] = self::makeSeoTransliterate$v );
            }
            return 
$text;
                
        }
        else if ( ( 
substr$text0) == '%%' AND substr$text, -) == '%%' ) )
        {
                return 
$text;
        }
        else
        {
            
                        
            
$text self::mbstrtolower($text);
            
            
$text self::transliterate$text );
                
            
//$text = preg_replace('#[^%a-z0-9 ._-]#', '', $text);
                
        
}
        
        return 
$text;
        
    }
    

    
/**
     * Make an SEO title for use in the URL
     * We parse them even if friendly urls are off so that the data is there when you do switch it on
     *
     * @param    string        Raw SEO title or text
     * @return    string        Cleaned up SEO title
     */
    
static public function makeSeoTitle$text )
    {
        if ( ! 
$text )
        {
            return 
'';
        }

        
/* Strip all HTML tags first */
        
$text strip_tags($text);
        
        
/* Remove specific hex characters (/,<,>,#) as it confuses redirect engine */
        
$text preg_replace'#%(2f|3c|3e|23)#i'''$text );
        
        
/* Preserve other %data */
        
$text preg_replace('#%([a-fA-F0-9][a-fA-F0-9])#''-xx-$1-xx-'$text);
        
$text str_replace( array( '%''`' ), ''$text);
        
$text preg_replace('#-xx-([a-fA-F0-9][a-fA-F0-9])-xx-#''%$1'$text);

        
/* Convert accented chars */
        
if ( IPS_DOC_CHAR_SET != 'UTF-8' )
        {
            
/* http://community.invisionpower.com/resources/bugs.html/_/ip-board/i-broke-furls-urls-with-accents-r41236 */
            /* AJAX requests have HTML entities, so convert to accents then romanize */
            
if ( strstr$text'&#' ) )
            {
                
$text html_entity_decode$textENT_NOQUOTES'UTF-8' );
            }
            
            
$text self::convertAccents($text);
        }
        
        
/* Convert it */
        
if ( self::isUTF8$text )  )
        {
            if ( 
function_exists('mb_strtolower') )
            {
                
$text mb_strtolower($text'UTF-8');
            }

            
$text self::utf8Encode$text250 );
        }

        
/* Finish off */
        
$text self::mbstrtolower($text);
        
        if ( 
strtolowerIPS_DOC_CHAR_SET ) == 'utf-8' )
        {
            
$text preg_replace'#&.+?;#'        ''$text );
            
$text preg_replace'#[^%a-z0-9 _-]#'''$text );
        }
        else
        {
            
$text str_replace( array( '&quot;''&amp;'), ''$text );
            
$text preg_replace'#&[#a-z0-9]{2,6};#i'''$text );
            
$text preg_replace'#[^%&#;a-z0-9 _-]#'''$text );
        }
        
        
$text str_replace( array( '`'' ''+''.''?''_''#''&' ), '-'$text );
        
$text preg_replace"#-{2,}#"'-'$text );
        
$text trim($text'-');
        
        
$text self::makeSeoTransliterate$text );
        
        
IPSDebug::addMessage"<span style='color:red'>makeSeoTitle ($text) called</span>" );
        
        return ( 
$text ) ? $text '-';
    }

    
/**
     * Seems like UTF-8?
     * hmdker at gmail dot com {@link php.net/utf8_encode}
     *
     * @param    string        Raw text
     * @return    boolean
     */
    
static public function isUTF8($str) {
        
$c=0$b=0;
        
$bits=0;
        
$len=strlen($str);
        for(
$i=0$i<$len$i++)
        {
            
$c=ord($str[$i]);

            if(
$c 128)
            {
                if((
$c >= 254)) return false;
                elseif(
$c >= 252$bits=6;
                elseif(
$c >= 248$bits=5;
                elseif(
$c >= 240$bits=4;
                elseif(
$c >= 224$bits=3;
                elseif(
$c >= 192$bits=2;
                else return 
false;

                if((
$i+$bits) > $len) return false;

                while( 
$bits )
                {
                    
$i++;
                    
$b ord($str[$i]);
                    if(
$b 128 || $b 191) return false;
                    
$bits--;
                }
            }
        }

        return 
true;
    }

    
/**
     * Converts accented characters into their plain alphabetic counterparts
     *
     * @param    string        Raw text
     * @return    string        Cleaned text
     */
    
static public function convertAccents($string)
    {        
        if ( ! 
preg_match('/[x80-xff]/'$string) )
        {
            return 
$string;
        }
        
        
/* language abstracted? */
        
$romanization IPS_ROOT_PATH 'extensions/romanization.php';
        
$className    'core_romanization';
        
        if ( 
is_file$romanization ) )
        {
            require_once( 
$romanization );
            
$rmz = new $className();

            return 
$rmz->process$string );
        }
        
        if ( 
self::isUTF8$string) )
        {
            
$_chr = array(
                            
/* Latin-1 Supplement */
                            
chr(195).chr(128) => 'Ae'chr(195).chr(129) => 'A',
                            
chr(195).chr(130) => 'A'chr(195).chr(131) => 'A',
                            
chr(195).chr(132) => 'A'chr(195).chr(133) => 'A',
                            
chr(195).chr(135) => 'C'chr(195).chr(136) => 'E',
                            
chr(195).chr(137) => 'E'chr(195).chr(138) => 'E',
                            
chr(195).chr(139) => 'E'chr(195).chr(140) => 'I',
                            
chr(195).chr(141) => 'I'chr(195).chr(142) => 'I',
                            
chr(195).chr(143) => 'I'chr(195).chr(145) => 'N',
                            
chr(195).chr(146) => 'O'chr(195).chr(147) => 'O',
                            
chr(195).chr(148) => 'O'chr(195).chr(149) => 'O',
                            
chr(195).chr(150) => 'Oe'chr(195).chr(153) => 'U',
                            
chr(195).chr(154) => 'U'chr(195).chr(155) => 'U',
                            
chr(195).chr(156) => 'Ue'chr(195).chr(157) => 'Y',
                            
chr(195).chr(159) => 'ss'chr(195).chr(160) => 'a',
                            
chr(195).chr(161) => 'a'chr(195).chr(162) => 'a',
                            
chr(195).chr(163) => 'a'chr(195).chr(164) => 'ae',
                            
chr(195).chr(165) => 'a'chr(195).chr(167) => 'c',
                            
chr(195).chr(168) => 'e'chr(195).chr(169) => 'e',
                            
chr(195).chr(170) => 'e'chr(195).chr(171) => 'e',
                            
chr(195).chr(172) => 'i'chr(195).chr(173) => 'i',
                            
chr(195).chr(174) => 'i'chr(195).chr(175) => 'i',
                            
chr(195).chr(177) => 'n'chr(195).chr(178) => 'o',
                            
chr(195).chr(179) => 'o'chr(195).chr(180) => 'o',
                            
chr(195).chr(181) => 'o'chr(195).chr(182) => 'oe',
                            
chr(195).chr(185) => 'u'chr(195).chr(186) => 'u',
                            
chr(195).chr(187) => 'u'chr(195).chr(188) => 'ue',
                            
chr(195).chr(189) => 'y'chr(195).chr(191) => 'y',
                            
/* Latin Extended-A */
                            
chr(196).chr(128) => 'A'chr(196).chr(129) => 'a',
                            
chr(196).chr(130) => 'A'chr(196).chr(131) => 'a',
                            
chr(196).chr(132) => 'A'chr(196).chr(133) => 'a',
                            
chr(196).chr(134) => 'C'chr(196).chr(135) => 'c',
                            
chr(196).chr(136) => 'C'chr(196).chr(137) => 'c',
                            
chr(196).chr(138) => 'C'chr(196).chr(139) => 'c',
                            
chr(196).chr(140) => 'C'chr(196).chr(141) => 'c',
                            
chr(196).chr(142) => 'D'chr(196).chr(143) => 'd',
                            
chr(196).chr(144) => 'D'chr(196).chr(145) => 'd',
                            
chr(196).chr(146) => 'E'chr(196).chr(147) => 'e',
                            
chr(196).chr(148) => 'E'chr(196).chr(149) => 'e',
                            
chr(196).chr(150) => 'E'chr(196).chr(151) => 'e',
                            
chr(196).chr(152) => 'E'chr(196).chr(153) => 'e',
                            
chr(196).chr(154) => 'E'chr(196).chr(155) => 'e',
                            
chr(196).chr(156) => 'G'chr(196).chr(157) => 'g',
                            
chr(196).chr(158) => 'G'chr(196).chr(159) => 'g',
                            
chr(196).chr(160) => 'G'chr(196).chr(161) => 'g',
                            
chr(196).chr(162) => 'G'chr(196).chr(163) => 'g',
                            
chr(196).chr(164) => 'H'chr(196).chr(165) => 'h',
                            
chr(196).chr(166) => 'H'chr(196).chr(167) => 'h',
                            
chr(196).chr(168) => 'I'chr(196).chr(169) => 'i',
                            
chr(196).chr(170) => 'I'chr(196).chr(171) => 'i',
                            
chr(196).chr(172) => 'I'chr(196).chr(173) => 'i',
                            
chr(196).chr(174) => 'I'chr(196).chr(175) => 'i',
                            
chr(196).chr(176) => 'I'chr(196).chr(177) => 'i',
                            
chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij',
                            
chr(196).chr(180) => 'J'chr(196).chr(181) => 'j',
                            
chr(196).chr(182) => 'K'chr(196).chr(183) => 'k',
                            
chr(196).chr(184) => 'k'chr(196).chr(185) => 'L',
                            
chr(196).chr(186) => 'l'chr(196).chr(187) => 'L',
                            
chr(196).chr(188) => 'l'chr(196).chr(189) => 'L',
                            
chr(196).chr(190) => 'l'chr(196).chr(191) => 'L',
                            
chr(197).chr(128) => 'l'chr(197).chr(129) => 'L',
                            
chr(197).chr(130) => 'l'chr(197).chr(131) => 'N',
                            
chr(197).chr(132) => 'n'chr(197).chr(133) => 'N',
                            
chr(197).chr(134) => 'n'chr(197).chr(135) => 'N',
                            
chr(197).chr(136) => 'n'chr(197).chr(137) => 'N',
                            
chr(197).chr(138) => 'n'chr(197).chr(139) => 'N',
                            
chr(197).chr(140) => 'O'chr(197).chr(141) => 'o',
                            
chr(197).chr(142) => 'O'chr(197).chr(143) => 'o',
                            
chr(197).chr(144) => 'O'chr(197).chr(145) => 'o',
                            
chr(197).chr(146) => 'OE'chr(197).chr(147) => 'oe',
                            
chr(197).chr(148) => 'R'chr(197).chr(149) => 'r',
                            
chr(197).chr(150) => 'R'chr(197).chr(151) => 'r',
                            
chr(197).chr(152) => 'R'chr(197).chr(153) => 'r',
                            
chr(197).chr(154) => 'S'chr(197).chr(155) => 's',
                            
chr(197).chr(156) => 'S'chr(197).chr(157) => 's',
                            
chr(197).chr(158) => 'S'chr(197).chr(159) => 's',
                            
chr(197).chr(160) => 'S'chr(197).chr(161) => 's',
                            
chr(197).chr(162) => 'T'chr(197).chr(163) => 't',
                            
chr(197).chr(164) => 'T'chr(197).chr(165) => 't',
                            
chr(197).chr(166) => 'T'chr(197).chr(167) => 't',
                            
chr(197).chr(168) => 'U'chr(197).chr(169) => 'u',
                            
chr(197).chr(170) => 'U'chr(197).chr(171) => 'u',
                            
chr(197).chr(172) => 'U'chr(197).chr(173) => 'u',
                            
chr(197).chr(174) => 'U'chr(197).chr(175) => 'u',
                            
chr(197).chr(176) => 'U'chr(197).chr(177) => 'u',
                            
chr(197).chr(178) => 'U'chr(197).chr(179) => 'u',
                            
chr(197).chr(180) => 'W'chr(197).chr(181) => 'w',
                            
chr(197).chr(182) => 'Y'chr(197).chr(183) => 'y',
                            
chr(197).chr(184) => 'Y'chr(197).chr(185) => 'Z',
                            
chr(197).chr(186) => 'z'chr(197).chr(187) => 'Z',
                            
chr(197).chr(188) => 'z'chr(197).chr(189) => 'Z',
                            
chr(197).chr(190) => 'z'chr(197).chr(191) => 's',
                            
/* Euro Sign */
                            
chr(226).chr(130).chr(172) => 'E',
                            
/* GBP (Pound) Sign */
                            
chr(194).chr(163) => '' );

            
$string strtr($string$_chr);
        }
        else
        {
            
$_chr      = array();
            
$_dblChars = array();
            
            
/* We assume ISO-8859-1 if not UTF-8 */
            
$_chr['in'] =   chr(128).chr(131).chr(138).chr(142).chr(154).chr(158)
                            .
chr(159).chr(162).chr(165).chr(181).chr(192).chr(193).chr(194)
                            .
chr(195).chr(199).chr(200).chr(201).chr(202)
                            .
chr(203).chr(204).chr(205).chr(206).chr(207).chr(209).chr(210)
                            .
chr(211).chr(212).chr(213).chr(217).chr(218)
                            .
chr(219).chr(220).chr(221).chr(224).chr(225).chr(226).chr(227)
                            .
chr(231).chr(232).chr(233).chr(234).chr(235)
                            .
chr(236).chr(237).chr(238).chr(239).chr(241).chr(242).chr(243)
                            .
chr(244).chr(245).chr(249).chr(250).chr(251)
                            .
chr(252).chr(253).chr(255).chr(191).chr(182).chr(179).chr(166)
                            .
chr(230).chr(198).chr(175).chr(172).chr(188)
                            .
chr(163).chr(161).chr(177);

            
$_chr['out'] = "EfSZszYcYuAAAACEEEEIIIINOOOOUUUUYaaaaceeeeiiiinoooouuuuyyzslScCZZzLAa";

            
$string           strtr$string$_chr['in'], $_chr['out'] );
            
$_dblChars['in']  = array( chr(140), chr(156), chr(196), chr(197), chr(198), chr(208), chr(214), chr(216), chr(222), chr(223), chr(228), chr(229), chr(230), chr(240), chr(246), chr(248), chr(254));
            
$_dblChars['out'] = array('Oe''oe''Ae''Aa''Ae''DH''Oe''Oe''TH''ss''ae''aa''ae''dh''oe''oe''th');
            
$string           str_replace($_dblChars['in'], $_dblChars['out'], $string);
        }
                
        return 
$string;
    }

    
/**
     * Manually utf8 encode to a specific length
     * Based on notes found at php.net
     *
     * @param    string        Raw text
     * @param    int            Length
     * @return    string
     */
    
static public function utf8Encode$string$len=)
    {
        
$_unicode       '';
        
$_values        = array();
        
$_nOctets       1;
        
$_unicodeLength 0;
         
$stringLength   strlen$string );

        for ( 
$i $i $stringLength $i++ )
        {
            
$value ord$string$i ] );

            if ( 
$value 128 )
            {
                if ( 
$len && ( $_unicodeLength >= $len ) )
                {
                    break;
                }

                
$_unicode .= chr($value);
                
$_unicodeLength++;
            }
            else
            {
                if ( 
count$_values ) == )
                {
                    
$_nOctets = ( $value 224 ) ? 3;
                }

                
$_values[] = $value;

                if ( 
$len && ( $_unicodeLength + ($_nOctets 3) ) > $len )
                {
                    break;
                }

                if ( 
count$_values ) == $_nOctets )
                {
                    if ( 
$_nOctets == )
                    {
                        
$_unicode .= '%' dechex($_values[0]) . '%' dechex($_values[1]) . '%' dechex($_values[2]);
                        
$_unicodeLength += 9;
                    }
                    else
                    {
                        
$_unicode .= '%' dechex($_values[0]) . '%' dechex($_values[1]);
                        
$_unicodeLength += 6;
                    }

                    
$_values  = array();
                    
$_nOctets 1;
                }
            }
        }

        return 
$_unicode;
    }
    
    
/**
     * Converts UTF-8 into HTML entities (&#1xxx;) for correct display in browsers
     *
     * @param     string         UTF8 Encoded string
     * @return     string         ..converted into HTML entities (similar to what a browser does with POST)
     */
    
public static function utf8ToEntities($string)
    { 
        
/*
          * @see http://en.wikipedia.org/wiki/UTF-8#Description
          * @link http://community.invisionpower.com/tracker/issue-23681-possible-addition/
          */
        # Four-byte chars
        
$string preg_replace"/([360-364])([200-277])([200-277])([200-277])/e",  "'&#' . ( ( ord('\1') - 240 ) * 262144 + ( ord('\2') - 128 ) * 4096 + ( ord('\3') - 128 ) * 64 + ( ord('\4') - 128 ) ) . ';'"$string );
        
        
/* Three byte chars */
        
$string preg_replace"/([340-357])([200-277])([200-277])/e""'&#'.((ord('\1')-224)*4096 + (ord('\2')-128)*64 + (ord('\3')-128)).';'"$string ); 

        
/* Two byte chars */
        
$string preg_replace("/([300-337])([200-277])/e""'&#'.((ord('\1')-192)*64+(ord('\2')-128)).';'"$string); 

        return 
$string
    }
    
    
/**
     * Strips out all non UTF-8 characters from a string
     * This is best used when you have already converted / got UTF-8 data
     * @param    string    In
     * @return    string    Cleaned
     */
    
public static function stripNonUtf8$string )
    {
        
$string preg_replace('/[x00-x08x10x0Bx0Cx0E-x19x7F]|(?<=^|[x00-x7F])[x80-xBF]+|([xC0xC1]|[xF0-xFF])[x80-xBF]*'.
                               
'|[xC2-xDF]((?![x80-xBF])|[x80-xBF]{2,})|[xE0-xEF](([x80-xBF](?![x80-xBF]))|(?![x80-xBF]{2})|[x80-xBF]{3,})/''?'$string );
        
        
        
$string preg_replace('/xE0[x80-x9F][x80-xBF]|xED[xA0-xBF][x80-xBF]/S','?'$string );
        
        return 
$string;
    }
    
    
/**
     * Decodes long named HTML entities in a string &eacute; without affecting other HTML entities (&lt; etc) etc
     * @param string     Raw string
     * @param string    Converted string
     */
    
public static function decodeNamedHtmlEntities$string )
    {
        
/* Some manual conversions */
        
$manual = array( 'Alpha' => 'A''Beta' => 'B''alpha' => 'a''beta' => 'b' );
        
        foreach( 
$manual as $o => $r )
        {
            
$string str_replace'&' $o ';'$r$string );
        }
        
        
preg_match_all'#&([^;s]+?);#'$string$matches );
        
$entities = array();
        
$notList  = array( 'amp''para''middot''gt''lt' );
        
        foreach( 
$matches[0] as $word )
        {
            if ( 
preg_match'#^&([a-zA-Z]{2,8});$#'$word$match ) )
            {
                if ( ! 
in_array$match[1], $notList ) )
                {
                    
$entities[] = $word;
                }
            }
        }

        
/* -15 is the same as -1 but with Euro plus French and Finnish */
        
$charSet    = ( IPS_DOC_CHAR_SET == 'ISO-8859-1' ) ? 'ISO-8859-15' IPS_DOC_CHAR_SET;
        
$translated = @explode' ', @html_entity_decode( @implode' '$entities ), ENT_NOQUOTES$charSet ) );

        if ( 
is_array$translated ) AND count$translated ) AND count$translated ) == count$entities ) )
        {
            return 
str_replace$entities$translated$string );
        }
        
        return 
$string;
    }
    
    
    
/**
     * Returns an MD5 hash of content which has whitespace stripped.
     * This is used in some classes to determine if content has changed without
     * whitespace changes triggering it.
     *
     * @param    string         Incoming text
     * @return    string        MD5 hash of whitespace stripped content
     */
    
public static function contentToMd5$t )
    {
        return 
md5trimpreg_replace'#[stnr]#'""$t ) ) );
    }

    
/**
     * Replace Recursively
     *
     * @param    string        Text to search in
     * @param    string        Opening text to search for. (Example: <a href=)
     * @param    string        Closing text to search for. (Example: >)
     * @param    mixed        Call back function that handles the replacement. If using a class, pass array( $classname, $function ) THIS MUST BE A STATIC FUNCTION
     * @return    string        Replaced text
     * <code>
     * # We want to replace all instances of <a href="http://www.domain.com"> with <a href="javascript:goLoad('domain.com')">
     * $text = IPSText::replaceRecursively( $text, "<a href=", ">", array( 'myClass', 'replaceIt' ) );
     * class myClass {
     *    static function replaceIt( $text, $openText, $closeText )
     *    {
     *        # $text contains the matched text between the tags, eg: "http://www.domain.com"
     *        # $openText contains the searched for opening, eg: <a href
     *        # $closeText contains the searched for closing, eg: >
     *        # Remove http...
     *        $text = str_replace( 'http://', '', $text )
     *        # Remove quotes
     *         $text = str_replace( array( '"', "'" ), '', $text );
     *        return '"javascript:goLoad('' . $text . '')"';
     *    }
     * }
     * </code>
     */
    
public static function replaceRecursively$text$textOpen$textClose$callBackFunction )
    {
        
//----------------------------------------
        // INIT
        //----------------------------------------

        # Tag specifics
        
$foundOpenText_pointer  0;
        
$foundCloseText_pointer 0;
        
$foundOpenTextRecurse_pointer 0;

        
//----------------------------------------
        // Keep the server busy for a while
        //----------------------------------------

        
while ( == )
        {
            
# Reset pointer
            
$startOfTextAfterOpenText_pointer 0;

            
# See if we have any 'textOpen' at all
            
$foundOpenText_pointer strpos$text$textOpen$foundCloseText_pointer );

            
# No?
            
if ( $foundOpenText_pointer === FALSE )
            {
                break;
            }

            
# Do we have any close text?
            
$foundCloseText_pointer strpos$text$textClose$foundOpenText_pointer );

            
# No?
            
if ( $foundCloseText_pointer === FALSE )
            {
                return 
$text;
            }

            
# Reset pointer for text between the open and close text
            
$startOfTextAfterOpenText_pointer $foundOpenText_pointer strlen$textOpen );

            
# Check recursively
            
$foundOpenTextRecurse_pointer $startOfTextAfterOpenText_pointer;

            while ( 
== )
            {
                
# Got any open text again?
                
$foundOpenTextRecurse_pointer strpos$text$textOpen$foundOpenTextRecurse_pointer );

                
# No?
                
if ( $foundOpenTextRecurse_pointer === FALSE OR $foundOpenTextRecurse_pointer >= $foundCloseText_pointer )
                {
                    break;
                }

                
# Yes! Reset recursive pointer
                
$foundCloseTextRecurse_pointer $foundCloseText_pointer strlen$textClose );

                
# Yes! Reset close normal pointer to next close tag FROM the last found close point
                
$foundCloseText_pointer strpos$text$textClose$foundCloseTextRecurse_pointer );

                
# Make sure we have a closing text
                
if ( $foundCloseText_pointer === FALSE )
                {
                    return 
$text;
                }

                
$foundOpenTextRecurse_pointer += strlen$textOpen );
            }

            
# This is the text between the open text and close text
            
$foundText  substr$text$startOfTextAfterOpenText_pointer$foundCloseText_pointer $startOfTextAfterOpenText_pointer );

            
# Recurse
            
if ( strpos$foundText$textOpen ) !== FALSE )
            {
                
$foundText IPSText::replaceRecursively$foundText$textOpen$textClose$callBackFunction );
            }

            
# Run the call back...
            
$_newText  call_user_func$callBackFunction$foundText$textOpen$textClose );

            
# Run the replacement
            
$text substr_replace$text$_newText$foundOpenText_pointer, ( $foundCloseText_pointer $foundOpenText_pointer ) + strlen$textClose )  );

            
# Reset pointer
            
$foundCloseText_pointer $foundOpenText_pointer strlen($_newText);
        }

        return 
$text;
    }

    
/**
     * Reset Text Classes
     *
     * @param    string        Classname to search for
     * @return    boolean        True if successful, false if not
     */
    
static public function resetTextClass$name )
    {
        if ( ! 
is_objectself::$_internalClasses$name ] ) )
        {
            return 
false;
        }

        switch( 
$name )
        {
            default:
            case 
'bbcode':
                
self::$_internalClasses$name ]->allow_cache_updates    1;
                
self::$_internalClasses$name ]->bypass_badwords        intvalipsRegistry::instance()->member()->getProperty('g_bypass_badwords') );
                
self::$_internalClasses$name ]->parse_smilies            1;
                
self::$_internalClasses$name ]->parse_nl2br            1;
                
self::$_internalClasses$name ]->parse_html            0;
                
self::$_internalClasses$name ]->parse_bbcode            1;
                
self::$_internalClasses$name ]->parsing_section        'post';
                
self::$_internalClasses$name ]->error                    '';
                
self::$_internalClasses$name ]->parsing_mgroup        '';
                
self::$_internalClasses$name ]->parsing_mgroup_others    '';
            break;
        }

        return 
true;
    }

    
/**
     * Clean _GET _POST key
     *
     * @param    string        Key name
     * @return    string        Cleaned key name
     * @since    2.1
     */
    
static public function parseCleanKey($key)
    {
        if ( 
$key === "" )
        {
            return 
"";
        }
        
        
$key htmlspecialcharsurldecode($key) );
        
$key str_replace".."           ""  $key );
        
$key preg_replace'/__(.+?)__/'  ""  $key );
        
$key preg_replace'/^([w.-_]+)$/'"$1"$key );
        
        return 
$key;
    }

    
/**
     * Clean _GET _POST value
     *
     * @param    string        Input
     * @param    bool        Also run postParseCleanValue
     * @return    string        Cleaned Input
     * @since    2.1
     */
    
static public function parseCleanValue$val$postParse=true )
    {
        if ( 
$val == "" )
        {
            return 
"";
        }

        
$val str_replace"&#032;"" "IPSText::stripslashes($val) );

        
# Convert all carriage return combos
        
$val str_replace( array( "rn""nr""r" ), "n"$val );

        
$val str_replace"&"                "&amp;"         $val );
        
$val str_replace"<!--"            "&#60;&#33;--"  $val );
        
$val str_replace"-->"            "--&#62;"       $val );
        
$val str_ireplace"<script"        "&#60;script"   $val );
        
$val str_replace">"                "&gt;"          $val );
        
$val str_replace"<"                "&lt;"          $val );
        
$val str_replace'"'                "&quot;"        $val );
        
$val str_replace"n"            "<br />"        $val ); // Convert literal newlines
        
$val str_replace"$"                "&#036;"        $val );
        
$val str_replace"!"                "&#33;"         $val );
        
$val str_replace"'"                "&#39;"         $val ); // IMPORTANT: It helps to increase sql query safety.

        
if ( IPS_ALLOW_UNICODE )
        {
            
$val preg_replace("/&amp;#([0-9]+);/s""&#\1;"$val );

            
//-----------------------------------------
            // Try and fix up HTML entities with missing ;
            //-----------------------------------------

            
$val preg_replace'/&#(d+?)([^d;])/i'"&#\1;\2"$val );
        }
        
        
//-----------------------------------------
        // Shortcut to auto run other cleaning
        //-----------------------------------------
        
        
if( $postParse )
        {
            
$val    IPSText::postParseCleanValue$val );
        }

        return 
$val;
    }
    
    
/**
     * Clean _GET _POST value after settings loaded
     *
     * @param    string        Input
     * @return    string        Cleaned Input
     * @since    2.1
     */
    
static public function postParseCleanValue($val)
    {
        if ( 
$val == "" )
        {
            return 
"";
        }

        
/* This looks wrong but it's correct. During FURL set up in registry this function is called before settings are loaded
         * and we want to strip hidden chars in this instance, so.. */
        
if ( ! isset( ipsRegistry::$settings['strip_space_chr'] ) OR ipsRegistry::$settings['strip_space_chr'] )
        {
            
$val IPSText::removeControlCharacters$val );
        }

        return 
$val;
    }

    
/**
     * Check email address to see if it seems valid
     *
     * @param    string        Email address
     * @return    boolean
     * @since    2.0
     */
    
static public function checkEmailAddress$email "" )
    {
        
//$email = trim($email);

        //$email = str_replace( " ", "", $email );
        
        //-----------------------------------------
        // Check we're not passing an array
        // (path disclosure prevention)
        //-----------------------------------------
        
if ( is_array$email ) )
        {
            return 
FALSE;
        }

        
//-----------------------------------------
        // Check for more than 1 @ symbol
        //-----------------------------------------
        
if ( substr_count$email'@' ) > )
        {
            return 
FALSE;
        }

        if ( 
preg_match'#[;#nr*'"<>&%!(){}[]?\/s,]#', $email ) )
        {
            return FALSE;
        }
        /* tld increased to 32 characters as per RFC - http://community.invisionpower.com/resources/bugs.html/_/ip-board/ipstextcheckemailaddress-does-not-match-new-2013-tlds-r41518 */
        else if ( preg_match( '/^.+@([?)[a-zA-Z0-9-.]+.([a-zA-Z]{2,32}|[0-9]{1,4})(]?)$/', 
$email) )
        {
            return TRUE;
        }
        else
        {
            return FALSE;
        }
    }
    
    /**
     * Function to trim text around a word or phrase
     *
     * @param    string    
$haystack    Text
     * @param    string    
$needle        Phrase
     * @return    string
     */
    static public function truncateTextAroundPhrase( 
$haystack$needle )
    {
        /* Base on words */
        
$haystack = explode( " ", $haystack );

        if( count( 
$haystack ) > 21 )
        {
            
$_term_at = IPSLib::arraySearchLoose( $needle$haystack );

            if( 
$_term_at - 11 > 0 )
            {
                
$begin = array_splice( $haystack, 0, $_term_at - 11 );
                
                /* The term position will have changed now */
                
$_term_at = IPSLib::arraySearchLoose( $needle$haystack );
            }

            if( 
$_term_at + 11 < count( $haystack ) )
            {
                
$end   = array_splice( $haystack$_term_at + 11, count( $haystack ) );
            }
        }
        else
        {
            
$begin = array();
            
$end   = array();
        }

        
$haystack = implode( " ", $haystack );
        
        if( is_array( 
$begin ) && count( $begin ) )
        {
            
$haystack = '...' . $haystack;
        }
        
        if( is_array( 
$end ) && count( $end ) )
        {
            
$haystack = $haystack . '...';
        }
        
        return 
$haystack;
    }
    
    /**
     * Replaces text with highlighted blocks
     *
     * @param    string        Incoming Content
     * @param    string        HL attribute
     * @return    string        Formatted text
     * @since    2.2.0
     */
    static public function searchHighlight( 
$text$highlight )
    {
        /* No highlight to do (1)? No point in wasting time then.. */
        if ( 
$highlight == '' || ! is_string( $highlight ) )
        {
            return 
$text;
        }
        
        
$highlight  = self::parseCleanValue( urldecode( $highlight ) );
        
        /* No highlight to do (2)? No point in wasting time then.. */
        if ( 
$highlight == '' )
        {
            return 
$text;
        }
        
        /* Init some more vars */
        
$loosematch = 1;//strstr( $highlight, '*' ) ? 1 : 0;
        
$isPhrase   = preg_match( '#("|&(amp;)?quot;)#', $highlight );
        
$keywords   str_replace'*'''str_replace"+"" "str_replace"++""+"str_replace'-'''trim($highlight) ) ) ) );
        
$keywords    str_replace'&quot;'''str_replace'\', '&#092;', str_replace( '&amp;quot;', '', $keywords ) ) );
        
$word_array = array();
        
$endmatch   "(.)?";
        
$beginmatch "(.)?";

        
//-----------------------------------------
        // Get rid of links first...
        //-----------------------------------------
        
        
$_storedUrls = array();
        
        
preg_match_all"/<a href=['"](.+?)["']([^>]*?)>/is"$text$_urls );

        for ( 
$i 0$i count($_urls[0]); $i++ )
        {
            
$_bleh    md5uniqidmicrotime(), true ) );
            
            
$text    str_replace$_urls[0][$i], "--URL::{$_bleh}-- "$text );
            
            
$_storedUrls$_bleh ]    = $_urls[0][$i];
        }

        
//-----------------------------------------
        // Go!
        //-----------------------------------------

        
if ( $keywords )
        {
            if ( 
preg_match("/,(and|or),/i"$keywords) )
            {
                while ( 
preg_match('/s+(and|or)s+/i'$keywords$match) )
                {
                    
$word_array explode" ".$match[1]." ",    $keywords );
                    
$keywords   str_replace$match[0], '',    $keywords );
                }
            }
            else if ( ! 
$isPhrase && strstr$keywords' ' ) )
            {
                
$word_array explode' 'str_replace'  '' '$keywords ) );
            }
            else
            {
                
$word_array[] = $keywords;
            }

            if ( ! 
$loosematch )
            {
                
$beginmatch '(^|s|>|;|])';
                
$endmatch   '(s|,|.|!|<br|&|$)';
            }

            if ( 
is_array($word_array) )
            {
                
/* We'll use this to match against, so we don't break images with the term in the image name */
                
$textForMatch strip_tagsIPSText::getTextClass'bbcode' )->stripAllTags$text ) );
                
                foreach ( 
$word_array as $keywords )
                {
                    
/* We don't want to highlight small words, they're usually noise and it can produce memory errors with single chars being highlighted 
                     * Correction: We don't want to highlight them unless user used double quotes in search term */
                    
if( strpos$highlight'&amp;quot;' ) === false AND strlen$keywords ) < ipsRegistry::$settings['min_search_word'] )
                    {
                        continue;
                    }
                    
                    
/* Make sure we're not trying to process an empty keyword */
                    
if( ! $keywords )
                    {
                        continue;
                    }

                    
preg_match_all"/{$beginmatch}(".preg_quote($keywords'/')."){$endmatch}/is"$textForMatch$matches );

                    for ( 
$i 0$i count($matches[0]); $i++ )
                    {
                        
$text str_replace$matches[0][$i], $matches[1][$i] . "<span class='searchlite'>" $matches[2][$i] . "</span>" $matches[3][$i], $text );
                        
/* Bug #39439 */
                        //$text = str_ireplace( $matches[0][$i], $matches[1][$i] . "<span class='searchlite'>" . $matches[2][$i] . "</span>" . $matches[3][$i], $text );
                    
}
                }
            }
        }

        
//-----------------------------------------
        // Fix links
        //-----------------------------------------
        
        
if( count($_storedUrls) )
        {
            foreach( 
$_storedUrls as $k => $v )
            {
                
$text    str_replace"--URL::{$k}-- "$v$text );
            }
        }

        return 
$text;
    }

    
/**
     * Check a URL to make sure it's not all hacky
     *
     * @param    string        Input String
     * @return    boolean
     * @since    2.1.0
     */
    
static public function xssCheckUrl$url )
    {
        
// This causes problems if people submit bbcode with urlencoded items that are valid
        // e.g.: http://www.google.com/search?q=site%3Aipb3preview.ipslink.com+-%22Viewing+Profile%22
        // %22 gets changed into " and then this fails, even though this is a valid url
        // $url = trim( urldecode( $url ) );
        
$url    trim$url );

        
/* Test for http://%XX */
        
if ( stristr$url'http://%' ) )
        {
            return 
FALSE;
        }
        
        
/* Test for http://&XX */
        
if ( stristr$url'http://&' ) )
        {
            return 
FALSE;
        }

        if ( ! 
preg_match'#^(http|https|news|ftp)://(?:[^<>"]+|[a-z0-9/._- !&#;,%+?:=]+)$#iU'$url ) )
        {
            return 
FALSE;
        }

        return 
TRUE;
    }
    
    
/**
     * Here we can do some generic checking for XSS
     * This should not be considered fool proof, though can provide
     * a centralized point for maintenance and checking
     * @param string $txt
     * @return string
     */
    
static public function xssMakeJavascriptSafe$txt )
    {
        
$txt preg_replace"/(j)avascript/i" "\1&#097;v&#097;script"$txt );
        
//$txt = str_ireplace( "alert"      , "&#097;lert"          , $txt );
        //$txt = preg_replace( "/(b)(e)(h)(a)(v)(i)(o)(r)/is"   , "\1\2\3<b></b>\4\5\6\7\8"          , $txt );
        
$txt preg_replace'/(e)((/*.*?*/)*)x((/*.*?*/)*)p((/*.*?*/)*)r((/*.*?*/)*)e((/*.*?*/)*)s((/*.*?*/)*)s((/*.*?*/)*)i((/*.*?*/)*)o((/*.*?*/)*)n/is' "\1xp<b></b>ressi&#111;n"     $txt );
        
$txt preg_replace'/(e)((\|&#092;)*)x((\|&#092;)*)p((\|&#092;)*)r((\|&#092;)*)e((\|&#092;)*)s((\|&#092;)*)s((\|&#092;)*)i((\|&#092;)*)o((\|&#092;)*)n/is'       "\1xp<b></b>ressi&#111;n"           $txt );
        
$txt preg_replace'/m((\|&#092;)*)o((\|&#092;)*)z((\|&#092;)*)-((\|&#092;)*)b((\|&#092;)*)i((\|&#092;)*)n((\|&#092;)*)d((\|&#092;)*)i((\|&#092;)*)n((\|&#092;)*)g/is'       "moz-<b></b>b&#105;nding"           $txt );
        
$txt str_ireplace"about:"     "&#097;bout:"         $txt );
        
$txt str_ireplace"<body"      "&lt;body"            $txt );
        
$txt str_ireplace"<html"      "&lt;html"            $txt );
        
$txt str_ireplace"document." "&#100;ocument."      $txt );
        
$txt str_ireplace"window."   "wind&#111;w."      $txt );
        
        
$event_handlers    = array( 'mouseover''mouseout''mouseup''mousemove''mousedown''mouseenter''mouseleave''mousewheel',
                                 
'contextmenu''click''dblclick''load''unload''submit''blur''focus''resize''scroll',
                                 
'change''reset''select''selectionchange''selectstart''start''stop''keydown''keyup',
                                 
'keypress''abort''error''dragdrop''move''moveend''movestart''activate''afterprint',
                                 
'afterupdate''beforeactivate''beforecopy''beforecut''beforedeactivate''beforeeditfocus',
                                 
'beforepaste''beforeprint''beforeunload''begin''bounce''cellchange''controlselect',
                                 
'copy''cut''paste''dataavailable''datasetchanged''datasetcomplete''deactivate''drag',
                                 
'dragend''dragleave''dragenter''dragover''drop''end''errorupdate''filterchange''finish',
                                 
'focusin''focusout''help''layoutcomplete''losecapture''mediacomplete''mediaerror''outofsync',
                                 
'pause''propertychange''progress''readystatechange''repeat''resizeend''resizestart''resume',
                                 
'reverse''rowsenter''rowexit''rowdelete''rowinserted''seek''syncrestored''timeerror',
                                 
'trackchange''urlflip',
                                );
        
        foreach( 
$event_handlers as $handler )
        {
            
$txt str_ireplace'on' $handler'&#111;n' $handler$txt );
        }
        
        return 
$txt;
    }
    
    
/**
     * Strip URLs from stuff
     *
     * @param    string        Input string
     * @return    string        Output string
     */
    
static public function stripUrls$txt )
    {
        
/* Start off by attempting to strip <a href=""></a> */
        
$txt preg_replace'#<a(?:[^"']+?)hrefs{0,}=s{0,}("|'|&quot;|&#34;|&#39;|&#034;|&#039;)([^<]+?)</a>#i', "", $txt );
        
        /* Now grab any non linked items */
        
$txt = preg_replace( '#(http|https|news|ftp)://(?:[^<>["s]+|[a-z0-9/._-!&#;,%+?:=]+)#i', "", $txt );
        
        
return $txt;
    }
    
    
/**
     * Returns a cleaned MD5 hash
     *
     * @param    string        Input String
     * @return    string        Parsed string
     * @since    2.1
     */
    
static public function md5Clean$text )
    {
        return 
preg_replace"/[^a-zA-Z0-9]/""" substr$text032 ) );
    }
    
    
/**
     * Convert unicode entities
     *
     * @param    string        Input to convert (in the form of %u00E9 (example))
     * @param    bool        Force to utf-8 (useful if you want to run convertCharsets() on it after)
     * @return    string        UTF-8 (or html entity) encoded content
     */
    
static public function convertUnicode$t$forceUtf8=false )
    {
        
$t rawurldecode$t );
        
        
/* Need this function? */
        
if ( ! strstr$t'%u' ) )
        {
            return 
$t;
        }

        if ( 
strtolower(IPS_DOC_CHAR_SET) == 'utf-8' || $forceUtf8 )
        {
            return 
preg_replace_callback'#%u([0-9A-F]{1,4})#i', array( self'_convertHexToUtf8' ), utf8_encode($t) );
        }
        else
        {
            return 
preg_replace_callback'#%u([0-9A-F]{1,4})#i'create_function'$matches'"return '&#' . hexdec($matches[1]) . ';';" ), $t );
        }
    }
    
    
/**
     * Convert decimal character code to utf-8
     *
     * @param    integer        Character code
     * @return    string        Character
     */
    
static protected function _convertToUtf8$int=)
    {
        
$return '';

        if ( 
$int )
        {
            return 
chr(0);
        }
        else if ( 
$int <= 0x007f )
        {
            
$return .= chr($int);
        }
        else if ( 
$int <= 0x07ff )
        {
            
$return .= chr(0xc0 | ($int >> 6));
            
$return .= chr(0x80 | ($int 0x003f));
        }
        else if ( 
$int <= 0xffff )
        {
            
$return .= chr(0xe0 | ($int  >> 12));
            
$return .= chr(0x80 | (($int >> 6) & 0x003f));
            
$return .= chr(0x80 | ($int  0x003f));
        }
        else if ( 
$int <= 0x10ffff )
        {
            
$return .= chr(0xf0 | ($int  >> 18));
            
$return .= chr(0x80 | (($int >> 12) & 0x3f));
            
$return .= chr(0x80 | (($int >> 6) & 0x3f));
            
$return .= chr(0x80 | ($int  &  0x3f));
        }
        else
        { 
            return 
chr(0);
        }
        
        return 
$return;
    }

    
/**
     * Wrapper for dec_char_ref_to_utf8
     *
     * @param    array        Hex character code
     * @return    string        Character
     */
    
static protected function _convertHexToUtf8$matches )
    {
        return 
self::_convertToUtf8hexdec$matches[1] ) );
    }
    
    
/**
     * Callback function for array_walk_recursive to convert each entry
     *
     * @param    mixed        Value
     * @param    string        Array key
     * @return    void
     */
    
static public function arrayWalkCallbackConvert( &$value$key )
    {
        if( 
is_string($value) )
        {
            
$value IPSText::convertCharsets$valueIPS_DOC_CHAR_SET"UTF-8" );
        }
    }

    
/**
     * Convert a string between charsets
     *
     * @param    string        Input String
     * @param    string        Current char set
     * @param    string        Destination char set
     * @return    string        Parsed string
     * @since    2.1.0
     * @todo     [Future] If an error is set in classConvertCharset, show it or log it somehow
     */
    
static public function convertCharsets$text$original_cset$destination_cset="UTF-8" )
    {
        
define'CONVERT_JSU_TO_ENTITY'true );
        
        
$original_cset    strtolower($original_cset);
        
$destination_cset strtolower$destination_cset );
        
$t                $text;

        
//-----------------------------------------
        // Not the same?
        //-----------------------------------------

        
if ( $destination_cset == $original_cset )
        {
            return 
$t;
        }

        
//-----------------------------------------
        // Are these only latin chars?  If so, no conversion should be needed.
        //-----------------------------------------

        
if( preg_match'/^([a-zA-Z0-9_-.:;[]]+?)$/'$t ) )
        {
            return 
$text;
        }
        
        if ( ! 
is_objectself::$classConvertCharset ) )
        {
            require_once( 
IPS_KERNEL_PATH.'/classConvertCharset.php' );/*noLibHook*/
            
self::$classConvertCharset = new classConvertCharset();
            
            if ( 
ipsRegistry::$settings['charset_conv_method'] == 'mb' AND function_exists'mb_convert_encoding' ) )
            {
                
self::$classConvertCharset->method 'mb';
            }
            else if ( 
ipsRegistry::$settings['charset_conv_method'] == 'iconv' AND function_exists'iconv' ) )
            {
                
self::$classConvertCharset->method 'iconv';
            }
            else if ( 
ipsRegistry::$settings['charset_conv_method'] == 'recode' AND function_exists'recode_string' ) )
            {
                
self::$classConvertCharset->method 'recode';
            }
            else
            {
                
self::$classConvertCharset->method 'internal';
            }
        }
        
        
$text self::$classConvertCharset->convertEncoding$text$original_cset$destination_cset );
        
        
/* Experimental - convert u#### to html entity */
        
if( $destination_cset != 'utf-8' AND $text AND CONVERT_JSU_TO_ENTITY )
        {
            
preg_match_all'#\u([0-9]{4})#ims'$text$matches );
            
            if( 
is_array($matches) AND count($matches) )
            {
                foreach( 
$matches[1] as $k => $v )
                {
                    
$v    "&#" hexdecltrim$v'0' ) )  . ";";
                    
                    
$text    str_replace$matches[0][$k], $v$text );
                }
            }
        }
        
        return 
$text $text $t;
    }

    
/**
     * Truncate a HTML string without breaking HTML entites
     *
     * @param    string        Input String
     * @param    integer        Desired min. length
     * @return    string        Parsed string
     * @since    2.0
     */
    
static public function truncate($text$limit=30)
    {
        
$orig $text;
        
$text str_replace'&amp;' '&#38;'$text );
        
$text str_replace'&quot;''&#34;'$text );
        
$text str_replace'&gt;''&#62;'$text );
        
$text str_replace'&lt;''&#60;'$text );

        
$string_length self::mbstrlen$text );

        if ( 
$string_length $limit)
        {
            
$text trimself::mbsubstr$text0$limit ) ). '...';
        }
        else
        {
            
$text preg_replace"/&(#{0,}([a-zA-Z0-9]+?)?)?$/"''$text );
        }

        
// Let's just use the original string if the truncated one is longer or same length
        
return ( self::mbstrlen$text ) >= $string_length ) ? $orig $text;
    }
    
    
/**
     * MB strtolower
     *
     * @param    string    Input String
     * @return    string    Parsed string
     */
    
static public function mbstrtolower$text )
    {
        if ( 
function_exists('mb_list_encodings') AND function_exists('mb_strtolower') )
        {
            
$valid_encodings = array();
            
$valid_encodings mb_list_encodings();

            if ( 
count($valid_encodings) )
            {
                if ( 
in_arraystrtoupper(IPS_DOC_CHAR_SET), $valid_encodings ) )
                {
                    
$text mb_strtolower$textstrtoupper(IPS_DOC_CHAR_SET) );
                }
            }
            else
            {
                
$text strtolower$text );
            }
        }
        else
        {
            
$text strtolower$text );
        }
        
        return 
$text;
    }
    
    
/**
     * Substr support for this without mb_substr
     *
     * @param    string    Input String
     * @param    integer    Desired min. length
     * @return    string    Parsed string
     * @since    2.0
     */
    
static public function mbsubstr$text$start=0$limit=30 )
    {
        
$text str_replace'&amp;' '&#38;'$text );
        
$text str_replace'&quot;''&#34;'$text );
        
$text str_replace'&gt;''&#62;'$text );
        
$text str_replace'&lt;''&#60;'$text );

        
//-----------------------------------------
        // Got multibyte?
        //-----------------------------------------

        
if( function_exists('mb_list_encodings') )
        {
            
$valid_encodings = array();
            
$valid_encodings mb_list_encodings();

            if( 
count($valid_encodings) )
            {
                if( 
in_arraystrtoupper(IPS_DOC_CHAR_SET), $valid_encodings ) )
                {
                    
$text    mb_substr$text$start$limitstrtoupper(IPS_DOC_CHAR_SET) );
                    
$text    preg_replace"/&(#{0,}([a-zA-Z0-9]+?)?)?$/"''$text );
                    
                    return 
$text;
                }
            }
        }

        
//-----------------------------------------
        // Handrolled method
        //-----------------------------------------
        
        
$string_length self::mbstrlen$text );
        
        
//-----------------------------------------
        // Negative start?
        //-----------------------------------------

        
if( $start )
        {
            
$start    $string_length $start;
        }

        
//-----------------------------------------
        // Do it!
        //-----------------------------------------
        
        
if ( $string_length $limit )
        {
            if( 
strtoupper(IPS_DOC_CHAR_SET) == 'UTF-8' )
            {
                
// Multi-byte support
                //$text = preg_replace('#^(?:[x00-x7F]|[xC0-xFF][x80-xBF]+){0,0}'.
                //           '((?:[x00-x7F]|[xC0-xFF][x80-xBF]+){'.intval($start).','.intval($limit).'}).*#s',
                //           '$1',$text);
                
                /**
                 * @link    http://www.php.net/manual/en/function.substr.php#55107
                 */
                
preg_match_all"/./su"$text$ar );
                
$text    implode""array_slice$ar[0], $start$limit ) );

            }
            else
            {
                
$text substr$text$start$limit );
            }

            
$text preg_replace"/&(#{0,}([a-zA-Z0-9]+?)?)?$/"''$text );
        }
        else
        {
            
$text preg_replace"/&(#{0,}([a-zA-Z0-9]+?)?)?$/"''$text );
        }

        return 
$text;
    }
    
    
/**
     * mb_stripos - uses mb functions if available
     *
     * @param    string    Input String
     * @param    integer    Desired min. length
     * @return    string    Parsed string
     * @since    2.0
     */
    
static public function mbstripos$text$string$start=)
    {
        
// Do we have multi-byte functions?

        
if( function_exists('mb_list_encodings') AND function_exists('mb_stripos') )
        {
            
$valid_encodings = array();
            
$valid_encodings mb_list_encodings();

            if( 
count($valid_encodings) )
            {
                if( 
in_arraystrtoupper(IPS_DOC_CHAR_SET), $valid_encodings ) )
                {
                    return @
mb_stripos$text$string$startstrtoupper(IPS_DOC_CHAR_SET) );
                }
            }
        }

        
// No?  Fallback to normal stripos
        
return stripos$text$string$start );
    }

    
/**
     * Clean a string to remove all non alphanumeric characters
     *
     * @param    string        Input String
     * @param    string        Additional tags
     * @return    string        Parsed string
     * @since    2.1
     */
    
static public function alphanumericalClean$text$additional_tags="" )
    {
        if ( 
$additional_tags )
        {
            
$additional_tags preg_quote$additional_tags"/" );
        }

        return 
preg_replace'/[^a-zA-Z0-9-_' $additional_tags "]/""" $text );
    }

    
/**
     * Get the true length of a multi-byte character string
     *
     * @param    string        Input String
     * @return    integer        String length
     * @since    2.1
     */
    
static public function mbstrlen$t )
    {
        if( 
function_exists'mb_list_encodings' ) )
        {
            
$encodings    mb_list_encodings();

            if( 
in_arraystrtoupper(IPS_DOC_CHAR_SET), array_map'strtoupper'$encodings ) ) )
            {
                return 
mb_strlen$tIPS_DOC_CHAR_SET );
            }
        }

        return 
strlenpreg_replace("/&#([0-9]+);/""-"self::stripslashes$t ) ) );
    }

    
/**
     * Convert text for use in a textarea
     *
     * @param    string        Input String
     * @return    string        Parsed string
     * @since    2.0
     */
    
static public function textToForm$t="" )
    {
        
// Use forward look up to only convert & not &#123;
        //$t = preg_replace("/&(?!#[0-9]+;)/s", '&#38;', $t );

        
$t str_replace"&" "&#38;"  $t );
        
$t str_replace"<" "&#60;"  $t );
        
$t str_replace">" "&#62;"  $t );
        
$t str_replace'"' "&#34;"  $t );
        
$t str_replace"'" '&#039;' $t );

        if ( 
IN_ACP )
        {
            
$t str_replace"\", "&#092;" , $t );
        
}

        return 
$t// A nice cup of?
    
}

    
/**
     * Cleaned form data back to text
     *
     * @param    string    Input String
     * @return    string    Parsed string
     * @since    2.0
     */
    
static public function formToText($t="")
    {
        
$t str_replace"&#38;"  "&"$t );
        
$t str_replace"&#60;"  "<"$t );
        
$t str_replace"&#62;"  ">"$t );
        
$t str_replace"&#34;"  '"'$t );
        
$t str_replace"&#039;" "'"$t );
        
$t str_replace"&#46;&#46;/" "../"$t );

        if ( 
IN_ACP )
        {
            
//$t = str_replace( '\'     , '\\', $t );
            
$t str_replace'&#092;' ,'\', $t );
        }

        return $t;
    }

    /**
     * Attempt to make slashes safe for use in DB (not really needed now?)
     *
     * @param    string    Input String
     * @return    string    Parsed string
     * @since    2.0
     */
    static public function safeslashes($t="")
    {
        return str_replace( '
\', "\\", self::stripslashes($t) );
    }

    /**
     * Remove slashes if magic_quotes enabled
     *
     * @param    string        Input String
     * @return    string        Parsed string
     * @since    2.0
     */
    static public function stripslashes($t)
    {
        if( is_array($t) )
        {
            return $t;
        }

        if ( IPS_MAGIC_QUOTES )
        {
            $t = stripslashes($t);
        }
        
        $t = preg_replace( '
/\(?!&amp;#|?#)/', "&#092;", $t );

        
return $t;
    }

    
/**
     * Strip the attachment tag from data
     *
     * @param    string        Incoming text
     * @return    string        Text with any attach tags removed
     */
    
static public function stripAttachTag$text )
    {
        return 
preg_replace'#[attachment=(d+?):(?:[^]]+?)]#is'''$text );
    }

    
/**
     * Convert text for use in a textarea
     *
     * @param    string        Input String
     * @return    string        Parsed string
     * @since    2.0
     */
    
static public function raw2form($t="")
    {
        
$t str_replace'$'"&#036;"$t);

        if ( 
IPS_MAGIC_QUOTES )
        {
            
$t stripslashes($t);
        }

        
$t preg_replace'/\(?!&amp;#|?#)/'"&#092;"$t );

        
//---------------------------------------
        // Make sure macros aren't converted
        //---------------------------------------

        
$t preg_replace"/<{(.+?)}>/""&lt;{\1}&gt;"$t );

        return 
$t;
    }

    
/**
     * htmlspecialchars including entities
     *
     * @param    string        Input String
     * @return    string        Parsed string
     * @since    2.0
     */
    
static public function htmlspecialchars($t="")
    {
        
// Use forward look up to only convert & not &#123;
        
$t preg_replace("/&(?!#[0-9]+;)/s"'&amp;'$t );
        
$t str_replace"<""&lt;"  $t );
        
$t str_replace">""&gt;"  $t );
        
$t str_replace'"'"&quot;"$t );
        
$t str_replace"'"'&#039;'$t );

        return 
$t// A nice cup of?
    
}

    
/**
     * unhtmlspecialchars including multi-byte characters
     *
     * @param    string        Input String
     * @return    string        Parsed string
     * @since    2.0
     */
    
static public function UNhtmlspecialchars($t="")
    {
        
$t str_replace"&amp;" "&"$t );
        
$t str_replace"&#38;" "&"$t );
        
$t str_replace"&lt;"  "<"$t );
        
$t str_replace"&gt;"  ">"$t );
        
$t str_replace"&quot;"'"'$t );
        
$t str_replace"&#039;""'"$t );
        
$t str_replace"&#39;" "'"$t );
        
$t str_replace"&#33;" "!"$t );
        
$t str_replace"&#34;" '"'$t );
        
$t str_replace"&#036;"'$'$t );
        
        return 
$t;
    }

    
/**
     * Remove leading comma from comma delim string
     *
     * @param    string    Input String
     * @return    string    Parsed string
     * @since    2.0
     */
    
static public function trimLeadingComma($t)
    {
        return 
ltrim$t',' );
    }

    
/**
     * Remove trailing comma from comma delim string
     *
     * @param    string    Input String
     * @return    string    Parsed string
     * @since    2.0
     */
    
static public function trimTrailingComma($t)
    {
        return 
rtrim$t',' );
    }

    
/**
     * Remove dupe commas from comma delim string
     *
     * @param    string    Input String
     * @return    string    Parsed string
     * @since    2.0
     */
    
static public function cleanComma($t)
    {
        return 
preg_replace"/,{2,}/"","$t );
    }

    
/**
     * Clean perm string (wrapper for comma cleaners)
     *
     * @param    string    Input String
     * @return    string    Parsed string
     * @since    2.0
     */
    
static public function cleanPermString($t)
    {
        
$t self::cleanComma($t);
        
$t self::trimLeadingComma($t);
        
$t self::trimTrailingComma($t);

        return 
$t;
    }

    
/**
     * Convert HTML line break tags to n
     *
     * @param    string    Input text
     * @return    string    Parsed text
     * @since    2.0
     */
    
static public function br2nl($t="")
    {
        
//print nl2br(htmlspecialchars($t)).'<br>--------------------------------<br>';
        
$t    str_replace( array( "r""n" ), ''$t );
        
$t    str_ireplace( array( "<br />""<br>" ), "n"$t );
        
//$t = preg_replace( "#(?:n|r)?<br />(?:n|r)?#", "n", $t );
        //$t = preg_replace( "#(?:n|r)?<br>(?:n|r)?#"  , "n", $t );
        //print nl2br(htmlspecialchars($t)).'<br>--------------------------------<br>';
        
return $t;
    }

    
/**
     * Removes control characters (hidden spaces)
     *
     * @param    string    Input String
     * @return    intger    String length
     * @since    2.1
     */
    
static public function removeControlCharacters$t )
    {
        
/* This looks wrong but it's correct. During FURL set up in registry this function is called before settings are loaded
         * and we want to strip hidden chars in this instance, so.. */
        
if ( ! isset( ipsRegistry::$settings['strip_space_chr'] ) OR ipsRegistry::$settings['strip_space_chr'] )
        {
            
/**
             * @see    http://en.wikipedia.org/wiki/Space_(punctuation)
             * @see http://www.ascii.cl/htmlcodes.htm
             */
            
if ( strtolower(IPS_DOC_CHAR_SET) == 'utf-8' )
            {
                
$t str_replace(chr(194).chr(160), chr(32), $t );
                return 
$t;
            }
            else
            {
            
$t str_replacechr(160), ' '$t );
            
$t str_replacechr(173), ' '$t );
            
            
//$t = str_replace( chr(240), ' ', $t );    -> latin small letter eth

            //$t = str_replace( chr(0xA0), "", $t );  //Remove sneaky spaces    Same as chr 160
            //$t = str_replace( chr(0x2004), "", $t );  //Remove sneaky spaces
            //$t = str_replace( chr(0x2005), "", $t );  //Remove sneaky spaces
            //$t = str_replace( chr(0x2006), "", $t );  //Remove sneaky spaces
            //$t = str_replace( chr(0x2009), "", $t );  //Remove sneaky spaces
            //$t = str_replace( chr(0x200A), "", $t );  //Remove sneaky spaces
            //$t = str_replace( chr(0x200B), "", $t );  //Remove sneaky spaces
            //$t = str_replace( chr(0x200C), "", $t );  //Remove sneaky spaces
            //$t = str_replace( chr(0x200D), " ", $t );  //Remove sneaky spaces
            //$t = str_replace( chr(0x202F), " ", $t );  //Remove sneaky spaces
            //$t = str_replace( chr(0x205F), " ", $t );  //Remove sneaky spaces
            //$t = str_replace( chr(0x2060), "", $t );  //Remove sneaky spaces
            //$t = str_replace( chr(0xFEFF), "", $t );  //Remove sneaky spaces
            
}
        }

        return 
$t;
    }

    
/**
     * URL encode safe with IPB FURLS
     * @param string $data
     * @return    string        Data
     */
    
static public function urlencode_furlSafe$data )
    {
        
$data str_replace'/''%2F'$data );
        
$data str_replace'%2B''+'$data );
        
        return 
str_replace'+''%2B'urlencode$data ) );
    }
    
    
/**
    * URL encode safe with IPB FURLS
    * @param string $data
    * @return    string        Data
    */
    
static public function urldecode_furlSafe$data )
    {
        
$data str_replace'%25''%'$data );
        
$data str_replace'%2B''+'$data );
         
        return 
urldecode$data );
    }
    
    
/**
     * Base64 encode for URLs
     *
     * @param    string        Data
     * @return    string        Data
     */
    
static public function base64_encode_urlSafe$data )
    {
        return 
strtrbase64_encode$data ), '+/=''-_,' );
    }
    
    
/**
     * Base64 decode for URLs
     *
     * @param    string        Data
     * @return    string        Data
     */
    
static public function base64_decode_urlSafe$data )
    {
        return 
base64_decodestrtr$data'-_,''+/=' ) );
    }
    
    
/**
     * Returns current emoticon directory...
     * @return string
     */
    
static public function getEmoticonDirectory()
    {
        if ( 
IN_ACP )
        {
            
$image_set ipsRegistry::DB()->buildAndFetch( array( 'select' => 'set_image_dir, set_emo_dir''from' => 'skin_collections''where' => 'set_is_default=1' ) );
    
            return 
$image_set['set_emo_dir'];
        }
        else
        {
            return 
ipsRegistry::getClass('output')->skin['set_emo_dir'];
        }
    }
    
    
/**
     * The crazy crap we have to deal with. User enters squiggly char, it gets turned into &#128; or &Aacute; but there is
     * no matching character in the doc set so we have to try and decode manually
    
     * @param string Raw string
     * @return string    Converted string
     */
    
static public function allowUtf8CharsWhenNotUsingUtf8Doc$string )
    {
        
/* If we're not using UTF-8, lets try and handle encoded data. */
        
if ( IPS_DOC_CHAR_SET != 'UTF-8' )
        {
            
/* First, convert any left over &acute; style chars */
            
preg_match_all'/&amp;([a-zA-Z]{5,8});/'$string$matches );
                
            foreach( 
$matches[1] as $word )
            {
                
$converted str_replace'&''&amp;'self::convertEntity( array( => $word ) ) );
    
                if ( 
$converted  )
                {
                    
$string str_replace'&amp;' $word ';'$converted$string );
                }
            }
                
            
preg_match_all'/&amp;#([0-9]{3,6});/'$string$matches );
                
            foreach( 
$matches[1] as $word )
            {
                if ( 
$word 127 )
                {
                    
$string str_replace'&amp;#' $word ';''&#' $word ';'$string );
                }
            }
        }
    
        return 
$string;
    }
    
    
/**
     * Converts numeric entities to their named equivalents
     *
     * @param    string
     * @return    string
     */
    
static public function convertNumericEntityToNamed$string )
    {
        
/* If we're not using UTF-8, lets try and handle encoded data. */
        
if ( IPS_DOC_CHAR_SET != 'UTF-8' )
        {
            
/* First, convert any left over &acute; style chars */
            
preg_match_all'/&#([0-9]{3,8});/'$string$matches );
                
            foreach( 
$matches[1] as $word )
            {
                if ( 
$word 127 )
                {
                    
$converted self::convertEntity( array( => $word ), true );
                        
                    if ( 
$converted  )
                    {
                        
$string str_replace'&#' $word ';'$converted$string );
                    }
                }
            }
        }
    
        return 
$string;
    }
    
    
/**
     * Function to convert named entities to numeric entities
     *
     * @param    array     $matches    Results from preg_match call
     * @return    string
     * @link    http://www.lazycat.org/php-convert-entities.php
     */
    
static public function convertEntity$matches$flip=false )
    {
        static 
$table = array(/*'quot' => '&#34;','amp' => '&#38;','lt' => '&#60;','gt' => '&#62;',*/'OElig' => '&#338;','oelig' => '&#339;','Scaron' => '&#352;','scaron' => '&#353;','Yuml' => '&#376;',
                
'circ' => '&#710;','tilde' => '&#732;','ensp' => '&#8194;','emsp' => '&#8195;','thinsp' => '&#8201;','zwnj' => '&#8204;','zwj' => '&#8205;','lrm' => '&#8206;','rlm' => '&#8207;',
                
'ndash' => '&#8211;','mdash' => '&#8212;','lsquo' => '&#8216;','rsquo' => '&#8217;','sbquo' => '&#8218;','ldquo' => '&#8220;','rdquo' => '&#8221;','bdquo' => '&#8222;','dagger' => '&#8224;',
                
'Dagger' => '&#8225;','permil' => '&#8240;','lsaquo' => '&#8249;','rsaquo' => '&#8250;','euro' => '&#8364;','fnof' => '&#402;','Alpha' => '&#913;','Beta' => '&#914;','Gamma' => '&#915;',
                
'Delta' => '&#916;','Epsilon' => '&#917;','Zeta' => '&#918;','Eta' => '&#919;','Theta' => '&#920;','Iota' => '&#921;','Kappa' => '&#922;','Lambda' => '&#923;','Mu' => '&#924;','Nu' => '&#925;',
                
'Xi' => '&#926;','Omicron' => '&#927;','Pi' => '&#928;','Rho' => '&#929;','Sigma' => '&#931;','Tau' => '&#932;','Upsilon' => '&#933;','Phi' => '&#934;','Chi' => '&#935;','Psi' => '&#936;',
                
'Omega' => '&#937;','alpha' => '&#945;','beta' => '&#946;','gamma' => '&#947;','delta' => '&#948;','epsilon' => '&#949;','zeta' => '&#950;','eta' => '&#951;','theta' => '&#952;','iota' => '&#953;',
                
'kappa' => '&#954;','lambda' => '&#955;','mu' => '&#956;','nu' => '&#957;','xi' => '&#958;','omicron' => '&#959;','pi' => '&#960;','rho' => '&#961;','sigmaf' => '&#962;','sigma' => '&#963;',
                
'tau' => '&#964;','upsilon' => '&#965;','phi' => '&#966;','chi' => '&#967;','psi' => '&#968;','omega' => '&#969;','thetasym' => '&#977;','upsih' => '&#978;','piv' => '&#982;','bull' => '&#8226;',
                
'hellip' => '&#8230;','prime' => '&#8242;','Prime' => '&#8243;','oline' => '&#8254;','frasl' => '&#8260;','weierp' => '&#8472;','image' => '&#8465;','real' => '&#8476;','trade' => '&#8482;',
                
'alefsym' => '&#8501;','larr' => '&#8592;','uarr' => '&#8593;','rarr' => '&#8594;','darr' => '&#8595;','harr' => '&#8596;','crarr' => '&#8629;','lArr' => '&#8656;','uArr' => '&#8657;',
                
'rArr' => '&#8658;','dArr' => '&#8659;','hArr' => '&#8660;','forall' => '&#8704;','part' => '&#8706;','exist' => '&#8707;','empty' => '&#8709;','nabla' => '&#8711;','isin' => '&#8712;',
                
'notin' => '&#8713;','ni' => '&#8715;','prod' => '&#8719;','sum' => '&#8721;','minus' => '&#8722;','lowast' => '&#8727;','radic' => '&#8730;','prop' => '&#8733;','infin' => '&#8734;',
                
'ang' => '&#8736;','and' => '&#8743;','or' => '&#8744;','cap' => '&#8745;','cup' => '&#8746;','int' => '&#8747;','there4' => '&#8756;','sim' => '&#8764;','cong' => '&#8773;','asymp' => '&#8776;',
                
'ne' => '&#8800;','equiv' => '&#8801;','le' => '&#8804;','ge' => '&#8805;','sub' => '&#8834;','sup' => '&#8835;','nsub' => '&#8836;','sube' => '&#8838;','supe' => '&#8839;','oplus' => '&#8853;',
                
'otimes' => '&#8855;','perp' => '&#8869;','sdot' => '&#8901;','lceil' => '&#8968;','rceil' => '&#8969;','lfloor' => '&#8970;','rfloor' => '&#8971;','lang' => '&#9001;','rang' => '&#9002;',
                
'loz' => '&#9674;','spades' => '&#9824;','clubs' => '&#9827;','hearts' => '&#9829;','diams' => '&#9830;','nbsp' => ' ','iexcl' => '&#161;','cent' => '&#162;','pound' => '&#163;',
                
'curren' => '&#164;','yen' => '&#165;','brvbar' => '&#166;','sect' => '&#167;','uml' => '&#168;','copy' => '&#169;','ordf' => '&#170;','laquo' => '&#171;','not' => '&#172;','shy' => '&#173;',
                
'reg' => '&#174;','macr' => '&#175;','deg' => '&#176;','plusmn' => '&#177;','sup2' => '&#178;','sup3' => '&#179;','acute' => '&#180;','micro' => '&#181;','para' => '&#182;','middot' => '&#183;',
                
'cedil' => '&#184;','sup1' => '&#185;','ordm' => '&#186;','raquo' => '&#187;','frac14' => '&#188;','frac12' => '&#189;','frac34' => '&#190;','iquest' => '&#191;','Agrave' => '&#192;',
                
'Aacute' => '&#193;','Acirc' => '&#194;','Atilde' => '&#195;','Auml' => '&#196;','Aring' => '&#197;','AElig' => '&#198;','Ccedil' => '&#199;','Egrave' => '&#200;','Eacute' => '&#201;',
                
'Ecirc' => '&#202;','Euml' => '&#203;','Igrave' => '&#204;','Iacute' => '&#205;','Icirc' => '&#206;','Iuml' => '&#207;','ETH' => '&#208;','Ntilde' => '&#209;','Ograve' => '&#210;',
                
'Oacute' => '&#211;','Ocirc' => '&#212;','Otilde' => '&#213;','Ouml' => '&#214;','times' => '&#215;','Oslash' => '&#216;','Ugrave' => '&#217;','Uacute' => '&#218;','Ucirc' => '&#219;',
                
'Uuml' => '&#220;','Yacute' => '&#221;','THORN' => '&#222;','szlig' => '&#223;','agrave' => '&#224;','aacute' => '&#225;','acirc' => '&#226;','atilde' => '&#227;','auml' => '&#228;',
                
'aring' => '&#229;','aelig' => '&#230;','ccedil' => '&#231;','egrave' => '&#232;','eacute' => '&#233;','ecirc' => '&#234;','euml' => '&#235;','igrave' => '&#236;','iacute' => '&#237;',
                
'icirc' => '&#238;','iuml' => '&#239;','eth' => '&#240;','ntilde' => '&#241;','ograve' => '&#242;','oacute' => '&#243;','ocirc' => '&#244;','otilde' => '&#245;','ouml' => '&#246;',
                
'divide' => '&#247;','oslash' => '&#248;','ugrave' => '&#249;','uacute' => '&#250;','ucirc' => '&#251;','uuml' => '&#252;','yacute' => '&#253;','thorn' => '&#254;','yuml' => '&#255;' );
    
        if ( 
$flip === true )
        {
            
$tmp array_flip$table );
                
            
$lookup '&#' $matches[1] . ';';
                
            return isset( 
$tmp$lookup ] ) ? '&' $tmp$lookup ] . ';' $lookup;
        }
    
        return isset( 
$table$matches[1] ] ) ? $table$matches[1] ] : '&' $matches[1] . ';';
    }
    
    
/**
     * Wrapper for PHPs strip_tags. Makes < and > not part of tags safe
     * Based on @link http://www.php.net/manual/en/function.strip-tags.php#89309
     * @param string $text
     * @param string $allowed
     */
    
static public function stripTags$text$allowed='' )
    {
        
$strs explode'<'$text ); 
        
$res  $strs[0]; 
        
        for( 
$i=$i count$strs ) ; $i++ ) 
           { 
            if ( ! 
strpos$strs[$i], '>' ) )
            {
                
$res $res '&#0000001;' $strs[$i];
            }
            else
            {
                
$res $res '<' $strs[$i];
            }
            }
        
            
$text strip_tags$res$allowed );
            
        return 
str_replace'&#0000001;''<'$text );
    }
}
Онлайн: 0
Реклама