<?php
require_once 'geshi.php';
require_once 
'PHPTAL/Filter.php';

/**
 * Syntax highlighting using GeSHi class
 *
 * @author TAAT
 * @version 1.0
 */
 
class My_PHPTAL_Filter_Geshify implements PHPTAL_Filter {
     
// PROPERTIES
     /**
     * DOM
     * @access private
     * @var DOMDocument
     */
     
private $_dom null;

     
/**
     * Current highlight mode
     * @access private
     * @var string
     */
     
private $_currentLang false;

     
/**
     * Character encoding
     * @access private
     * @var string
     */
     
private $_encoding 'UTF-8';

     
/**
     * Tags to process
     * @access private
     * @var array of strings
     */
     
private $_tags = array('code');

     
/**
     * Supported languages
     * Class names to process
     * @access private
     * @var array
     */
     
private $_languages = array(
                                 
'apache',
                                 
'php',
                                 
'html',
                                 
'xml',
                                 
'php-brief',
                                 
'javascript',
                                 
'sql',
                                 
'css',
                                 
'html4strict'
                                 
// other supported by geshi
                                 /*
                                 abap
                                 actionscript
                                 actionscript3
                                 ada
                                 apache
                                 applescript
                                 apt_sources
                                 asm
                                 asp
                                 autoit
                                 avisynth
                                 bash
                                 basic4gl
                                 blitzbasic
                                 bnf
                                 boo
                                 c
                                 caddcl
                                 cadlisp
                                 cfdg
                                 cfm
                                 cil
                                 cobol
                                 cpp-qt
                                 cpp
                                 csharp
                                 css
                                 c_mac
                                 d
                                 delphi
                                 diff
                                 div
                                 dos
                                 dot
                                 eiffel
                                 email
                                 fortran
                                 freebasic
                                 genero
                                 gettext
                                 glsl
                                 gml
                                 gnuplot
                                 groovy
                                 haskell
                                 html4strict
                                 idl
                                 ini
                                 inno
                                 io
                                 java
                                 java5
                                 javascript
                                 kixtart
                                 klonec
                                 klonecpp
                                 latex
                                 lisp
                                 lotusformulas
                                 lotusscript
                                 lua
                                 m68k
                                 make
                                 matlab
                                 mirc
                                 mpasm
                                 mxml
                                 mysql
                                 nsis
                                 objc
                                 ocaml-brief
                                 ocaml
                                 oobas
                                 oracle11
                                 oracle8
                                 pascal
                                 per
                                 perl
                                 php-brief
                                 php
                                 pic16
                                 plsql
                                 povray
                                 powershell
                                 progress
                                 prolog
                                 python
                                 qbasic
                                 rails
                                 reg
                                 robots
                                 ruby
                                 sas
                                 scala
                                 scheme
                                 scilab
                                 sdlbasic
                                 smalltalk
                                 smarty
                                 sql
                                 tcl
                                 teraterm
                                 text
                                 thinbasic
                                 tsql
                                 typoscript
                                 vb
                                 vbnet
                                 verilog
                                 vhdl
                                 visualfoxpro
                                 winbatch
                                 xml
                                 xorg_conf
                                 xpp
                                 z80
                                 */
                                 
);

     
// GETTERS
     // SETTERS
     /**
     * Sets tags in which to replace
     * @access public
     * @param  $params
     * @return object itself
     */
     
public function setTags($params) {
         if (
is_array($params)) {
             
$this->_tags $params;
         } else {
             
$this->_tags = array();
             foreach (
func_get_args() as $arg) {
                 
$this->_tags[] = (string) $arg;
             }
         }
         return 
$this;
     }

     
/**
     * Sets classes/languages to highlight
     * @access public
     * @param  $params
     * @return object itself
     */
     
public function setLanguages($params) {
         if (
is_array($params)) {
             
$this->_languages $params;
         } else {
             foreach (
func_get_args() as $arg) {
                 
$this->_languges[] = (string) $arg;
             }
         }
         return 
$this;
     }

     
/**
     * Alias for setLanguages
     * @access public
     * @see setLanguages
     */
     
public function setLanguage($params) {
         
$this->setLanguage($params);
         return 
$this;
     }

     
/**
     * Alias for setTags
     * @access public
     * @see setLanguages
     */
     
public function setTag($params) {
         
$this->setTag($params);
     }

     
/**
     * Set character encoding
     * @access public
     * @param string $encoding
     * @return null
     */
     
public function setEncoding($encoding) {
         
$this->_encoding $encoding;
         return 
$this;
     }

     
// PUBLIC METHODS

     /**
     * Filter string
     * @access public
     * @param string $xhtml
     * @return null
     */
     
public function filter($xhtml) {
         
$this->_dom DOMDocument::loadXML($xhtml);
         
$this->_dom->encoding $this->_encoding;
         
$this->_dom->preserveWhiteSpace true;
         foreach (
$this->_tags as $tag) {
             
$nodes $this->_dom->getElementsByTagName($tag);
             foreach (
$nodes as $node) {
                 
$this->_currentLang $this->_onList(split(' '$node->getAttribute('class')),  $this->_languages);
                 if (
$this->_currentLang) {
                     
$this->_walk($node);
                 }
             }
         }
         
$output $this->_dom->saveXML();
         
// remove xml prolog
         
$output preg_replace('@<\?xml[^\?]+\?>\n?@s'''$output);
         return 
$output;
     }

     
// PRIVATE METHODS

     /**
     * Returns copy of the node
     * @access private
     * @param DOMNode $node
     * @return null
     */
     
private function _walk($node) {
         if (
$node->hasChildNodes()) {
             
$children $node->childNodes;
             
$i $children->length 1;
             while (
$i >= 0) {
                 
$child $children->item($i);
                 
$i--;
                 
$this->_walk($child);
                 if (
$child->nodeType === XML_TEXT_NODE) {
                     
// highlight if text node
                     
$newnode $this->_geshi($child->nodeValue$this->_currentLang);
                     if (
$newnode->hasChildNodes()) {
                         foreach (
$newnode->childNodes as $nn) {
                             
$newchild $this->_dom->importNode($nntrue);
                             
$node->insertBefore($newchild$child);
                         }
                     }
                     
$child->parentNode->removeChild($child);
                 }
             }
         }
     }

     
/**
     * Determine if at least one of specified searches exist on the list
     * @access private
     * @param array $list array of strings
     * @param array $searches array of strings
     * @return null
     */
     
private function _onList($list$searches) {
         if (
is_string($searches)) {
             
$searches = array($searches);
         }
         foreach (
$searches as $search) {
             foreach (
$list as $item) {
                 if (
$item === $search) {
                     return 
$item;
                 }
             }
         }
         return 
false;
     }

     
/**
     * Highlight syntax using GeSHi
     * @access private
     * @param  $string
     * @return null
     */
     
private function _geshi($source$language 'html4strict') {
         if (
$language == 'html') {
             
$language 'html4strict';
         }
         
$geshi = new GeSHi($source$language);
         
$geshi->enable_classes();
         
$geshi->set_encoding($this->_encoding);
         
$geshi->set_header_type(GESHI_HEADER_NONE);
         
$geshi->enable_keyword_links(false);
         
$parsed $geshi->parse_code();
         
$parsed str_replace('<br />'''$parsed);
         
$parsed str_replace('&nbsp;'' '$parsed);
         
$parsed"<geshi>" $parsed "</geshi>";
         
$newdom DOMDocument::loadXML($parsed);
         
$newdom->encoding $this->_encoding;
         
$dom->preserveWhiteSpace true;
         
$nodes $newdom->getElementsByTagName('geshi');
         return 
$nodes->item(0);
     }
 }

?>