<?php
/**
 * text_format
 *
 * @package TextFormat
 * @author Sandi "sverde1" Verdev <sverde1@email.si>
 * @copyright Copyright (c) 2007
 * @version 1.1-beta
 * @access public
 * @licence GPL (http://www.gnu.org/licenses/gpl.txt)
 **/
class text_format
{
    
/**
     * Contains array with allowed codes or string 'all'
     *
     * @var mixed
     * @access private
     **/
    
private $allowed_codes;

    
/**
     * Contains array with parsing rules
     *
     * @var array
     * @access private
     **/
    
private $format_cache;

    
/**
     * Contains string with path to GeSHi class
     *
     * @var string
     * @access private
     **/
    
private $GeSHi_path;

    
/**
     * Contains PEAR::Crypt_Blowfish object
     *
     * @var object
     * @access private
     **/
    
private $Blowfish;

    
/**
     * text_format::__construct()
     *
     * @access public
     * @param mixed $allowed_codes
     **/
    
public function __construct($allowed_codes 'all')
    {
        
// set allowed codes
        
if($allowed_codes == 'all' || is_array($allowed_codes))
        {
            
$this->allowed_codes $allowed_codes;
        }
        else 
// if user specifies wrong parameter alert error
        
{
            
$this->Error('<b>Error:</b> Wrong data specified for second parameter. Parameter should be <b>all</b> or <b>array with allowed codes</b>!');
        }

        
$this->bbcode_cache_init();
    }

    
/**
     * text_format::text()
     *
     * @access public
     * @param string $text
     * @param boolean $blowfish
     * @return string
     **/
    
public function text($text$blowfish false)
    {
        
// PEAR::Crypt_Blowfish class was run and user wants to decrypt
        // specified text we decrypt the text
        
if(isset($this->Blowfish) && $blowfish === true)
        {
            
// decrypting
            
$text $this->Blowfish->decrypt($text);
            
// choping strange signs on both sides of the string
            
$text trim($text);
        }

        
// encode HTML chars
        
$text htmlspecialchars($text);

        
// parse BBCodes
        
return $this->bbcode($text);
    }

    
/**
     * text_format::add_smiley()
     *
     * @access public
     * @param string $alias
     * @param string $image
     * @return void
     **/
    
public function add_smiley($alias$image)
    {
        
$this->format_cache[] = array(
            
'str' => array(
                
$alias    =>    '<img src="' $image '" alt="' $alias '" border="0" />'
            
)
        );
    }

    
/**
     * text_format::GeSHi()
     *
     * @access public
     * @param string $path
     * @return boolean
     **/
    
public function GeSHi($path 'geshi.php')
    {
        
// if specified file exists we include it, but first we check if
        // filename is geshi.php <-- extension has to be the same as extension
        // of this file
        
if(file_exists($path) && (substr(strrchr($path'/'), 1) == 'geshi' strrchr(__FILE__'.')))
        {
            
// including GeSHi class
            
require('./' $path);
            
// checking if user specified the right file
            
if(defined('GESHI_VERSION'))
            {
                
// if he did remember the path
                
$this->GeSHi_path $path;
                return 
true;
            }
            
// in every GeSHi class there is constant GESHI_VERSION if this
            // constant is missing we trigger error because file user specified
            // is 99,9% not GeSHi class
            
else
            {
                
$this->Error('<b>Error:</b> The file you specified is not GeSHi syntax highlighting class!');
                return 
false;
            }
        }
        else
        {
            
$this->Error('<b>Error:</b> Specified file <b>' $path '</b> doesn\'t exist!');
            return 
false;
        }
    }

    
/**
     * text_format::Blowfish()
     *
     * @access public
     * @param string $key
     * @param string $text
     * @return mixed
     **/
    
public function Blowfish($key$text null)
    {
        
// include PEAR::Crypt_Blowfish
        
if(@include_once('Crypt/Blowfish.php'))
        {
            
// run the class and set Blowfish $key
            
$this->Blowfish = new Crypt_Blowfish($key);

            
// if there's some text, encode it with $key
            
if(isset($text))
            {
                return 
$this->Blowfish->encrypt($text);
            }
            return 
true;
        }
        else
        {
            
$this->Error('<b>Error:</b> You need to install <a href="http://pear.php.net/package/Crypt_Blowfish">PEAR::Crypt_Blowfish</a> to use this!');
            return 
false;
        }
    }

    
/**
     * text_format::Error()
     *
     * @access private
     * @param string $message
     * @return void
     **/
    
private function Error($message)
    {
        die(
$message);
    }

    
/**
     * text_format::bbcode()
     *
     * @access private
     * @param string $text
     * @return string parsed text
     **/
    
private function bbcode($text)
    {
        
$length count($this->format_cache);
        for(
$i 0$i $length$i++)
        {
            if(!empty(
$this->format_cache[$i]))
            {
                foreach(
$this->format_cache[$i] as $type => $array)
                {
                    foreach(
$array as $search => $replace)
                    {
                        switch(
$type)
                        {
                            case 
"str":
                                
$text str_ireplace($search$replace$text);
                            break;
                            case 
"preg":
                                
$text preg_replace($search$replace$text);
                            break;
                        }
                    }
                }
            }
        }
        return 
$text;
    }

    
/**
     * text_format::bbcode_cache_init()
     *
     * @return void
     **/
    
private function bbcode_cache_init()
    {
        if(
$this->allowed_codes == 'all' || in_array('b'$this->allowed_codes))
        {
            
$this->format_cache[] = array(
                
// [b]some string[/b] code...
                
'str' => array(
                    
'[b]'        => '<span style="font-weight: bold">',
                    
'[/b]'        => '</span>',
                )
            );
        }

        if(
$this->allowed_codes == 'all' || in_array('i'$this->allowed_codes))
        {
            
$this->format_cache[] = array(
                
// [i]some string[/i] code...
                
'str' => array(
                    
'[i]'        => '<span style="font-style: italic">',
                    
'[/i]'        => '</span>',
                )
            );
        }

        if(
$this->allowed_codes == 'all' || in_array('u'$this->allowed_codes))
        {
            
$this->format_cache[] = array(
                
// [u]some string[/u] code...
                
'str' => array(
                    
'[u]'        => '<span style="text-decoration: underline">',
                    
'[/u]'        => '</span>',
                )
            );
        }

        if(
$this->allowed_codes == 'all' || in_array('hr'$this->allowed_codes))
        {
            
$this->format_cache[] = array(
                
// some[hr]text --> some<hr />text
                
'str' => array(
                    
'[hr]'    => '<hr />'
                
)
            );
        }

        if(
$this->allowed_codes == 'all' || in_array('quote'$this->allowed_codes))
        {
            
$this->format_cache[] = array(
                
// [quote]somethink someone else said[/quote] code...
                
'str' => array(
                    
'[quote]'    => '<blockquote>',
                    
'[/quote]'    => '</blockquote>'
                
)
            );
        }

        
// START: custom for sverde1.com
        
if($this->allowed_codes == 'all' || in_array('del'$this->allowed_codes))
        {
            
$this->format_cache[] = array(
                
// [del]somethink someone else said[/del] code...
                
'str' => array(
                    
'[del]'    => '<del>',
                    
'[/del]'    => '</del>'
                
)
            );
        }
        
// END: custom for sverde1.com

        
if($this->allowed_codes == 'all' || in_array('url'$this->allowed_codes))
        {
            
$this->format_cache[] = array(
                
'preg' => array(
                    
// START: custom for sverde1.com
                    
'#\[url=([\w\#$%&~/.\-;:=,?@]+)\](\[[\w\d]{3,}\])\[/url\]#si'
                        
=>    '<a href="\\1" class="file_type">\\2</a>',
                    
'#\[url=([\w\#$%&~/.\-;:=,?@]+)\]([^?\n\r\t].*?)\[/url\]#si'
                        
=> '<a href="\\1">\\2</a>',
                    
// END: custom for sverde1.com
                    // matches a [url]xxxx://www.google.com[/url] code..
                    
'#\[url\]([\w]+?://([\w\#$%&~/.\-;:=,?@\]+]|\[(?!url=))*?)\[/url\]#is'
                        
=>    '<a href="\\1">\\1</a>',
                    
// [url]www.google.com[/url] code.. (no xxxx:// prefix).
                    
'#\[url\]((www|ftp)\.([\w\#$%&~/.\-;:=,?@\]+]|\[(?!url=))*?)\[/url\]#is'
                        
=>    '<a href="http://\\1">\\1</a>',
                    
// [url=xxxx://www.google.com]phpBB[/url] code..
                    
'#\[url=([\w]+?://[\w\#$%&~/.\-;:=,?@\[\]+]*?)\]([^?\n\r\t].*?)\[/url\]#is'
                        
=>    '<a href="\\1">\\2</a>',
                    
// [url=www.google.com]phpBB[/url] code.. (no xxxx:// prefix).
                    
'#\[url=((www|ftp)\.[\w\#$%&~/.\-;:=,?@\[\]+]*?)\]([^?\n\r\t].*?)\[/url\]#is'
                        
=>    '<a href="http://\\1">\\3</a>'
                    
// ([\w]+?://([\w\#$%&~/.\-;:=,?@\]+]|\[(?!url=))*?)
                    // ((www|ftp)\.([\w\#$%&~/.\-;:=,?@\]+]|\[(?!url=))*?)
                
)
            );
        }

        if(
$this->allowed_codes == 'all' || in_array('email'$this->allowed_codes))
        {
            
$this->format_cache[] = array(
                
'preg' => array(
                    
// [email]info@example.com[/email] code...
                    
'#\[email\]([a-z0-9&\-_.]+?@[\w\-]+\.([\w\-\.]+\.)?[\w]{2,4})\[/email\]#si'
                        
=>    '<a href="mailto:\\1">\\1</a>',
                    
// [email=info@example.com]phpBB[/email] code...
                    
'#\[email=([a-z0-9&\-_.]+?@[\w\-]+\.([\w\-\.]+\.)?[\w]{2,4})\]([^?\n\r\t].*?)\[/email\]#si'
                        
=>    '<a href="mailto:\\1">\\3</a>'

                    
// ([a-z0-9&\-_.]+?@[\w\-]+\.([\w\-\.]+\.)?[\w]{2,4})
                
)
            );
        }

        if(
$this->allowed_codes == 'all' || in_array('img'$this->allowed_codes))
        {
            
$this->format_cache[] = array(
                
// [img]http://www.example.com/somepic.jpg[/img]
                
'preg' => array(
                    
'#\[img\]([^?].*?)\[/img\]#si'
                        
=>    '<img src="\\1" alt="" />'
                
)
            );
        }

        if(
$this->allowed_codes == 'all' || in_array('size'$this->allowed_codes))
        {
            
$this->format_cache[] = array(
                
// [size=29]some string[/size]
                
'preg' => array(
                    
'#\[size=([\-\+]?[1-2]?[0-9])\](.*?)\[/size\]#si'
                        
=>    '<span style="font-size: \\1px; line-height: normal;">\\2</span>'
                
)
            );
        }

        if(
$this->allowed_codes == 'all' || in_array('color'$this->allowed_codes))
        {
            
$this->format_cache[] = array(
                
// [clor=#FFFFFF]some string[/color] code...
                
'preg' => array(
                    
'@\[color=(#[0-9A-F]{6}|[a-z\-]+)\](.*?)\[/color\]@si'
                        
=>    '<span style="color: \\1">\\2</span>'
                
)
            );
        }

        if(
$this->allowed_codes == 'all' || in_array('code'$this->allowed_codes))
        {
            
$this->format_cache[] = array(
                
/* [code=php]<?php echo 'foo'; ?>[/code] code... */
                
'preg' => array(
                    
'#\[code(?:=([a-z]+))?\](.*?)\[/code\]#ise'
                        
=>    "\$this->format_code('\\2', '\\1')"
                
)
            );
        }

        
/*if($this->allowed_codes == 'all' || in_array('list', $this->allowed_codes))
        {
            $this->format_cache[] = array(
                'preg' => array(
                    /*'#(\[\/?(list|\*):[mou]?\])[\n]{1}#'
                        => "\$1",*/
                    /*'#(\[list=([^\[]+)\])#'
                        => '<ol>',
                    /*'#\[list=([^\[]+):$uid\]#e'
                        => "\$this->bbcode_list('\$1')",*/
                /*),
                /* [list][*]foo[*]bar[/list] code... */
                /*'str' => array(
                    '[list]'    =>    '<ul>',
                    '[*]'        =>    '<li>',
                    '[/*]'        =>    '</li>',
                    '[/list]'    =>    '</ul>',
                    '[/list:u]'    =>    '</ul>',
                    '[/list:o]'    =>    '</ol>',
                )
            );
        }*/

        
if($this->allowed_codes == 'all' || in_array('flash'$this->allowed_codes))
        {
            
$this->format_cache[] = array(
                
// [flash=x,y]http://www.example.com/flash.swf[/flash] code...
                
'preg' => array(
                    
'#\[flash=([0-9]+),([0-9]+)\](.*?)\[/flash\]#i'
                        
=>    '<object classid="clsid:D27CDB6E-AE6D-11CF-96B8-444553540000" codebase="http://active.macromedia.com/flash2/cabs/swflash.cab#version=5,0,0,0" width="\\1" height="\\2">' .
                            
'    <param name="movie" value="\\3" />' .
                            
'    <param name="play" value="true" />' .
                            
'    <param name="loop" value="true" />' .
                            
'    <param name="quality" value="high" />' .
                            
'    <param name="allowScriptAccess" value="never" />' .
                            
'    <embed src="\\3" type="application/x-sockwave-flash" pluginspage="http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash" width="\\1" height="\\2" play="true" loop="true" quality="high" allowscriptaccess="never"></embed>' .
                            
'</object>'
                
)
            );
        }
    }

    
/**
     * text_format::format_code()
     *
     * @param string $code
     * @param string $type
     * @return string formated code
     **/
    
private function format_code($code$type null)
    {
        
$code htmlspecialchars_decode($code);
        
$GeSHi_passed false;
        if(isset(
$this->GeSHi_path) && !empty($type) && !empty($code))
        {
            
// run through all files in /geshi/ folder with color shemes to find
            // out if $type (language) is supported
            
$dh dir('./' dirname($this->GeSHi_path) . '/geshi/');
            while((
$dir $dh->read()) !== false)
            {
                
// color shemes are usualy in files with .php extension, for
                // extra compatibility we compare extension of THIS file with
                // files in /geshi/ folder
                
if(substr(strrchr($dir'.'), 1) == substr(strrchr(__FILE__'.'), 1))
                {
                    if(
strrev(substr(strstr(strrev($dir), '.'), 1)) == strtolower($type))
                    {
                        
// if no color sheme matches requested $type (language)
                        // set $type_ok to true
                        
$type_ok true;
                        
// if we found that $type is OK we stop the loop
                        
break;
                    }
                }
            }

            
// if $type matched checking ^^ we can pass the code trough GeSHi
            
if(isset($type_ok) && strtolower($type) != 'php')
            {
                
// parse code with GeSHi
                
$GeSHi = new GeSHi($code$type);
                
$code $GeSHi->parse_code();

                
$code '<div class="code">' $code '</div>';

                
// mark that we have passed code through GeSHi and it doesn't need
                // to be passed trough non GeSHi code parser
                
$GeSHi_passed true;
            }
        }

        
// if code didn't pass trough GeSHi we pass through this...
        
if($GeSHi_passed !== true)
        {
            switch(
$type)
            {
                case 
"php":
                    
// This colors PHP code...
                    
$code highlight_string(stripcslashes($code), true);
                    
$code '<div class="php">' $code .'</div>';
                    break;
                default:
                    
$code htmlspecialchars($code);
                    
$code '<div class="code">' $code '</div>';
                    break;
            }
        }
        return 
$code;
    }
}

// ******************* EXAMPLE *******************

// some example text
$text "<b>bold</b>[code=php]<?php
// Komentar
\$koda = '<b>foo</b>';
?>[/code][code=lisp](foo(bar))[/code] [:)] [;)]"
;

// start the class and let it prepare parsing enviorment
// as first parameter you can specify parsing engine (BBcode or wikitext)
// and as second parameter you can specify allowed codes in array
// you can also use:
//
// $text_format = new text_format();
//
// this way engine will be automaticaly set to BBCode and all codes will
// be enabled
$text_format = new text_format(array('b''i''u''code'));

// you can add smilies, first parameter is alias which will be replaced with
// smiley, the second parameter is link to image with smiley (link can be
// absolute or relative)
$text_format->add_smiley('[:)]''http://slike.slo-tech.com/smeski/smajl15.gif');
$text_format->add_smiley('[;)]''http://slike.slo-tech.com/smeski/smajl18.gif');

// if you want to use GeSHi for coloring of the code add this line
// as parameter you can specify path to GeSHi class, if not specified
// the class will try to include "./geshi.php"
$text_format->GeSHi('../geshi/geshi.php');

// if you want to encode or decode text with Blowfish, you can add this line
// you have to install PEAR::Crypt_Blowfish if you don't already. As first
// paramater you have to specify Blowfish key, as second you can optionaly
// specify the text that you want to encode.
$text $text_format->Blowfish('abc'$text);

// parsing of the text... As first parameter you MUST specify text that you
// want to parse it can be also Blowfish encoded, but then you have to set
// the second parameter to TRUE.
echo $text_format->text($texttrue);

?>