<?php
/*
Plugin Name: CodeHighlight
Plugin URI: http://www.chroder.com/archives/2005/04/16/wordpress-codehighlight-plugin/
Description: Adds syntax highlighting to your code, or simply allows you to post workable code not affected by the WordPress text replacement features.
Version: 1.0 Beta 3.1.1
Author: Chroder
Author URI: http://www.chroder.com/
*/

//================================================================================
// Get the PEAR path, and include the highlighter
//================================================================================
$pear_dir ABSPATH 'wp-content/plugins/CodeHighlight/PEAR';

if(
is_dir($pear_dir))
    
ini_set("include_path"ini_get("include_path") . PATH_SEPARATOR $pear_dir);

require_once 
'Text/Highlighter.php';




//================================================================================
// Create our custom highlighter, then add the filters
// We have sets - before and after, which are performed before and
// after all of the other filters. This is to bypass any filters
// that do crazy text replacements. It's easier this way, instead of
// trying to undo what the other filters did.
//================================================================================
$HLight = new HLight();

// Showing
add_filter('the_content', array(&$HLight'part_one'), -1000);
add_filter('the_content', array(&$HLight'part_two'),  1000);

add_filter('the_excerpt', array(&$HLight'part_one'), -1000);
add_filter('the_excerpt', array(&$HLight'part_two'),  1000);

add_filter('comment_text', array(&$HLight'part_one'), -1000);
add_filter('comment_text', array(&$HLight'part_two'),  1000);

// Inserting/Updating
//add_filter('content_save_pre', array(&$HLight, 'part_one_pre'), -1000);
//add_filter('content_save_pre', array(&$HLight, 'part_two_pre'),  1000);
add_filter('content_save_pre', array(&$HLight'comment_save_one'), -1000);
add_filter('content_save_pre', array(&$HLight'comment_save_two'),  1000);

add_filter('excerpt_save_pre', array(&$HLight'part_one_pre'), -1000);
add_filter('excerpt_save_pre', array(&$HLight'part_two_pre'),  1000);

//add_filter('pre_comment_content', array(&$HLight, 'part_one_pre'), -1000);
//add_filter('pre_comment_content', array(&$HLight, 'part_two_pre'),  1000);

add_filter('comment_save_pre', array(&$HLight'comment_save_one'), -500);
add_filter('comment_save_pre', array(&$HLight'comment_save_two'),  500);

add_filter('pre_comment_content', array(&$HLight'comment_save_one'), -500);
add_filter('pre_comment_content', array(&$HLight'comment_save_two'),  500);

add_filter('wp_head', array('HLight''css'));

class 
HLight
{
    
// The languages the PEAR HLigher can accept
    
var $acceptable_lang = array('php''cpp''css''diff''dtd''javascript',
                                 
'mysql''perl''python''ruby''sql''xml''java');

    
// The pattern to replace with
    // Make sure the {STYLE} (used when forcing a height for h-scroll)
    // and {CONTENT} replacement "variables" are present.
    
var $tpl '<div class="hl-surround" {STYLE}>{CONTENT}</div>';

    
// The number of lines to accept before adding an H-Scroller
    
var $lines 20;

    
// The blocks array that holds the block ID's and their real code blocks
    
var $blocks = array();

    
// The height of the font you are using for code.
    // This is used when calculating the set-height of the code boxes
    // when a scroller is needed.
    // Example: $lines is 20, so if your code is over 20 lines, the box will
    // be $lines*$line_height px high. [20*14px by default (280px)]
    
var $line_height 14;




    
/****************************************************************************
     * part_one
     *    > Replace the code blocks with the block IDs
     ****************************************************************************/
    
function part_one($content)
    {
        
$content preg_replace('#\[code\](.*?)\[/code\]#sie''$this->do_hlight(\'\\1\', false, $content);'$content);
        
$content preg_replace('#\[code lang="(.*?)"\](.*?)\[/code\]#sie''$this->do_hlight(\'\\2\', \'\\1\', $content);'$content);        
        
$content preg_replace('#\[code lang="(.*?)" lines="([0-9]{1,3})"\](.*?)\[/code\]#sie''HLight::lines = \\2; $this->do_hlight(\'\\3\', \'\\1\', $content);'$content);

        return 
$content;
    }




    
/****************************************************************************
     * part_two
     *    > Replace the block ID's from part one with the actual code blocks
     ****************************************************************************/
    
function part_two($content)
    {
        
$content str_replace(array_keys($this->blocks), array_values($this->blocks), $content);
        
$this->blocks = array();

        return 
$content;
    }


    
// -------------------------------------------------------------------------
    // Output CSS
    // -------------------------------------------------------------------------
    
function css() {
    
?>
    <style type="text/css" media="screen">
     /********************************************************************************
     * CodeHighlight Classes
     *******************************************************************************/
    .hl-surround {
        -moz-border-radius: 5px;
        background-color: #F9FBFC;
        border: 1px solid #C3CED9;
        padding: 8px;
        margin-bottom: 5px;
        width: auto;
        overflow: auto;
        text-align: left;
    }

    .hl-surround, .hl-surround pre, .hl-surround span { font: normal 9pt 'Courier New', monospace;}
    .hl-default     { color: Black; }
    .hl-code        { color: Gray; }
    .hl-brackets    { color: Olive; }
    .hl-comment     { color: #ffa500; } /* Orange */
    .hl-quotes      { color: #8b0000; } /* Dark red */
    .hl-string      { color: Red; }
    .hl-identifier  { color: Blue; }
    .hl-builtin     { color: Teal; }
    .hl-reserved    { color: Green; }
    .hl-inlinedoc   { color: Blue; }
    .hl-var         { color: #00008b; } /* Dark blue */
    .hl-url         { color: Blue; }
    .hl-special     { color: Navy; }
    .hl-number      { color: Maroon; }
    .hl-inlinetags  { color: Blue; }
    .hl-main        { background-color: transparent; }

    .hl-main pre { margin: 0; padding: 0; }
        </style>
    <?php
    
}

    
/****************************************************************************
     * do_hlight
     *    > Perform the code highlighting that is to be replaced with a block ID
     ****************************************************************************/
    
function do_hlight($txt$lang false$content)
    {
        
$txt str_replace(array("\\\"""\\\'"), array("\"""\'"), $txt);
        
$txt trim($txt);


        
// See if we should force a height
        
$num_lines count(explode("\n"$txt));

        if(
$num_lines $this->lines)
            
$style 'style="height:' . ($this->lines $this->line_height) . 'px;"';
        elseif(
$num_lines == 1)
            
$style 'style="height:' . ($this->line_height) . 'px;"';
        else
            
$style '';

        
$blockID $this->getBlockID($content);

        if(!
in_array($lang$this->acceptable_lang))
            
$this->blocks[$blockID] = str_replace(array('{STYLE}''{CONTENT}'), array($style'<div class="hl-main"><pre>' htmlspecialchars($txt) . '</pre></div>'), $this->tpl);

        else
        {
            
$hl =& Text_Highlighter::factory($lang);
            
$this->blocks[$blockID] = str_replace(array('{STYLE}''{CONTENT}'), array($style$hl->highlight($txt)), $this->tpl);
        }

        return 
$blockID;
    }




     
/****************************************************************************
     * part_one_pre
     *    > Replace the code blocks with the block IDs
     *      (when inserting/updating posts)
     ****************************************************************************/
    
function part_one_pre($content)
    {
        
$content stripslashes($content);
        
$content preg_replace('#\[code\](.*?)\[/code\]#sie''$this->do_blocks(\'\\1\', false, $content);'$content);
        
$content preg_replace('#\[code lang=\\\\"(.*?)\\\\"\](.*?)\[/code\]#sie''$this->do_blocks(\'\\2\', \'\\1\', $content);'$content);
        
$content preg_replace('#\[code lang=\\\\"(.*?)\\\\" lines="([0-9]{1,3})"\](.*?)\[/code\]#sie''HLight::lines = \\2; $this->do_blocks(\'\\3\', \'\\1\', $content);'$content);        
        
$content addslashes($content);

        return 
$content;
    }




    
/****************************************************************************
     * part_two_pre
     *    > Replace the block ID's from part one with the actual code blocks
     *      (when inserting/updating posts)
     ****************************************************************************/
    
function part_two_pre($content)
    {
        return 
$this->part_two($content);
    }


    
/* ---------------------------------------------------------------------------
    * remove code between [code]...[/code] and replaces it by a number
    * to be replaced back again later
    * Action could be 'unhide' (which returns $content recovererd) or 'hide' 
    * (= 0, returns $content with code replaced)
    ------------------------------------------------------------------------------*/
    
function hide_code($content$action)
    {
        static 
$counter 0// Initial counter
        
static $mem = array(); // Initial empty array

        
$action strtolower($action);
        
$content stripslashes($content);        
        
$tag '\\[code\\]|\\[code lang=\\"[^"]+\\"\\]';
        
$etag '\\[\\/code\\]';
        
        
$textarr preg_split("/($tag.*$etag)/Us"$content, -1PREG_SPLIT_DELIM_CAPTURE); // capture the tags as well as in between    
        
$stop count($textarr);// loop stuff
        
$output '';    
        for (
$phpexec_i 0$phpexec_i $stop$phpexec_i++) {
            
$content_ $textarr[$phpexec_i];
        
            if (
preg_match("/($tag)(.*)($etag)/Us"$content_$code)) { // If it's a phpcode                
                
if ($action == 'hide') {
                    
$mem["$counter"] = $code[2];
                    
$content_ $code[1] . $counter $code[3];
                    
$counter++;
                } else { 
// action == 'unhide'
                    
$content_ $code[1] . $mem[$code[2]] . $code[3];
                }
            }
            
            
$output .= $content_;
        }
        
        
$content addslashes($output);    
        return 
$content;
    }

    function 
comment_save_one($content)
    {
        
$content $this->hide_code($content'hide');
        return 
$content;
    }
    
    
    function 
comment_save_two($content)
    {
        
$content $this->hide_code($content'unhide');
        return 
$content;
    }

    
/****************************************************************************
     * do_blocks
     *    > Replace blocks of code with the blockID's
     ****************************************************************************/
    
function do_blocks($txt$lang false$content)
    {
        
$blockID $this->getBlockID($content);

        if(!
in_array($lang$this->acceptable_lang))
            
$this->blocks[$blockID] = '[code]' $txt '[/code]';
        else
            
$this->blocks[$blockID] = '[code lang="' $lang '"]' $txt '[/code]';

        return 
$blockID;
    }




    
/****************************************************************************
     * getBlockID
     *    > Generate a block ID that will be replaced at the end (after all that
     *      crazy WP text work!) with the right code
     ****************************************************************************/
    
function getBlockID($content)
    {
        static 
$num 0;

        
// Just do a check to make sure the user
        // hasn't (however unlikely) input block replacements
        // as legit text
        
do
        {
            ++
$num;
            
$blockID "<div>::HLIGHT_BLOCK_$num::</div>";
        }
        while(
strpos($content$blockID) !== false);

        return 
$blockID;
    }
}


?>