Support Joomla!

Joomla! 1.5 Documentation

Packages

Package: core

Developer Network License

The Joomla! Developer Network content is © copyright 2006 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution- NonCommercial- ShareAlike 2.5
Source code for file /geshi/geshi.php

Documentation is available at geshi.php

  1. <?php
  2. /**
  3.  * GeSHi - Generic Syntax Highlighter
  4.  *
  5.  * The GeSHi class for Generic Syntax Highlighting. Please refer to the documentation
  6.  * at http://qbnz.com/highlighter/documentation.php for more information about how to
  7.  * use this class.
  8.  *
  9.  * For changes, release notes, TODOs etc, see the relevant files in the docs/ directory
  10.  *
  11.  *   This file is part of GeSHi.
  12.  *
  13.  *  GeSHi is free software; you can redistribute it and/or modify
  14.  *  it under the terms of the GNU General Public License as published by
  15.  *  the Free Software Foundation; either version 2 of the License, or
  16.  *  (at your option) any later version.
  17.  *
  18.  *  GeSHi is distributed in the hope that it will be useful,
  19.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  20.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21.  *  GNU General Public License for more details.
  22.  *
  23.  *  You should have received a copy of the GNU General Public License
  24.  *  along with GeSHi; if not, write to the Free Software
  25.  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  26.  *
  27.  * @package   core
  28.  * @author    Nigel McNie <[email protected]>
  29.  * @copyright Copyright &copy; 2004, 2005, Nigel McNie
  30.  * @license   http://gnu.org/copyleft/gpl.html GNU GPL
  31.  * @version   $Id: geshi.php,v 1.32 2006/03/23 07:24:17 oracleshinoda Exp $
  32.  *
  33.  */
  34.  
  35. //
  36. // GeSHi Constants
  37. // You should use these constant names in your programs instead of
  38. // their values - you never know when a value may change in a future
  39. // version
  40. //
  41.  
  42. /** The version of this GeSHi file */
  43. define('GESHI_VERSION''1.0.7.8');
  44.  
  45. /** Set the correct directory separator */
  46. define('GESHI_DIR_SEPARATOR'('WIN' != substr(PHP_OS03)) '/' '\\');
  47.  
  48. // Define the root directory for the GeSHi code tree
  49. if (!defined('GESHI_ROOT')) {
  50.     /** The root directory for GeSHi */
  51.     define('GESHI_ROOT'dirname(__FILE__GESHI_DIR_SEPARATOR);
  52. }
  53. /** The language file directory for GeSHi
  54.     @access private */
  55. define('GESHI_LANG_ROOT'GESHI_ROOT 'geshi' GESHI_DIR_SEPARATOR);
  56.  
  57.  
  58. // Line numbers - use with enable_line_numbers()
  59. /** Use no line numbers when building the result */
  60. define('GESHI_NO_LINE_NUMBERS'0);
  61. /** Use normal line numbers when building the result */
  62. define('GESHI_NORMAL_LINE_NUMBERS'1);
  63. /** Use fancy line numbers when building the result */
  64. define('GESHI_FANCY_LINE_NUMBERS'2);
  65.  
  66. // Container HTML type
  67. /** Use nothing to surround the source */
  68. define('GESHI_HEADER_NONE'0);
  69. /** Use a "div" to surround the source */
  70. define('GESHI_HEADER_DIV'1);
  71. /** Use a "pre" to surround the source */
  72. define('GESHI_HEADER_PRE'2);
  73.  
  74. // Capatalisation constants
  75. /** Lowercase keywords found */
  76. define('GESHI_CAPS_NO_CHANGE'0);
  77. /** Uppercase keywords found */
  78. define('GESHI_CAPS_UPPER'1);
  79. /** Leave keywords found as the case that they are */
  80. define('GESHI_CAPS_LOWER'2);
  81.  
  82. // Link style constants
  83. /** Links in the source in the :link state */
  84. define('GESHI_LINK'0);
  85. /** Links in the source in the :hover state */
  86. define('GESHI_HOVER'1);
  87. /** Links in the source in the :active state */
  88. define('GESHI_ACTIVE'2);
  89. /** Links in the source in the :visited state */
  90. define('GESHI_VISITED'3);
  91.  
  92. // Important string starter/finisher
  93. // Note that if you change these, they should be as-is: i.e., don't
  94. // write them as if they had been run through htmlentities()
  95. /** The starter for important parts of the source */
  96. define('GESHI_START_IMPORTANT''<BEGIN GeSHi>');
  97. /** The ender for important parts of the source */
  98. define('GESHI_END_IMPORTANT''<END GeSHi>');
  99.  
  100. /**#@+
  101.  *  @access private
  102.  */
  103. // When strict mode applies for a language
  104. /** Strict mode never applies (this is the most common) */
  105. define('GESHI_NEVER'0);
  106. /** Strict mode *might* apply, and can be enabled or
  107.     disabled by {@link GeSHi::enable_strict_mode()} */
  108. define('GESHI_MAYBE'1);
  109. /** Strict mode always applies */
  110. define('GESHI_ALWAYS'2);
  111.  
  112. // Advanced regexp handling constants, used in language files
  113. /** The key of the regex array defining what to search for */
  114. define('GESHI_SEARCH'0);
  115. /** The key of the regex array defining what bracket group in a
  116.     matched search to use as a replacement */
  117. define('GESHI_REPLACE'1);
  118. /** The key of the regex array defining any modifiers to the regular expression */
  119. define('GESHI_MODIFIERS'2);
  120. /** The key of the regex array defining what bracket group in a
  121.     matched search to put before the replacement */
  122. define('GESHI_BEFORE'3);
  123. /** The key of the regex array defining what bracket group in a
  124.     matched search to put after the replacement */
  125. define('GESHI_AFTER'4);
  126.  
  127. /** Used in language files to mark comments */
  128. define('GESHI_COMMENTS'0);
  129.  
  130. // Error detection - use these to analyse faults
  131. /** No sourcecode to highlight was specified
  132.  * @deprecated
  133.  */
  134. define('GESHI_ERROR_NO_INPUT'1);
  135. /** The language specified does not exist */
  136. define('GESHI_ERROR_NO_SUCH_LANG'2);
  137. /** GeSHi could not open a file for reading (generally a language file) */
  138. define('GESHI_ERROR_FILE_NOT_READABLE'3);
  139. /** The header type passed to {@link GeSHi::set_header_type()} was invalid */
  140. define('GESHI_ERROR_INVALID_HEADER_TYPE'4);
  141. /** The line number type passed to {@link GeSHi::enable_line_numbers()} was invalid */
  142. define('GESHI_ERROR_INVALID_LINE_NUMBER_TYPE'5);
  143. /**#@-*/
  144.  
  145.  
  146.  * The GeSHi Class.
  147.  *
  148.  * Please refer to the documentation for GeSHi 1.0.X that is available
  149.  * at http://qbnz.com/highlighter/documentation.php for more information
  150.  * about how to use this class.
  151.  *
  152.  * @package   core
  153.  * @author    Nigel McNie <[email protected]>
  154.  * @copyright Copyright &copy; 2004, 2005 Nigel McNie
  155.  */
  156. class GeSHi
  157. {
  158.     /**#@+
  159.      * @access private
  160.      */
  161.     /**
  162.      * The source code to highlight
  163.      * @var string 
  164.      */
  165.     var $source '';
  166.  
  167.     /**
  168.      * The language to use when highlighting
  169.      * @var string 
  170.      */
  171.     var $language '';
  172.  
  173.     /**
  174.      * The data for the language used
  175.      * @var array 
  176.      */
  177.     var $language_data array();
  178.  
  179.     /**
  180.      * The path to the language files
  181.      * @var string 
  182.      */
  183.     var $language_path GESHI_LANG_ROOT;
  184.  
  185.     /**
  186.      * The error message associated with an error
  187.      * @var string 
  188.      * @todo check err reporting works
  189.      */
  190.     var $error false;
  191.  
  192.     /**
  193.      * Possible error messages
  194.      * @var array 
  195.      */
  196.     var $error_messages array(
  197.         //GESHI_ERROR_NO_INPUT => 'No source code inputted',
  198.                 GESHI_ERROR_NO_SUCH_LANG => 'GeSHi could not find the language {LANGUAGE} (using path {PATH})',
  199.         GESHI_ERROR_FILE_NOT_READABLE => 'The file specified for load_from_file was not readable',
  200.         GESHI_ERROR_INVALID_HEADER_TYPE => 'The header type specified is invalid',
  201.         GESHI_ERROR_INVALID_LINE_NUMBER_TYPE => 'The line number type specified is invalid'
  202.     );
  203.  
  204.     /**
  205.      * Whether highlighting is strict or not
  206.      * @var boolean 
  207.      */
  208.     var $strict_mode false;
  209.  
  210.     /**
  211.      * Whether to use CSS classes in output
  212.      * @var boolean 
  213.      */
  214.     var $use_classes false;
  215.  
  216.     /**
  217.      * The type of header to use. Can be one of the following
  218.      * values:
  219.      *
  220.      * <ul>
  221.      *   <li><b>GESHI_HEADER_PRE</b>: Source is outputted in
  222.      *   a &lt;pre&gt; HTML element.</li>
  223.      *   <li><b>GESHI_HEADER_DIV</b>: Source is outputted in
  224.      *   a &lt;div&gt; HTML element.</li>
  225.      *   <li><b>GESHI_HEADER_NONE</b>: No header is outputted.</li>
  226.      * </ul>
  227.      *
  228.      * @var int 
  229.      */
  230.     var $header_type GESHI_HEADER_PRE;
  231.  
  232.     /**
  233.      * Array of permissions for which lexics should be highlighted
  234.      * @var array 
  235.      */
  236.     var $lexic_permissions array(
  237.         'KEYWORDS' =>    array(),
  238.         'COMMENTS' =>    array('MULTI' => true),
  239.         'REGEXPS' =>     array(),
  240.         'ESCAPE_CHAR' => true,
  241.         'BRACKETS' =>    true,
  242.         'SYMBOLS' =>     true,
  243.         'STRINGS' =>     true,
  244.         'NUMBERS' =>     true,
  245.         'METHODS' =>     true,
  246.         'SCRIPT' =>      true
  247.     );
  248.  
  249.     /**
  250.      * The time it took to parse the code
  251.      * @var double 
  252.      */
  253.     var $time 0;
  254.  
  255.     /**
  256.      * The content of the header block
  257.      * @var string 
  258.      */
  259.     var $header_content '';
  260.  
  261.     /**
  262.      * The content of the footer block
  263.      * @var string 
  264.      */
  265.     var $footer_content '';
  266.  
  267.     /**
  268.      * The style of the header block
  269.      * @var string 
  270.      */
  271.     var $header_content_style '';
  272.  
  273.     /**
  274.      * The style of the footer block
  275.      * @var string 
  276.      */
  277.     var $footer_content_style '';
  278.  
  279.     /**
  280.      * The styles for hyperlinks in the code
  281.      * @var array 
  282.      */
  283.     var $link_styles array();
  284.  
  285.     /**
  286.      * Whether important blocks should be recognised or not
  287.      * @var boolean 
  288.      * @deprecated
  289.      * @todo REMOVE THIS FUNCTIONALITY!
  290.      */
  291.     var $enable_important_blocks false;
  292.  
  293.     /**
  294.      * Styles for important parts of the code
  295.      * @var string 
  296.      * @deprecated
  297.      * @todo As above - rethink the whole idea of important blocks as it is buggy and
  298.      *  will be hard to implement in 1.2
  299.      */
  300.     var $important_styles 'font-weight: bold; color: red;'// Styles for important parts of the code
  301.  
  302.     
  303.     /**
  304.      * Whether CSS IDs should be added to the code
  305.      * @var boolean 
  306.      */
  307.     var $add_ids false;
  308.  
  309.     /**
  310.      * Lines that should be highlighted extra
  311.      * @var array 
  312.      */
  313.     var $highlight_extra_lines array();
  314.  
  315.     /**
  316.      * Styles of extra-highlighted lines
  317.      * @var string 
  318.      */
  319.     var $highlight_extra_lines_style 'color: #cc0; background-color: #ffc;';
  320.  
  321.     /**
  322.      * Number at which line numbers should start at
  323.      * @var int 
  324.      * @todo Warning documentation about XHTML compliance
  325.      */
  326.     var $line_numbers_start 1;
  327.  
  328.     /**
  329.      * The overall style for this code block
  330.      * @var string 
  331.      */
  332.     var $overall_style '';
  333.  
  334.     /**
  335.      *  The style for the actual code
  336.      * @var string 
  337.      */
  338.     var $code_style 'font-family: \'Courier New\', Courier, monospace; font-weight: normal;';
  339.  
  340.     /**
  341.      * The overall class for this code block
  342.      * @var string 
  343.      */
  344.     var $overall_class '';
  345.  
  346.     /**
  347.      * The overall ID for this code block
  348.      * @var string 
  349.      */
  350.     var $overall_id '';
  351.  
  352.     /**
  353.      * Line number styles
  354.      * @var string 
  355.      */
  356.     var $line_style1 'font-family: \'Courier New\', Courier, monospace; color: black; font-weight: normal; font-style: normal;';
  357.  
  358.     /**
  359.      * Line number styles for fancy lines
  360.      * @var string 
  361.      */
  362.     var $line_style2 'font-weight: bold;';
  363.  
  364.     /**
  365.      * Flag for how line nubmers are displayed
  366.      * @var boolean 
  367.      */
  368.     var $line_numbers GESHI_NO_LINE_NUMBERS;
  369.  
  370.     /**
  371.      * The "nth" value for fancy line highlighting
  372.      * @var int 
  373.      */
  374.     var $line_nth_row 0;
  375.  
  376.     /**
  377.      * The size of tab stops
  378.      * @var int 
  379.      */
  380.     var $tab_width 8;
  381.  
  382.     /**
  383.      * Default target for keyword links
  384.      * @var string 
  385.      */
  386.     var $link_target '';
  387.  
  388.     /**
  389.      * The encoding to use for entity encoding
  390.      * @var string 
  391.      */
  392.     var $encoding 'ISO-8859-1';
  393.  
  394.     /**#@-*/
  395.  
  396.     /**
  397.      * Creates a new GeSHi object, with source and language
  398.      *
  399.      * @param string The source code to highlight
  400.      * @param string The language to highlight the source with
  401.      * @param string The path to the language file directory. <b>This
  402.      *                is deprecated!</b> I've backported the auto path
  403.      *                detection from the 1.1.X dev branch, so now it
  404.      *                should be automatically set correctly. If you have
  405.      *                renamed the language directory however, you will
  406.      *                still need to set the path using this parameter or
  407.      *                {@link GeSHi::set_language_path()}
  408.      * @since 1.0.0
  409.      */
  410.     function GeSHi ($source$language$path '')
  411.     {
  412.         $this->set_source($source);
  413.         $this->set_language_path($path);
  414.         $this->set_language($language);
  415.     }
  416.  
  417.     /**
  418.      * Returns an error message associated with the last GeSHi operation,
  419.      * or false if no error has occured
  420.      *
  421.      * @return string|falseAn error message if there has been an error, else false
  422.      * @since  1.0.0
  423.      */
  424.     function error ()
  425.     {
  426.         if ($this->error{
  427.             $msg $this->error_messages[$this->error];
  428.             $debug_tpl_vars array(
  429.                 '{LANGUAGE}' => $this->language,
  430.                 '{PATH}' => $this->language_path
  431.             );
  432.             foreach ($debug_tpl_vars as $tpl => $var{
  433.                 $msg str_replace($tpl$var$msg);
  434.             }
  435.             return "<br /><strong>GeSHi Error:</strong$msg (code $this->error)<br />";
  436.         }
  437.         return false;
  438.     }
  439.  
  440.     /**
  441.      * Gets a human-readable language name (thanks to Simon Patterson
  442.      * for the idea :))
  443.      *
  444.      * @return string The name for the current language
  445.      * @since  1.0.2
  446.      */
  447.     function get_language_name ()
  448.     {
  449.         if (GESHI_ERROR_NO_SUCH_LANG == $this->_error{
  450.             return $this->language_data['LANG_NAME'' (Unknown Language)';
  451.         }
  452.         return $this->language_data['LANG_NAME'];
  453.     }
  454.  
  455.     /**
  456.      * Sets the source code for this object
  457.      *
  458.      * @param string The source code to highlight
  459.      * @since 1.0.0
  460.      */
  461.     function set_source ($source)
  462.     {
  463.         $this->source $source;
  464.     }
  465.  
  466.     /**
  467.      * Sets the language for this object
  468.      *
  469.      * @param string The name of the language to use
  470.      * @since 1.0.0
  471.      */
  472.     function set_language ($language)
  473.     {
  474.         $this->error false;
  475.         $this->strict_mode GESHI_NEVER;
  476.  
  477.         $language preg_replace('#[^a-zA-Z0-9\-_]#'''$language);
  478.         $this->language strtolower($language);
  479.  
  480.         $file_name $this->language_path $this->language '.php';
  481.         if (!is_readable($file_name)) {
  482.             $this->error GESHI_ERROR_NO_SUCH_LANG;
  483.             return;
  484.         }
  485.         // Load the language for parsing
  486.         $this->load_language($file_name);
  487.     }
  488.  
  489.     /**
  490.      * Sets the path to the directory containing the language files. Note
  491.      * that this path is relative to the directory of the script that included
  492.      * geshi.php, NOT geshi.php itself.
  493.      *
  494.      * @param string The path to the language directory
  495.      * @since 1.0.0
  496.      * @deprecated The path to the language files should now be automatically
  497.      *              detected, so this method should no longer be needed. The
  498.      *              1.1.X branch handles manual setting of the path differently
  499.      *              so this method will disappear in 1.2.0.
  500.      */
  501.     function set_language_path ($path)
  502.     {
  503.         if ($path{
  504.             $this->language_path ('/' == substr($pathstrlen($path11)) $path $path '/';
  505.             $this->set_language($this->language);        // otherwise set_language_path has no effect
  506.         }
  507.     }
  508.  
  509.     /**
  510.      * Sets the type of header to be used.
  511.      *
  512.      * If GESHI_HEADER_DIV is used, the code is surrounded in a "div".This
  513.      * means more source code but more control over tab width and line-wrapping.
  514.      * GESHI_HEADER_PRE means that a "pre" is used - less source, but less
  515.      * control. Default is GESHI_HEADER_PRE.
  516.      *
  517.      * From 1.0.7.2, you can use GESHI_HEADER_NONE to specify that no header code
  518.      * should be outputted.
  519.      *
  520.      * @param int The type of header to be used
  521.      * @since 1.0.0
  522.      */
  523.     function set_header_type ($type)
  524.     {
  525.         if (GESHI_HEADER_DIV != $type && GESHI_HEADER_PRE != $type && GESHI_HEADER_NONE != $type{
  526.             $this->error GESHI_ERROR_INVALID_HEADER_TYPE;
  527.             return;
  528.         }
  529.         $this->header_type $type;
  530.     }
  531.  
  532.     /**
  533.      * Sets the styles for the code that will be outputted
  534.      * when this object is parsed. The style should be a
  535.      * string of valid stylesheet declarations
  536.      *
  537.      * @param string  The overall style for the outputted code block
  538.      * @param boolean Whether to merge the styles with the current styles or not
  539.      * @since 1.0.0
  540.      */
  541.     function set_overall_style ($style$preserve_defaults false)
  542.     {
  543.         if (!$preserve_defaults{
  544.             $this->overall_style $style;
  545.         else {
  546.             $this->overall_style .= $style;
  547.         }
  548.     }
  549.  
  550.     /**
  551.      * Sets the overall classname for this block of code. This
  552.      * class can then be used in a stylesheet to style this object's
  553.      * output
  554.      *
  555.      * @param string The class name to use for this block of code
  556.      * @since 1.0.0
  557.      */
  558.     function set_overall_class ($class)
  559.     {
  560.         $this->overall_class $class;
  561.     }
  562.  
  563.     /**
  564.      * Sets the overall id for this block of code. This id can then
  565.      * be used in a stylesheet to style this object's output
  566.      *
  567.      * @param string The ID to use for this block of code
  568.      * @since 1.0.0
  569.      */
  570.     function set_overall_id ($id)
  571.     {
  572.         $this->overall_id $id;
  573.     }
  574.  
  575.     /**
  576.      * Sets whether CSS classes should be used to highlight the source. Default
  577.      * is off, calling this method with no arguments will turn it on
  578.      *
  579.      * @param boolean Whether to turn classes on or not
  580.      * @since 1.0.0
  581.      */
  582.     function enable_classes ($flag true)
  583.     {
  584.         $this->use_classes ($flagtrue false;
  585.     }
  586.  
  587.     /**
  588.      * Sets the style for the actual code. This should be a string
  589.      * containing valid stylesheet declarations. If $preserve_defaults is
  590.      * true, then styles are merged with the default styles, with the
  591.      * user defined styles having priority
  592.      *
  593.      * Note: Use this method to override any style changes you made to
  594.      * the line numbers if you are using line numbers, else the line of
  595.      * code will have the same style as the line number! Consult the
  596.      * GeSHi documentation for more information about this.
  597.      *
  598.      * @param string  The style to use for actual code
  599.      * @param boolean Whether to merge the current styles with the new styles
  600.      */
  601.     function set_code_style ($style$preserve_defaults false)
  602.     {
  603.         if (!$preserve_defaults{
  604.             $this->code_style $style;
  605.         else {
  606.             $this->code_style .= $style;
  607.         }
  608.     }
  609.  
  610.     /**
  611.      * Sets the styles for the line numbers.
  612.      *
  613.      * @param string The style for the line numbers that are "normal"
  614.      * @param string|booleanIf a string, this is the style of the line
  615.      *         numbers that are "fancy", otherwise if boolean then this
  616.      *         defines whether the normal styles should be merged with the
  617.      *         new normal styles or not
  618.      * @param boolean If set, is the flag for whether to merge the "fancy"
  619.      *         styles with the current styles or not
  620.      * @since 1.0.2
  621.      */
  622.     function set_line_style ($style1$style2 ''$preserve_defaults false)
  623.     {
  624.         if (is_bool($style2)) {
  625.             $preserve_defaults $style2;
  626.             $style2 '';
  627.         }
  628.         if (!$preserve_defaults{
  629.             $this->line_style1 $style1;
  630.             $this->line_style2 $style2;
  631.         else {
  632.             $this->line_style1 .= $style1;
  633.             $this->line_style2 .= $style2;
  634.         }
  635.     }
  636.  
  637.     /**
  638.      * Sets whether line numbers should be displayed.
  639.      *
  640.      * Valid values for the first parameter are:
  641.      *
  642.      * <ul>
  643.      *   <li><b>GESHI_NO_LINE_NUMBERS</b>: Line numbers will not be displayed</li>
  644.      *   <li><b>GESHI_NORMAL_LINE_NUMBERS</b>: Line numbers will be displayed</li>
  645.      *   <li><b>GESHI_FANCY_LINE_NUMBERS</b>: Fancy line numbers will be displayed</li>
  646.      * </ul>
  647.      *
  648.      * For fancy line numbers, the second parameter is used to signal which lines
  649.      * are to be fancy. For example, if the value of this parameter is 5 then every
  650.      * 5th line will be fancy.
  651.      *
  652.      * @param int How line numbers should be displayed
  653.      * @param int Defines which lines are fancy
  654.      * @since 1.0.0
  655.      */
  656.     function enable_line_numbers ($flag$nth_row 5)
  657.     {
  658.         if (GESHI_NO_LINE_NUMBERS != $flag && GESHI_NORMAL_LINE_NUMBERS != $flag
  659.             && GESHI_FANCY_LINE_NUMBERS != $flag{
  660.             $this->error GESHI_ERROR_INVALID_LINE_NUMBER_TYPE;
  661.         }
  662.         $this->line_numbers $flag;
  663.         $this->line_nth_row $nth_row;
  664.     }
  665.  
  666.     /**
  667.      * Sets the style for a keyword group. If $preserve_defaults is
  668.      * true, then styles are merged with the default styles, with the
  669.      * user defined styles having priority
  670.      *
  671.      * @param int     The key of the keyword group to change the styles of
  672.      * @param string  The style to make the keywords
  673.      * @param boolean Whether to merge the new styles with the old or just
  674.      *                 to overwrite them
  675.      * @since 1.0.0
  676.      */
  677.     function set_keyword_group_style ($key$style$preserve_defaults false)
  678.     {
  679.         if (!$preserve_defaults{
  680.             $this->language_data['STYLES']['KEYWORDS'][$key$style;
  681.         else {
  682.             $this->language_data['STYLES']['KEYWORDS'][$key.= $style;
  683.         }
  684.     }
  685.  
  686.     /**
  687.      * Turns highlighting on/off for a keyword group
  688.      *
  689.      * @param int     The key of the keyword group to turn on or off
  690.      * @param boolean Whether to turn highlighting for that group on or off
  691.      * @since 1.0.0
  692.      */
  693.     function set_keyword_group_highlighting $key$flag true )
  694.     {
  695.         $this->lexic_permissions['KEYWORDS'][$key($flagtrue false;
  696.     }
  697.  
  698.     /**
  699.      * Sets the styles for comment groups.  If $preserve_defaults is
  700.      * true, then styles are merged with the default styles, with the
  701.      * user defined styles having priority
  702.      *
  703.      * @param int     The key of the comment group to change the styles of
  704.      * @param string  The style to make the comments
  705.      * @param boolean Whether to merge the new styles with the old or just
  706.      *                 to overwrite them
  707.      * @since 1.0.0
  708.      */
  709.     function set_comments_style ($key$style$preserve_defaults false)
  710.     {
  711.         if (!$preserve_defaults{
  712.             $this->language_data['STYLES']['COMMENTS'][$key$style;
  713.         else {
  714.             $this->language_data['STYLES']['COMMENTS'][$key.= $style;
  715.         }
  716.     }
  717.  
  718.     /**
  719.      * Turns highlighting on/off for comment groups
  720.      *
  721.      * @param int     The key of the comment group to turn on or off
  722.      * @param boolean Whether to turn highlighting for that group on or off
  723.      * @since 1.0.0
  724.      */
  725.     function set_comments_highlighting ($key$flag true)
  726.     {
  727.         $this->lexic_permissions['COMMENTS'][$key($flagtrue false;
  728.     }
  729.  
  730.     /**
  731.      * Sets the styles for escaped characters. If $preserve_defaults is
  732.      * true, then styles are merged with the default styles, with the
  733.      * user defined styles having priority
  734.      *
  735.      * @param string  The style to make the escape characters
  736.      * @param boolean Whether to merge the new styles with the old or just
  737.      *                 to overwrite them
  738.      * @since 1.0.0
  739.      */
  740.     function set_escape_characters_style ($style$preserve_defaults false)
  741.     {
  742.         if (!$preserve_defaults{
  743.             $this->language_data['STYLES']['ESCAPE_CHAR'][0$style;
  744.         else {
  745.             $this->language_data['STYLES']['ESCAPE_CHAR'][0.= $style;
  746.         }
  747.     }
  748.  
  749.     /**
  750.      * Turns highlighting on/off for escaped characters
  751.      *
  752.      * @param boolean Whether to turn highlighting for escape characters on or off
  753.      * @since 1.0.0
  754.      */
  755.     function set_escape_characters_highlighting ($flag true)
  756.     {
  757.         $this->lexic_permissions['ESCAPE_CHAR'($flagtrue false;
  758.     }
  759.  
  760.     /**
  761.      * Sets the styles for brackets. If $preserve_defaults is
  762.      * true, then styles are merged with the default styles, with the
  763.      * user defined styles having priority
  764.      *
  765.      * This method is DEPRECATED: use set_symbols_style instead.
  766.      * This method will be removed in 1.2.X
  767.      *
  768.      * @param string  The style to make the brackets
  769.      * @param boolean Whether to merge the new styles with the old or just
  770.      *                 to overwrite them
  771.      * @since 1.0.0
  772.      * @deprecated In favour of set_symbols_style
  773.      */
  774.     function set_brackets_style ($style$preserve_defaults false)
  775.     {
  776.         if (!$preserve_defaults{
  777.             $this->language_data['STYLES']['BRACKETS'][0$style;
  778.         else {
  779.             $this->language_data['STYLES']['BRACKETS'][0.= $style;
  780.         }
  781.     }
  782.  
  783.     /**
  784.      * Turns highlighting on/off for brackets
  785.      *
  786.      * This method is DEPRECATED: use set_symbols_highlighting instead.
  787.      * This method will be remove in 1.2.X
  788.      *
  789.      * @param boolean Whether to turn highlighting for brackets on or off
  790.      * @since 1.0.0
  791.      * @deprecated In favour of set_symbols_highlighting
  792.      */
  793.     function set_brackets_highlighting ($flag)
  794.     {
  795.         $this->lexic_permissions['BRACKETS'($flagtrue false;
  796.     }
  797.  
  798.     /**
  799.      * Sets the styles for symbols. If $preserve_defaults is
  800.      * true, then styles are merged with the default styles, with the
  801.      * user defined styles having priority
  802.      *
  803.      * @param string  The style to make the symbols
  804.      * @param boolean Whether to merge the new styles with the old or just
  805.      *                 to overwrite them
  806.      * @since 1.0.1
  807.      */
  808.     function set_symbols_style ($style$preserve_defaults false)
  809.     {
  810.         if (!$preserve_defaults{
  811.             $this->language_data['STYLES']['SYMBOLS'][0$style;
  812.         else {
  813.             $this->language_data['STYLES']['SYMBOLS'][0.= $style;
  814.         }
  815.         // For backward compatibility
  816.         $this->set_brackets_style ($style$preserve_defaults);
  817.     }
  818.  
  819.     /**
  820.      * Turns highlighting on/off for symbols
  821.      *
  822.      * @param boolean Whether to turn highlighting for symbols on or off
  823.      * @since 1.0.0
  824.      */
  825.     function set_symbols_highlighting ($flag)
  826.     {
  827.         $this->lexic_permissions['SYMBOLS'($flagtrue false;
  828.         // For backward compatibility
  829.         $this->set_brackets_highlighting ($flag);
  830.     }
  831.  
  832.     /**
  833.      * Sets the styles for strings. If $preserve_defaults is
  834.      * true, then styles are merged with the default styles, with the
  835.      * user defined styles having priority
  836.      *
  837.      * @param string  The style to make the escape characters
  838.      * @param boolean Whether to merge the new styles with the old or just
  839.      *                 to overwrite them
  840.      * @since 1.0.0
  841.      */
  842.     function set_strings_style ($style$preserve_defaults false)
  843.     {
  844.         if (!$preserve_defaults{
  845.             $this->language_data['STYLES']['STRINGS'][0$style;
  846.         else {
  847.             $this->language_data['STYLES']['STRINGS'][0.= $style;
  848.         }
  849.     }
  850.  
  851.     /**
  852.      * Turns highlighting on/off for strings
  853.      *
  854.      * @param boolean Whether to turn highlighting for strings on or off
  855.      * @since 1.0.0
  856.      */
  857.     function set_strings_highlighting ($flag)
  858.     {
  859.         $this->lexic_permissions['STRINGS'($flagtrue false;
  860.     }
  861.  
  862.     /**
  863.      * Sets the styles for numbers. If $preserve_defaults is
  864.      * true, then styles are merged with the default styles, with the
  865.      * user defined styles having priority
  866.      *
  867.      * @param string  The style to make the numbers
  868.      * @param boolean Whether to merge the new styles with the old or just
  869.      *                 to overwrite them
  870.      * @since 1.0.0
  871.      */
  872.     function set_numbers_style ($style$preserve_defaults false)
  873.     {
  874.         if (!$preserve_defaults{
  875.             $this->language_data['STYLES']['NUMBERS'][0$style;
  876.         else {
  877.             $this->language_data['STYLES']['NUMBERS'][0.= $style;
  878.         }
  879.     }
  880.  
  881.     /**
  882.      * Turns highlighting on/off for numbers
  883.      *
  884.      * @param boolean Whether to turn highlighting for numbers on or off
  885.      * @since 1.0.0
  886.      */
  887.     function set_numbers_highlighting ($flag)
  888.     {
  889.         $this->lexic_permissions['NUMBERS'($flagtrue false;
  890.     }
  891.  
  892.     /**
  893.      * Sets the styles for methods. $key is a number that references the
  894.      * appropriate "object splitter" - see the language file for the language
  895.      * you are highlighting to get this number. If $preserve_defaults is
  896.      * true, then styles are merged with the default styles, with the
  897.      * user defined styles having priority
  898.      *
  899.      * @param int     The key of the object splitter to change the styles of
  900.      * @param string  The style to make the methods
  901.      * @param boolean Whether to merge the new styles with the old or just
  902.      *                 to overwrite them
  903.      * @since 1.0.0
  904.      */
  905.     function set_methods_style ($key$style$preserve_defaults false)
  906.     {
  907.         if (!$preserve_defaults{
  908.             $this->language_data['STYLES']['METHODS'][$key$style;
  909.         else {
  910.             $this->language_data['STYLES']['METHODS'][$key.= $style;
  911.         }
  912.     }
  913.  
  914.     /**
  915.      * Turns highlighting on/off for methods
  916.      *
  917.      * @param boolean Whether to turn highlighting for methods on or off
  918.      * @since 1.0.0
  919.      */
  920.     function set_methods_highlighting ($flag)
  921.     {
  922.         $this->lexic_permissions['METHODS'($flagtrue false;
  923.     }
  924.  
  925.     /**
  926.      * Sets the styles for regexps. If $preserve_defaults is
  927.      * true, then styles are merged with the default styles, with the
  928.      * user defined styles having priority
  929.      *
  930.      * @param string  The style to make the regular expression matches
  931.      * @param boolean Whether to merge the new styles with the old or just
  932.      *                 to overwrite them
  933.      * @since 1.0.0
  934.      */
  935.     function set_regexps_style ($key$style$preserve_defaults false)
  936.     {
  937.         if (!$preserve_defaults{
  938.             $this->language_data['STYLES']['REGEXPS'][$key$style;
  939.         else {
  940.             $this->language_data['STYLES']['REGEXPS'][$key.= $style;
  941.         }
  942.     }
  943.  
  944.     /**
  945.      * Turns highlighting on/off for regexps
  946.      *
  947.      * @param int     The key of the regular expression group to turn on or off
  948.      * @param boolean Whether to turn highlighting for the regular expression group on or off
  949.      * @since 1.0.0
  950.      */
  951.     function set_regexps_highlighting ($key$flag)
  952.     {
  953.         $this->lexic_permissions['REGEXPS'][$key($flagtrue false;
  954.     }
  955.  
  956.     /**
  957.      * Sets whether a set of keywords are checked for in a case sensitive manner
  958.      *
  959.      * @param int The key of the keyword group to change the case sensitivity of
  960.      * @param boolean Whether to check in a case sensitive manner or not
  961.      * @since 1.0.0
  962.      */
  963.     function set_case_sensitivity ($key$case)
  964.     {
  965.         $this->language_data['CASE_SENSITIVE'][$key($casetrue false;
  966.     }
  967.  
  968.     /**
  969.      * Sets the case that keywords should use when found. Use the constants:
  970.      *
  971.      * <ul>
  972.      *   <li><b>GESHI_CAPS_NO_CHANGE</b>: leave keywords as-is</li>
  973.      *   <li><b>GESHI_CAPS_UPPER</b>: convert all keywords to uppercase where found</li>
  974.      *   <li><b>GESHI_CAPS_LOWER</b>: convert all keywords to lowercase where found</li>
  975.      * </ul>
  976.      *
  977.      * @param int A constant specifying what to do with matched keywords
  978.      * @since 1.0.1
  979.      * @todo  Error check the passed value
  980.      */
  981.     function set_case_keywords ($case)
  982.     {
  983.         $this->language_data['CASE_KEYWORDS'$case;
  984.     }
  985.  
  986.     /**
  987.      * Sets how many spaces a tab is substituted for
  988.      *
  989.      * Widths below zero are ignored
  990.      *
  991.      * @param int The tab width
  992.      * @since 1.0.0
  993.      */
  994.     function set_tab_width ($width)
  995.     {
  996.         $this->tab_width intval($width);
  997.     }
  998.  
  999.     /**
  1000.      * Enables/disables strict highlighting. Default is off, calling this
  1001.      * method without parameters will turn it on. See documentation
  1002.      * for more details on strict mode and where to use it.
  1003.      *
  1004.      * @param boolean Whether to enable strict mode or not
  1005.      * @since 1.0.0
  1006.      */
  1007.     function enable_strict_mode ($mode true)
  1008.     {
  1009.         if (GESHI_MAYBE == $this->language_data['STRICT_MODE_APPLIES']{
  1010.           $this->strict_mode ($modetrue false;
  1011.         }
  1012.     }
  1013.  
  1014.     /**
  1015.      * Disables all highlighting
  1016.      *
  1017.      * @since 1.0.0
  1018.      * @todo Rewrite with an array traversal
  1019.      */
  1020.     function disable_highlighting ()
  1021.     {
  1022.         foreach ($this->lexic_permissions as $key => $value{
  1023.             if (is_array($value)) {
  1024.                 foreach ($value as $k => $v{
  1025.                     $this->lexic_permissions[$key][$kfalse;
  1026.                 }
  1027.             else {
  1028.                 $this->lexic_permissions[$keyfalse;
  1029.             }
  1030.         }
  1031.         // Context blocks
  1032.         $this->enable_important_blocks false;
  1033.     }
  1034.  
  1035.     /**
  1036.      * Enables all highlighting
  1037.      *
  1038.      * @since 1.0.0
  1039.      * @todo  Rewrite with array traversal
  1040.      */
  1041.     function enable_highlighting ()
  1042.     {
  1043.         foreach ($this->lexic_permissions as $key => $value{
  1044.             if (is_array($value)) {
  1045.                 foreach ($value as $k => $v{
  1046.                     $this->lexic_permissions[$key][$ktrue;
  1047.                 }
  1048.             else {
  1049.                 $this->lexic_permissions[$keytrue;
  1050.             }
  1051.         }
  1052.         // Context blocks
  1053.         $this->enable_important_blocks true;
  1054.     }
  1055.  
  1056.     /**
  1057.      * Given a file extension, this method returns either a valid geshi language
  1058.      * name, or the empty string if it couldn't be found
  1059.      *
  1060.      * @param string The extension to get a language name for
  1061.      * @param array  A lookup array to use instead of the default
  1062.      * @since 1.0.5
  1063.      * @todo Re-think about how this method works (maybe make it private and/or make it
  1064.      *        a extension->lang lookup?)
  1065.      * @todo static?
  1066.      */
  1067.     function get_language_name_from_extension $extension$lookup array() )
  1068.     {
  1069.         if !$lookup )
  1070.         {
  1071.             $lookup array(
  1072.                 'actionscript' => array('as'),
  1073.                 'ada' => array('a''ada''adb''ads'),
  1074.                 'apache' => array('conf'),
  1075.                 'asm' => array('ash''asm'),
  1076.                 'asp' => array('asp'),
  1077.                 'bash' => array('sh'),
  1078.                 'c' => array('c'),
  1079.                 'c_mac' => array('c'),
  1080.                 'caddcl' => array(),
  1081.                 'cadlisp' => array(),
  1082.                 'cpp' => array('cpp'),
  1083.                 'csharp' => array(),
  1084.                 'css' => array('css'),
  1085.                 'delphi' => array('dpk''dpr'),
  1086.                 'html4strict' => array('html''htm'),
  1087.                 'java' => array('java'),
  1088.                 'javascript' => array('js'),
  1089.                 'lisp' => array('lisp'),
  1090.                 'lua' => array('lua'),
  1091.                 'mpasm' => array(),
  1092.                 'nsis' => array(),
  1093.                 'objc' => array(),
  1094.                 'oobas' => array(),
  1095.                 'oracle8' => array(),
  1096.                 'pascal' => array('pas'),
  1097.                 'perl' => array('pl''pm'),
  1098.                 'php' => array('php''php5''phtml''phps'),
  1099.                 'python' => array('py'),
  1100.                 'qbasic' => array('bi'),
  1101.                 'smarty' => array(),
  1102.                 'vb' => array('bas'),
  1103.                 'vbnet' => array(),
  1104.                 'visualfoxpro' => array(),
  1105.                 'xml' => array('xml')
  1106.             );
  1107.         }
  1108.  
  1109.         foreach ($lookup as $lang => $extensions{
  1110.             foreach ($extensions as $ext{
  1111.                 if ($ext == $extension{
  1112.                     return $lang;
  1113.                 }
  1114.             }
  1115.         }
  1116.         return '';
  1117.     }
  1118.  
  1119.     /**
  1120.      * Given a file name, this method loads its contents in, and attempts
  1121.      * to set the language automatically. An optional lookup table can be
  1122.      * passed for looking up the language name. If not specified a default
  1123.      * table is used
  1124.      *
  1125.      * The language table is in the form
  1126.      * <pre>array(
  1127.      *   'lang_name' => array('extension', 'extension', ...),
  1128.      *   'lang_name' ...
  1129.      * );</pre>
  1130.      *
  1131.      * @todo Complete rethink of this and above method
  1132.      * @since 1.0.5
  1133.      */
  1134.     function load_from_file ($file_name$lookup array())
  1135.     {
  1136.         if (is_readable($file_name)) {
  1137.             $this->set_source(implode(''file($file_name)));
  1138.             $this->set_language($this->get_language_name_from_extension(substr(strrchr($file_name'.')1)$lookup));
  1139.         else {
  1140.             $this->error GESHI_ERROR_FILE_NOT_READABLE;
  1141.         }
  1142.     }
  1143.  
  1144.     /**
  1145.      * Adds a keyword to a keyword group for highlighting
  1146.      *
  1147.      * @param int    The key of the keyword group to add the keyword to
  1148.      * @param string The word to add to the keyword group
  1149.      * @since 1.0.0
  1150.      */
  1151.     function add_keyword ($key$word)
  1152.     {
  1153.         $this->language_data['KEYWORDS'][$key][$word;
  1154.     }
  1155.  
  1156.     /**
  1157.      * Removes a keyword from a keyword group
  1158.      *
  1159.      * @param int    The key of the keyword group to remove the keyword from
  1160.      * @param string The word to remove from the keyword group
  1161.      * @since 1.0.0
  1162.      */
  1163.     function remove_keyword ($key$word)
  1164.     {
  1165.         $this->language_data['KEYWORDS'][$key=
  1166.             array_diff($this->language_data['KEYWORDS'][$key]array($word));
  1167.     }
  1168.  
  1169.     /**
  1170.      * Creates a new keyword group
  1171.      *
  1172.      * @param int    The key of the keyword group to create
  1173.      * @param string The styles for the keyword group
  1174.      * @param boolean Whether the keyword group is case sensitive ornot
  1175.      * @param array  The words to use for the keyword group
  1176.      * @since 1.0.0
  1177.      */
  1178.     function add_keyword_group $key$styles$case_sensitive true$words array() )
  1179.     {
  1180.         $words = (array) $words;
  1181.         $this->language_data['KEYWORDS'][$key$words;
  1182.         $this->lexic_permissions['KEYWORDS'][$keytrue;
  1183.         $this->language_data['CASE_SENSITIVE'][$key$case_sensitive;
  1184.         $this->language_data['STYLES']['KEYWORDS'][$key$styles;
  1185.     }
  1186.  
  1187.     /**
  1188.      * Removes a keyword group
  1189.      *
  1190.      * @param int    The key of the keyword group to remove
  1191.      * @since 1.0.0
  1192.      */
  1193.     function remove_keyword_group ($key)
  1194.     {
  1195.         unset($this->language_data['KEYWORDS'][$key]);
  1196.         unset($this->lexic_permissions['KEYWORDS'][$key]);
  1197.         unset($this->language_data['CASE_SENSITIVE'][$key]);
  1198.         unset($this->language_data['STYLES']['KEYWORDS'][$key]);
  1199.     }
  1200.  
  1201.     /**
  1202.      * Sets the content of the header block
  1203.      *
  1204.      * @param string The content of the header block
  1205.      * @since 1.0.2
  1206.      */
  1207.     function set_header_content ($content)
  1208.     {
  1209.         $this->header_content $content;
  1210.     }
  1211.  
  1212.     /**
  1213.      * Sets the content of the footer block
  1214.      *
  1215.      * @param string The content of the footer block
  1216.      * @since 1.0.2
  1217.      */
  1218.     function set_footer_content ($content)
  1219.     {
  1220.         $this->footer_content $content;
  1221.     }
  1222.  
  1223.     /**
  1224.      * Sets the style for the header content
  1225.      *
  1226.      * @param string The style for the header content
  1227.      * @since 1.0.2
  1228.      */
  1229.     function set_header_content_style ($style)
  1230.     {
  1231.         $this->header_content_style $style;
  1232.     }
  1233.  
  1234.     /**
  1235.      * Sets the style for the footer content
  1236.      *
  1237.      * @param string The style for the footer content
  1238.      * @since 1.0.2
  1239.      */
  1240.     function set_footer_content_style ($style)
  1241.     {
  1242.         $this->footer_content_style $style;
  1243.     }
  1244.  
  1245.     /**
  1246.      * Sets the base URL to be used for keywords
  1247.      *
  1248.      * @param int The key of the keyword group to set the URL for
  1249.      * @param string The URL to set for the group. If {FNAME} is in
  1250.      *                the url somewhere, it is replaced by the keyword
  1251.      *                that the URL is being made for
  1252.      * @since 1.0.2
  1253.      */
  1254.     function set_url_for_keyword_group ($group$url)
  1255.     {
  1256.         $this->language_data['URLS'][$group$url;
  1257.     }
  1258.  
  1259.     /**
  1260.      * Sets styles for links in code
  1261.      *
  1262.      * @param int A constant that specifies what state the style is being
  1263.      *             set for - e.g. :hover or :visited
  1264.      * @param string The styles to use for that state
  1265.      * @since 1.0.2
  1266.      */
  1267.     function set_link_styles ($type$styles)
  1268.     {
  1269.         $this->link_styles[$type$styles;
  1270.     }
  1271.  
  1272.     /**
  1273.      * Sets the target for links in code
  1274.      *
  1275.      * @param string The target for links in the code, e.g. _blank
  1276.      * @since 1.0.3
  1277.      */
  1278.     function set_link_target $target )
  1279.     {
  1280.         if (!$target{
  1281.             $this->link_target '';
  1282.         else {
  1283.             $this->link_target ' target="' $target '" ';
  1284.         }
  1285.     }
  1286.  
  1287.     /**
  1288.      * Sets styles for important parts of the code
  1289.      *
  1290.      * @param string The styles to use on important parts of the code
  1291.      * @since 1.0.2
  1292.      */
  1293.     function set_important_styles ($styles)
  1294.     {
  1295.         $this->important_styles $styles;
  1296.     }
  1297.  
  1298.     /**
  1299.      * Sets whether context-important blocks are highlighted
  1300.      *
  1301.      * @todo REMOVE THIS SHIZ FROM GESHI!
  1302.      * @deprecated
  1303.      */
  1304.     function enable_important_blocks $flag )
  1305.     {
  1306.         $this->enable_important_blocks $flag true false;
  1307.     }
  1308.  
  1309.     /**
  1310.      * Whether CSS IDs should be added to each line
  1311.      *
  1312.      * @param boolean If true, IDs will be added to each line.
  1313.      * @since 1.0.2
  1314.      */
  1315.     function enable_ids ($flag true)
  1316.     {
  1317.         $this->add_ids ($flagtrue false;
  1318.     }
  1319.  
  1320.     /**
  1321.      * Specifies which lines to highlight extra
  1322.      *
  1323.      * @param mixed An array of line numbers to highlight, or just a line
  1324.      *               number on its own.
  1325.      * @since 1.0.2
  1326.      * @todo  Some data replication here that could be cut down on
  1327.      */
  1328.     function highlight_lines_extra ($lines)
  1329.     {
  1330.         if (is_array($lines)) {
  1331.             foreach ($lines as $line{
  1332.                 $this->highlight_extra_lines[intval($line)intval($line);
  1333.             }
  1334.         else {
  1335.             $this->highlight_extra_lines[intval($lines)intval($lines);
  1336.         }
  1337.     }
  1338.  
  1339.     /**
  1340.      * Sets the style for extra-highlighted lines
  1341.      *
  1342.      * @param string The style for extra-highlighted lines
  1343.      * @since 1.0.2
  1344.      */
  1345.     function set_highlight_lines_extra_style ($styles)
  1346.     {
  1347.         $this->highlight_extra_lines_style $styles;
  1348.     }
  1349.  
  1350.     /**
  1351.      * Sets what number line numbers should start at. Should
  1352.      * be a positive integer, and will be converted to one.
  1353.      *
  1354.      * <b>Warning:</b> Using this method will add the "start"
  1355.      * attribute to the &lt;ol&gt; that is used for line numbering.
  1356.      * This is <b>not</b> valid XHTML strict, so if that's what you
  1357.      * care about then don't use this method. Firefox is getting
  1358.      * support for the CSS method of doing this in 1.1 and Opera
  1359.      * has support for the CSS method, but (of course) IE doesn't
  1360.      * so it's not worth doing it the CSS way yet.
  1361.      *
  1362.      * @param int The number to start line numbers at
  1363.      * @since 1.0.2
  1364.      */
  1365.     function start_line_numbers_at ($number)
  1366.     {
  1367.         $this->line_numbers_start abs(intval($number));
  1368.     }
  1369.  
  1370.     /**
  1371.      * Sets the encoding used for htmlspecialchars(), for international
  1372.      * support.
  1373.      *
  1374.      * @param string The encoding to use for the source
  1375.      * @since 1.0.3
  1376.      */
  1377.     function set_encoding ($encoding)
  1378.     {
  1379.         if ($encoding{
  1380.           $this->encoding $encoding;
  1381.         }
  1382.     }
  1383.  
  1384.     /**
  1385.      * Returns the code in $this->source, highlighted and surrounded by the
  1386.      * nessecary HTML.
  1387.      *
  1388.      * This should only be called ONCE, cos it's SLOW! If you want to highlight
  1389.      * the same source multiple times, you're better off doing a whole lot of
  1390.      * str_replaces to replace the &lt;span&gt;s
  1391.      *
  1392.      * @since 1.0.0
  1393.      */
  1394.     function parse_code ()
  1395.     {
  1396.         // Start the timer
  1397.         $start_time microtime();
  1398.  
  1399.         // Firstly, if there is an error, we won't highlight
  1400.         if ($this->error{
  1401.             $result @htmlspecialchars($this->sourceENT_COMPAT$this->encoding);
  1402.             // Timing is irrelevant
  1403.             $this->set_time($start_time$start_time);
  1404.             return $this->finalise($result);
  1405.         }
  1406.  
  1407.         // Add spaces for regular expression matching and line numbers
  1408.         $code ' ' $this->source ' ';
  1409.         // Replace all newlines to a common form.
  1410.         $code str_replace("\r\n""\n"$code);
  1411.         $code str_replace("\r""\n"$code);
  1412.  
  1413.         // Initialise various stuff
  1414.         $length           strlen($code);
  1415.         $STRING_OPEN      '';
  1416.         $CLOSE_STRING     false;
  1417.         $ESCAPE_CHAR_OPEN false;
  1418.         $COMMENT_MATCHED  false;
  1419.         // Turn highlighting on if strict mode doesn't apply to this language
  1420.         $HIGHLIGHTING_ON  !$this->strict_mode true '';
  1421.         // Whether to highlight inside a block of code
  1422.         $HIGHLIGHT_INSIDE_STRICT false;
  1423.         $HARDQUOTE_OPEN false;
  1424.         $stuff_to_parse   '';
  1425.         $result           '';
  1426.  
  1427.         // "Important" selections are handled like multiline comments
  1428.         // @todo GET RID OF THIS SHIZ
  1429.         if ($this->enable_important_blocks{
  1430.             $this->language_data['COMMENT_MULTI'][GESHI_START_IMPORTANTGESHI_END_IMPORTANT;
  1431.         }
  1432.  
  1433.         if ($this->strict_mode{
  1434.             // Break the source into bits. Each bit will be a portion of the code
  1435.             // within script delimiters - for example, HTML between < and >
  1436.             $parts array(=> array(=> ''));
  1437.             $k 0;
  1438.             for ($i 0$i $length$i++{
  1439.                 $char substr($code$i1);
  1440.                 if (!$HIGHLIGHTING_ON{
  1441.                     foreach ($this->language_data['SCRIPT_DELIMITERS'as $key => $delimiters{
  1442.                         foreach ($delimiters as $open => $close{
  1443.                             // Get the next little bit for this opening string
  1444.                             $check substr($code$istrlen($open));
  1445.                             // If it matches...
  1446.                             if ($check == $open{
  1447.                                 // We start a new block with the highlightable
  1448.                                 // code in it
  1449.                                 $HIGHLIGHTING_ON $open;
  1450.                                 $i += strlen($open1;
  1451.                                 $char $open;
  1452.                                 $parts[++$k][0$char;
  1453.  
  1454.                                 // No point going around again...
  1455.                                 break(2);
  1456.                             }
  1457.                         }
  1458.                     }
  1459.                 else {
  1460.                     foreach ($this->language_data['SCRIPT_DELIMITERS'as $key => $delimiters{
  1461.                         foreach ($delimiters as $open => $close{
  1462.                             if ($open == $HIGHLIGHTING_ON{
  1463.                                 // Found the closing tag
  1464.                                 break(2);
  1465.                             }
  1466.                         }
  1467.                     }
  1468.                     // We check code from our current position BACKWARDS. This is so
  1469.                     // the ending string for highlighting can be included in the block
  1470.                     $check substr($code$i strlen($close1strlen($close));
  1471.                     if ($check == $close{
  1472.                         $HIGHLIGHTING_ON '';
  1473.                         // Add the string to the rest of the string for this part
  1474.                         $parts[$k][1isset($parts[$k][1]) ) $parts[$k][1$char $char;
  1475.                         $parts[++$k][0'';
  1476.                         $char '';
  1477.                     }
  1478.                 }
  1479.                 $parts[$k][1isset($parts[$k][1]) ) $parts[$k][1$char $char;
  1480.             }
  1481.             $HIGHLIGHTING_ON '';
  1482.         else {
  1483.             // Not strict mode - simply dump the source into
  1484.             // the array at index 1 (the first highlightable block)
  1485.             $parts array(
  1486.                 => array(
  1487.                     => '',
  1488.                     => $code
  1489.                 )
  1490.             );
  1491.         }
  1492.  
  1493.         // Now we go through each part. We know that even-indexed parts are
  1494.         // code that shouldn't be highlighted, and odd-indexed parts should
  1495.         // be highlighted
  1496.         foreach ($parts as $key => $data{
  1497.             $part $data[1];
  1498.             // If this block should be highlighted...
  1499.             if ($key 2{
  1500.                 if ($this->strict_mode{
  1501.                     // Find the class key for this block of code
  1502.                     foreach ($this->language_data['SCRIPT_DELIMITERS'as $script_key => $script_data{
  1503.                         foreach ($script_data as $open => $close{
  1504.                             if ($data[0== $open{
  1505.                                 break(2);
  1506.                             }
  1507.                         }
  1508.                     }
  1509.  
  1510.                     if ($this->language_data['STYLES']['SCRIPT'][$script_key!= '' &&
  1511.                         $this->lexic_permissions['SCRIPT']{
  1512.                         // Add a span element around the source to
  1513.                         // highlight the overall source block
  1514.                         if (!$this->use_classes &&
  1515.                             $this->language_data['STYLES']['SCRIPT'][$script_key!= ''{
  1516.                             $attributes ' style="' $this->language_data['STYLES']['SCRIPT'][$script_key'"';
  1517.                         else {
  1518.                             $attributes ' class="sc' $script_key '"';
  1519.                         }
  1520.                         $result .= "<span$attributes>";
  1521.                     }
  1522.                 }
  1523.  
  1524.                 if (!$this->strict_mode || $this->language_data['HIGHLIGHT_STRICT_BLOCK'][$script_key]{
  1525.                     // Now, highlight the code in this block. This code
  1526.                     // is really the engine of GeSHi (along with the method
  1527.                     // parse_non_string_part).
  1528.                     $length strlen($part);
  1529.                     for ($i 0$i $length$i++{
  1530.                         // Get the next char
  1531.                         $char substr($part$i1);
  1532.                         $hq = isset($this->language_data['HARDQUOTE']$this->language_data['HARDQUOTE'][0false;
  1533.                         // Is this char the newline and line numbers being used?
  1534.                         if (($this->line_numbers != GESHI_NO_LINE_NUMBERS
  1535.                             || count($this->highlight_extra_lines0)
  1536.                             && $char == "\n"{
  1537.                             // If so, is there a string open? If there is, we should end it before
  1538.                             // the newline and begin it again (so when <li>s are put in the source
  1539.                             // remains XHTML compliant)
  1540.                             // note to self: This opens up possibility of config files specifying
  1541.                             // that languages can/cannot have multiline strings???
  1542.                             if ($STRING_OPEN{
  1543.                                 if (!$this->use_classes{
  1544.                                     $attributes ' style="' $this->language_data['STYLES']['STRINGS'][0'"';
  1545.                                 else {
  1546.                                     $attributes ' class="st0"';
  1547.                                 }
  1548.                                 $char '</span>' $char "<span$attributes>";
  1549.                             }
  1550.                         elseif ($char == $STRING_OPEN{
  1551.                             // A match of a string delimiter
  1552.                             if (($this->lexic_permissions['ESCAPE_CHAR'&& $ESCAPE_CHAR_OPEN||
  1553.                                 ($this->lexic_permissions['STRINGS'&& !$ESCAPE_CHAR_OPEN)) {
  1554.                                 $char .= '</span>';
  1555.                             }
  1556.                             $escape_me false;
  1557.                             if ($HARDQUOTE_OPEN)
  1558.                             {
  1559.                                 if ($ESCAPE_CHAR_OPEN)
  1560.                                 $escape_me true;
  1561.                                 else {
  1562.                                     foreach ($this->language_data['HARDESCAPE'as $hardesc)
  1563.                                     if (substr($part$istrlen($hardesc)) == $hardesc)
  1564.                                     {
  1565.                                         $escape_me true;
  1566.                                         break;
  1567.                                     }
  1568.                                 }
  1569.                             }
  1570.                             if (!$ESCAPE_CHAR_OPEN{
  1571.                                 $STRING_OPEN '';
  1572.                                 $CLOSE_STRING true;
  1573.                             }
  1574.                             if (!$escape_me{
  1575.                                 $HARDQUOTE_OPEN false;
  1576.                             }
  1577.                             $ESCAPE_CHAR_OPEN false;
  1578.                         elseif (in_array($char$this->language_data['QUOTEMARKS']&&
  1579.                             ($STRING_OPEN == ''&& $this->lexic_permissions['STRINGS']{
  1580.                             // The start of a new string
  1581.                             $STRING_OPEN $char;
  1582.                             if (!$this->use_classes{
  1583.                                 $attributes ' style="' $this->language_data['STYLES']['STRINGS'][0'"';
  1584.                             else {
  1585.                                 $attributes ' class="st0"';
  1586.                             }
  1587.                             $char "<span$attributes>$char;
  1588.  
  1589.                             $result .= $this->parse_non_string_part$stuff_to_parse );
  1590.                             $stuff_to_parse '';
  1591.                         elseif (
  1592.                         $hq &&
  1593.                         substr($part$istrlen($hq)) == $hq &&
  1594.                         ($STRING_OPEN == ''&& $this->lexic_permissions['STRINGS']
  1595.                         )
  1596.                         {
  1597.                         // The start of a hard quoted string
  1598.                         $STRING_OPEN $this->language_data['HARDQUOTE'][1];
  1599.                         if (!$this->use_classes{
  1600.                             $attributes ' style="' $this->language_data['STYLES']['STRINGS'][0'"';
  1601.                         else {
  1602.                             $attributes ' class="st0"';
  1603.                         }
  1604.                         $char "<span$attributes>$hq;
  1605.                         $i += strlen($hq1;
  1606.                         $HARDQUOTE_OPEN true;
  1607.                         $result .= $this->parse_non_string_part$stuff_to_parse );
  1608.                         $stuff_to_parse '';
  1609.                         elseif ($char == $this->language_data['ESCAPE_CHAR'&& $STRING_OPEN != '')
  1610.                         {
  1611.                             // An escape character
  1612.                             if (!$ESCAPE_CHAR_OPEN{
  1613.                                 $ESCAPE_CHAR_OPEN !$HARDQUOTE_OPEN;  // true unless $HARDQUOTE_OPEN
  1614.                                 if ($HARDQUOTE_OPEN)
  1615.                                     foreach ($this->language_data['HARDESCAPE'as $hard)
  1616.                                     {
  1617.                                         if (substr($part$istrlen($hard)) == $hard)
  1618.                                         {
  1619.                                 $ESCAPE_CHAR_OPEN true;
  1620.                                             break;
  1621.                                         }
  1622.                                     }
  1623.                                 if ($ESCAPE_CHAR_OPEN && $this->lexic_permissions['ESCAPE_CHAR']{
  1624.                                     if (!$this->use_classes{
  1625.                                         $attributes ' style="' $this->language_data['STYLES']['ESCAPE_CHAR'][0'"';
  1626.                                     else {
  1627.                                         $attributes ' class="es0"';
  1628.                                     }
  1629.                                     $char "<span$attributes>$char;
  1630.                                     if (substr($code$i 11== "\n"{
  1631.                                         // escaping a newline, what's the point in putting the span around
  1632.                                         // the newline? It only causes hassles when inserting line numbers
  1633.                                         $char .= '</span>';
  1634.                                         $ESCAPE_CHAR_OPEN false;
  1635.                                     }
  1636.                                 }
  1637.                             else {
  1638.                                 $ESCAPE_CHAR_OPEN false;
  1639.                                 if ($this->lexic_permissions['ESCAPE_CHAR']{
  1640.                                     $char .= '</span>';
  1641.                                 }
  1642.                             }
  1643.                         elseif ($ESCAPE_CHAR_OPEN{
  1644.                             if ($this->lexic_permissions['ESCAPE_CHAR']{
  1645.                                 $char .= '</span>';
  1646.                             }
  1647.                             $ESCAPE_CHAR_OPEN false;
  1648.                             $test_str $char;
  1649.                         elseif ($STRING_OPEN == ''{
  1650.                             // Is this a multiline comment?
  1651.                             foreach ($this->language_data['COMMENT_MULTI'as $open => $close{
  1652.                                 $com_len strlen($open);
  1653.                                 $test_str substr$part$i$com_len );
  1654.                                 $test_str_match $test_str;
  1655.                                 if ($open == $test_str{
  1656.                                     $COMMENT_MATCHED true;
  1657.                                     //@todo If remove important do remove here
  1658.                                     if ($this->lexic_permissions['COMMENTS']['MULTI'||
  1659.                                         $test_str == GESHI_START_IMPORTANT{
  1660.                                         if ($test_str != GESHI_START_IMPORTANT{
  1661.                                             if (!$this->use_classes{
  1662.                                                 $attributes ' style="' $this->language_data['STYLES']['COMMENTS']['MULTI''"';
  1663.                                             else {
  1664.                                                 $attributes ' class="coMULTI"';
  1665.                                             }
  1666.                                             $test_str "<span$attributes>@htmlspecialchars($test_strENT_COMPAT$this->encoding);
  1667.                                         else {
  1668.                                             if (!$this->use_classes{
  1669.                                                 $attributes ' style="' $this->important_styles '"';
  1670.                                             else {
  1671.                                                 $attributes ' class="imp"';
  1672.                                             }
  1673.                                             // We don't include the start of the comment if it's an
  1674.                                             // "important" part
  1675.                                             $test_str "<span$attributes>";
  1676.                                         }
  1677.                                     else {
  1678.                                         $test_str @htmlspecialchars($test_strENT_COMPAT$this->encoding);
  1679.                                     }
  1680.  
  1681.                                     $close_pos strpos$part$close$i strlen($close) );
  1682.  
  1683.                                     if ($close_pos === false{
  1684.                                         $close_pos strlen($part);
  1685.                                     }
  1686.  
  1687.                                     // Short-cut through all the multiline code
  1688.                                     $rest_of_comment @htmlspecialchars(substr($part$i $com_len$close_pos $i)ENT_COMPAT$this->encoding);
  1689.                                     if (($this->lexic_permissions['COMMENTS']['MULTI'||
  1690.                                         $test_str_match == GESHI_START_IMPORTANT&&
  1691.                                         ($this->line_numbers != GESHI_NO_LINE_NUMBERS ||
  1692.                                         count($this->highlight_extra_lines0)) {
  1693.                                         // strreplace to put close span and open span around multiline newlines
  1694.                                         $test_str .= str_replace("\n""</span>\n<span$attributes>"$rest_of_comment);
  1695.                                     else {
  1696.                                         $test_str .= $rest_of_comment;
  1697.                                     }
  1698.  
  1699.                                     if ($this->lexic_permissions['COMMENTS']['MULTI'||
  1700.                                         $test_str_match == GESHI_START_IMPORTANT{
  1701.                                         $test_str .= '</span>';
  1702.                                     }
  1703.                                     $i $close_pos $com_len 1;
  1704.                                     // parse the rest
  1705.                                     $result .= $this->parse_non_string_part($stuff_to_parse);
  1706.                                     $stuff_to_parse '';
  1707.                                     break;
  1708.                                 }
  1709.                             }
  1710.                             // If we haven't matched a multiline comment, try single-line comments
  1711.                             if (!$COMMENT_MATCHED{
  1712.                                 foreach ($this->language_data['COMMENT_SINGLE'as $comment_key => $comment_mark{
  1713.                                     $com_len strlen($comment_mark);
  1714.                                     $test_str substr($part$i$com_len);
  1715.                                     if ($this->language_data['CASE_SENSITIVE'][GESHI_COMMENTS]{
  1716.                                         $match ($comment_mark == $test_str);
  1717.                                     else {
  1718.                                         $match (strtolower($comment_mark== strtolower($test_str));
  1719.                                     }
  1720.                                     if ($match{
  1721.                                         $COMMENT_MATCHED true;
  1722.                                         if ($this->lexic_permissions['COMMENTS'][$comment_key]{
  1723.                                             if (!$this->use_classes{
  1724.                                                 $attributes ' style="' $this->language_data['STYLES']['COMMENTS'][$comment_key'"';
  1725.                                             else {
  1726.                                                 $attributes ' class="co' $comment_key '"';
  1727.                                             }
  1728.                                             $test_str "<span$attributes>@htmlspecialchars($this->change_case($test_str)ENT_COMPAT$this->encoding);
  1729.                                         else {
  1730.                                             $test_str @htmlspecialchars($test_strENT_COMPAT$this->encoding);
  1731.                                         }
  1732.                                         $close_pos strpos($part"\n"$i);
  1733.                                         $oops false;
  1734.                                         if ($close_pos === false{
  1735.                                             $close_pos strlen($part);
  1736.                                             $oops true;
  1737.                                         }
  1738.                                         $test_str .= @htmlspecialchars(substr($part$i $com_len$close_pos $i $com_len)ENT_COMPAT$this->encoding);
  1739.                                         if ($this->lexic_permissions['COMMENTS'][$comment_key]{
  1740.                                             $test_str .= "</span>";
  1741.                                         }
  1742.                                         // Take into account that the comment might be the last in the source
  1743.                                         if (!$oops{
  1744.                                           $test_str .= "\n";
  1745.                                         }
  1746.                                         $i $close_pos;
  1747.                                         // parse the rest
  1748.                                         $result .= $this->parse_non_string_part($stuff_to_parse);
  1749.                                         $stuff_to_parse '';
  1750.                                         break;
  1751.                                     }
  1752.                                 }
  1753.                             }
  1754.                         elseif ($STRING_OPEN != ''{
  1755.                             // Otherwise, convert it to HTML form
  1756.                             if (strtolower($this->encoding== 'utf-8'{
  1757.                                 //only escape <128 (we don't want to break multibyte chars)
  1758.                                 if (ord($char128{
  1759.                                     $char @htmlspecialchars($charENT_COMPAT$this->encoding);
  1760.                                 }
  1761.                             else {
  1762.                                 //encode everthing
  1763.                                 $char @htmlspecialchars($charENT_COMPAT$this->encoding);
  1764.                             }
  1765.                         }
  1766.                         // Where are we adding this char?
  1767.                         if (!$COMMENT_MATCHED{
  1768.                             if (($STRING_OPEN == ''&& !$CLOSE_STRING{
  1769.                                 $stuff_to_parse .= $char;
  1770.                             else {
  1771.                                 $result .= $char;
  1772.                                 $CLOSE_STRING false;
  1773.                             }
  1774.                         else {
  1775.                             $result .= $test_str;
  1776.                             $COMMENT_MATCHED false;
  1777.                         }
  1778.                     }
  1779.                     // Parse the last bit
  1780.                     $result .= $this->parse_non_string_part($stuff_to_parse);
  1781.                     $stuff_to_parse '';
  1782.                 else {
  1783.                     $result .= @htmlspecialchars($partENT_COMPAT$this->encoding);
  1784.                 }
  1785.                 // Close the <span> that surrounds the block
  1786.                 if ($this->strict_mode && $this->language_data['STYLES']['SCRIPT'][$script_key!= '' &&
  1787.                     $this->lexic_permissions['SCRIPT']{
  1788.                     $result .= '</span>';
  1789.                 }
  1790.             else {
  1791.                 // Else not a block to highlight
  1792.                 $result .= @htmlspecialchars($partENT_COMPAT$this->encoding);
  1793.             }
  1794.         }
  1795.  
  1796.         // Parse the last stuff (redundant?)
  1797.         $result .= $this->parse_non_string_part($stuff_to_parse);
  1798.  
  1799.         // Lop off the very first and last spaces
  1800.         $result substr($result1strlen($result1);
  1801.  
  1802.         // Are we still in a string?
  1803.         if ($STRING_OPEN{
  1804.             $result .= '</span>';
  1805.         }
  1806.  
  1807.         // We're finished: stop timing
  1808.         $this->set_time($start_timemicrotime());
  1809.  
  1810.         return $this->finalise($result);
  1811.     }
  1812.  
  1813.     /**
  1814.      * Swaps out spaces and tabs for HTML indentation. Not needed if
  1815.      * the code is in a pre block...
  1816.      *
  1817.      * @param  string The source to indent
  1818.      * @return string The source with HTML indenting applied
  1819.      * @since  1.0.0
  1820.      * @access private
  1821.      */
  1822.     function indent ($result)
  1823.     {
  1824.             /// Replace tabs with the correct number of spaces
  1825.             if (false !== strpos($result"\t")) {
  1826.                 $lines explode("\n"$result);
  1827.                 foreach ($lines as $key => $line{
  1828.                     if (false === strpos($line"\t")) {
  1829.                         $lines[$key$line;
  1830.                         continue;
  1831.                     }//echo 'checking line ' . $key . '<br />';
  1832.  
  1833.                     $pos 0;
  1834.                     $tab_width $this->tab_width;
  1835.                     $length strlen($line);
  1836.                     $result_line '';
  1837.  
  1838.                     //echo '<pre>line: ' . htmlspecialchars($line) . '</pre>';
  1839.                     $IN_TAG false;
  1840.                     for ($i 0$i $length$i++{
  1841.                         $char substr($line$i1);
  1842.                         // Simple engine to work out whether we're in a tag.
  1843.                         // If we are we modify $pos. This is so we ignore HTML
  1844.                         // in the line and only workout the tab replacement
  1845.                         // via the actual content of the string
  1846.                         // This test could be improved to include strings in the
  1847.                         // html so that < or > would be allowed in user's styles
  1848.                         // (e.g. quotes: '<' '>'; or similar)
  1849.                         if ($IN_TAG && '>' == $char{
  1850.                             $IN_TAG false;
  1851.                             $result_line .= '>';
  1852.                             ++$pos;
  1853.                         elseif (!$IN_TAG && '<' == $char{
  1854.                             $IN_TAG true;
  1855.                             $result_line .= '<';
  1856.                             ++$pos;
  1857.                         elseif (!$IN_TAG && '&' == $char{
  1858.                             //echo "matched &amp; in line... ";
  1859.                             $substr substr($line$i 34);
  1860.                             //$substr_5 = substr($line, 5, 1);
  1861.                             $posi strpos($substr';');
  1862.                             if (false !== $posi{
  1863.                                 //echo "found entity at $posi\n";
  1864.                                 $pos += $posi 3;
  1865.                             }
  1866.                             $result_line .= '&';
  1867.                         elseif (!$IN_TAG && "\t" == $char{
  1868.                             $str '';
  1869.                             // OPTIMISE - move $strs out. Make an array:
  1870.                             // $tabs = array(
  1871.                             //  1 => '&nbsp;',
  1872.                             //  2 => '&nbsp; ',
  1873.                             //  3 => '&nbsp; &nbsp;' etc etc
  1874.                             // to use instead of building a string every time
  1875.                             $strs array(=> '&nbsp;'=> ' ');
  1876.                             //echo "building (pos=$pos i=$i) (" . ($i - $pos) . ") " . ($tab_width - (($i - $pos) % $tab_width)) . " spaces\n";
  1877.                             for ($k 0$k ($tab_width (($i $pos$tab_width))$k++$str .= $strs[$k 2];
  1878.                             $result_line .= $str;
  1879.                             //$pos--;
  1880.                             $pos++;
  1881.                             //$pos -= $tab_width-1;
  1882.  
  1883.                             if (false === strpos($line"\t"$i 1)) {
  1884.                                 //$lines[$key] = $result_line;
  1885.                                 //echo 'got here';
  1886.                                 $result_line .= substr($line$i 1);
  1887.                                 break;
  1888.                             }
  1889.                         elseif $IN_TAG {
  1890.                             ++$pos;
  1891.                             $result_line .= $char;
  1892.                         else {
  1893.                             $result_line .= $char;
  1894.                             //++$pos;
  1895.                         }
  1896.                     }
  1897.                     $lines[$key$result_line;
  1898.                 }
  1899.                 $result implode("\n"$lines);
  1900.             }
  1901.         // Other whitespace
  1902.         $result str_replace('  ''&nbsp; '$result);
  1903.         $result str_replace('  '' &nbsp;'$result);
  1904.         $result str_replace("\n ""\n&nbsp;"$result);
  1905.  
  1906.         if ($this->line_numbers == GESHI_NO_LINE_NUMBERS{
  1907.             $result nl2br($result);
  1908.         }
  1909.         return $result;
  1910.     }
  1911.  
  1912.     /**
  1913.      * Changes the case of a keyword for those languages where a change is asked for
  1914.      *
  1915.      * @param  string The keyword to change the case of
  1916.      * @return string The keyword with its case changed
  1917.      * @since  1.0.0
  1918.      * @access private
  1919.      */
  1920.     function change_case ($instr)
  1921.     {
  1922.         if ($this->language_data['CASE_KEYWORDS'== GESHI_CAPS_UPPER{
  1923.             return strtoupper($instr);
  1924.         elseif ($this->language_data['CASE_KEYWORDS'== GESHI_CAPS_LOWER{
  1925.             return strtolower($instr);
  1926.         }
  1927.         return $instr;
  1928.     }
  1929.  
  1930.     /**
  1931.      * Adds a url to a keyword where needed.
  1932.      *
  1933.      * @param  string The keyword to add the URL HTML to
  1934.      * @param  int What group the keyword is from
  1935.      * @param  boolean Whether to get the HTML for the start or end
  1936.      * @return The HTML for either the start or end of the HTML &lt;a&gt; tag
  1937.      * @since  1.0.2
  1938.      * @access private
  1939.      * @todo   Get rid of ender
  1940.      */
  1941.     function add_url_to_keyword ($keyword$group$start_or_end)
  1942.     {
  1943.         if (isset($this->language_data['URLS'][$group]&&
  1944.             $this->language_data['URLS'][$group!= '' &&
  1945.             substr($keyword05!= '&lt;/'{
  1946.             // There is a base group for this keyword
  1947.             if ($start_or_end == 'BEGIN'{
  1948.                 // HTML workaround... not good form (tm) but should work for 1.0.X
  1949.                 if ($keyword != ''{
  1950.                     // Old system: strtolower
  1951.                     //$keyword = ( $this->language_data['CASE_SENSITIVE'][$group] ) ? $keyword : strtolower($keyword);
  1952.                     // New system: get keyword from language file to get correct case
  1953.                     foreach ($this->language_data['KEYWORDS'][$groupas $word{
  1954.                         if (strtolower($word== strtolower($keyword)) {
  1955.                             break;
  1956.                         }
  1957.                     }
  1958.                     $word substr($word04== '&lt;' substr($word4$word;
  1959.                     $word substr($word-4== '&gt;' substr($word0strlen($word4$word;
  1960.                     if (!$wordreturn '';
  1961.  
  1962.                     return '<|UR1|"' .
  1963.                         str_replace(
  1964.                             array('{FNAME}''.'),
  1965.                             array(@htmlspecialchars($wordENT_COMPAT$this->encoding)'<DOT>'),
  1966.                             $this->language_data['URLS'][$group]
  1967.                         '">';
  1968.                 }
  1969.                 return '';
  1970.             // HTML fix. Again, dirty hackage...
  1971.             elseif (!($this->language == 'html4strict' && '&gt;' == $keyword)) {
  1972.                 return '</a>';
  1973.             }
  1974.         }
  1975.     }
  1976.  
  1977.     /**
  1978.      * Takes a string that has no strings or comments in it, and highlights
  1979.      * stuff like keywords, numbers and methods.
  1980.      *
  1981.      * @param string The string to parse for keyword, numbers etc.
  1982.      * @since 1.0.0
  1983.      * @access private
  1984.      * @todo BUGGY! Why? Why not build string and return?
  1985.      */
  1986.     function parse_non_string_part (&$stuff_to_parse)
  1987.     {
  1988.         $stuff_to_parse ' ' @htmlspecialchars($stuff_to_parseENT_COMPAT$this->encoding);
  1989.         $stuff_to_parse_pregquote preg_quote($stuff_to_parse'/');
  1990.         // These vars will disappear in the future
  1991.         $func '$this->change_case';
  1992.         $func2 '$this->add_url_to_keyword';
  1993.  
  1994.         //
  1995.         // Regular expressions
  1996.         //
  1997.         foreach ($this->language_data['REGEXPS'as $key => $regexp{
  1998.             if ($this->lexic_permissions['REGEXPS'][$key]{
  1999.                 if (is_array($regexp)) {
  2000.                     $stuff_to_parse preg_replace(
  2001.                         "/" .
  2002.                         str_replace('/''\/'$regexp[GESHI_SEARCH].
  2003.                         "/{$regexp[GESHI_MODIFIERS]}",
  2004.                         "{$regexp[GESHI_BEFORE]}<|!REG3XP$key!>{$regexp[GESHI_REPLACE]}|>{$regexp[GESHI_AFTER]}",
  2005.                         $stuff_to_parse
  2006.                     );
  2007.                 else {
  2008.                     $stuff_to_parse preg_replace"/(" str_replace('/''\/'$regexp")/""<|!REG3XP$key!>\\1|>"$stuff_to_parse);
  2009.                 }
  2010.             }
  2011.         }
  2012.  
  2013.         //
  2014.         // Highlight numbers. This regexp sucks... anyone with a regexp that WORKS
  2015.         // here wins a cookie if they send it to me. At the moment there's two doing
  2016.         // almost exactly the same thing, except the second one prevents a number
  2017.         // being highlighted twice (eg <span...><span...>5</span></span>)
  2018.         // Put /NUM!/ in for the styles, which gets replaced at the end.
  2019.         //
  2020.         // NEW ONE: Brice Bernard
  2021.         // $stuff_to_parse = preg_replace('/([^(\\w|#|\\\|"|\')])(\\d+)/', '\\1<|/NUM!/>\\2|>', $stuff_to_parse);
  2022.         //$stuff_to_parse = preg_replace('/([-+]?\\b(?:[0-9]*\\.)?[0-9]+\\b)/', '<|/NUM!/>\\1|>', $stuff_to_parse);
  2023.         //
  2024.         if ($this->lexic_permissions['NUMBERS'&& preg_match('#[0-9]#'$stuff_to_parse )) {
  2025.             //$stuff_to_parse = preg_replace('#([^a-zA-Z0-9_\#])([0-9]+)([^a-zA-Z0-9])#', "\\1<|/NUM!/>\\2|>\\3", $stuff_to_parse);
  2026.             //$stuff_to_parse = preg_replace('#([^a-zA-Z0-9_\#>])([0-9]+)([^a-zA-Z0-9])#', "\\1<|/NUM!/>\\2|>\\3", $stuff_to_parse);
  2027.             $stuff_to_parse preg_replace('/([-+]?\\b(?:[0-9]*\\.)?[0-9]+\\b)/''<|/NUM!/>\\1|>'$stuff_to_parse);
  2028.         }
  2029.  
  2030.         // Highlight keywords
  2031.         // if there is a couple of alpha symbols there *might* be a keyword
  2032.         if (preg_match('#[a-zA-Z]{2,}#'$stuff_to_parse)) {
  2033.             foreach ($this->language_data['KEYWORDS'as $k => $keywordset{
  2034.                 if ($this->lexic_permissions['KEYWORDS'][$k]{
  2035.                     foreach ($keywordset as $keyword{
  2036.                         $keyword preg_quote($keyword'/');
  2037.                         //
  2038.                         // This replacement checks the word is on it's own (except if brackets etc
  2039.                         // are next to it), then highlights it. We don't put the color=" for the span
  2040.                         // in just yet - otherwise languages with the keywords "color" or "or" have
  2041.                         // a fit.
  2042.                         //
  2043.                         if (false !== stristr($stuff_to_parse_pregquote$keyword )) {
  2044.                             $stuff_to_parse .= ' ';
  2045.                             // Might make a more unique string for putting the number in soon
  2046.                             // Basically, we don't put the styles in yet because then the styles themselves will
  2047.                             // get highlighted if the language has a CSS keyword in it (like CSS, for example ;))
  2048.                             $styles "/$k/";
  2049.                             if ($this->language_data['CASE_SENSITIVE'][$k]{
  2050.                                 $stuff_to_parse preg_replace(
  2051.                                     "/([^a-zA-Z0-9\$_\|\#;>|^])($keyword)(?=[^a-zA-Z0-9_<\|%\-&])/e",
  2052.                                     "'\\1' . $func2('\\2', '$k', 'BEGIN') . '<|$styles>' . $func('\\2') . '|>' . $func2('\\2', '$k', 'END')",
  2053.                                     $stuff_to_parse
  2054.                                 );
  2055.                             else {
  2056.                                 // Change the case of the word.
  2057.                                 // hackage again... must... release... 1.2...
  2058.                                 if ('smarty' == $this->language$hackage '\/'else $hackage ''}
  2059.                                 $stuff_to_parse preg_replace(
  2060.                                     "/([^a-zA-Z0-9\$_\|\#;>$hackage|^])($keyword)(?=[^a-zA-Z0-9_<\|%\-&])/ie",
  2061.                                     "'\\1' . $func2('\\2', '$k', 'BEGIN') . '<|$styles>' . $func('\\2') . '|>' . $func2('\\2', '$k', 'END')",
  2062.                                     $stuff_to_parse
  2063.                                 );
  2064.                             }
  2065.                             $stuff_to_parse substr($stuff_to_parse0strlen($stuff_to_parse1);
  2066.                         }
  2067.                     }
  2068.                 }
  2069.             }
  2070.         }
  2071.  
  2072.         //
  2073.         // Now that's all done, replace /[number]/ with the correct styles
  2074.         //
  2075.         foreach ($this->language_data['KEYWORDS'as $k => $kws{
  2076.             if (!$this->use_classes{
  2077.                 $attributes ' style="' $this->language_data['STYLES']['KEYWORDS'][$k'"';
  2078.             else {
  2079.                 $attributes ' class="kw' $k '"';
  2080.             }
  2081.             $stuff_to_parse str_replace("/$k/"$attributes$stuff_to_parse);
  2082.         }
  2083.  
  2084.         // Put number styles in
  2085.         if (!$this->use_classes && $this->lexic_permissions['NUMBERS']{
  2086.             $attributes ' style="' $this->language_data['STYLES']['NUMBERS'][0'"';
  2087.         else {
  2088.             $attributes ' class="nu0"';
  2089.         }
  2090.         $stuff_to_parse str_replace('/NUM!/'$attributes$stuff_to_parse);
  2091.  
  2092.         //
  2093.         // Highlight methods and fields in objects
  2094.         //
  2095.         if ($this->lexic_permissions['METHODS'&& $this->language_data['OOLANG']{
  2096.             foreach ($this->language_data['OBJECT_SPLITTERS'as $key => $splitter{
  2097.                 if (false !== stristr($stuff_to_parse$splitter)) {
  2098.                     if (!$this->use_classes{
  2099.                         $attributes ' style="' $this->language_data['STYLES']['METHODS'][$key'"';
  2100.                     else {
  2101.                         $attributes ' class="me' $key '"';
  2102.                     }
  2103.                     $stuff_to_parse preg_replace("/(" preg_quote($this->language_data['OBJECT_SPLITTERS'][$key]1"[\s]*)([a-zA-Z\*\(][a-zA-Z0-9_\*]*)/""\\1<|$attributes>\\2|>"$stuff_to_parse);
  2104.                 }
  2105.             }
  2106.         }
  2107.  
  2108.         //
  2109.         // Highlight brackets. Yes, I've tried adding a semi-colon to this list.
  2110.         // You try it, and see what happens ;)
  2111.         // TODO: Fix lexic permissions not converting entities if shouldn't
  2112.         // be highlighting regardless
  2113.         //
  2114.         if ($this->lexic_permissions['BRACKETS']{
  2115.             $code_entities_match array('['']''('')''{''}');
  2116.             if (!$this->use_classes{
  2117.                 $code_entities_replace array(
  2118.                     '<| style="' $this->language_data['STYLES']['BRACKETS'][0'">&#91;|>',
  2119.                     '<| style="' $this->language_data['STYLES']['BRACKETS'][0'">&#93;|>',
  2120.                     '<| style="' $this->language_data['STYLES']['BRACKETS'][0'">&#40;|>',
  2121.                     '<| style="' $this->language_data['STYLES']['BRACKETS'][0'">&#41;|>',
  2122.                     '<| style="' $this->language_data['STYLES']['BRACKETS'][0'">&#123;|>',
  2123.                     '<| style="' $this->language_data['STYLES']['BRACKETS'][0'">&#125;|>',
  2124.                 );
  2125.             else {
  2126.                 $code_entities_replace array(
  2127.                     '<| class="br0">&#91;|>',
  2128.                     '<| class="br0">&#93;|>',
  2129.                     '<| class="br0">&#40;|>',
  2130.                     '<| class="br0">&#41;|>',
  2131.                     '<| class="br0">&#123;|>',
  2132.                     '<| class="br0">&#125;|>',
  2133.                 );
  2134.             }
  2135.             $stuff_to_parse str_replace$code_entities_match,  $code_entities_replace$stuff_to_parse );
  2136.         }
  2137.  
  2138.         //
  2139.         // Add class/style for regexps
  2140.         //
  2141.         foreach ($this->language_data['REGEXPS'as $key => $regexp{
  2142.             if ($this->lexic_permissions['REGEXPS'][$key]{
  2143.                 if (!$this->use_classes{
  2144.                     $attributes ' style="' $this->language_data['STYLES']['REGEXPS'][$key'"';
  2145.                 else {
  2146.                     $attributes ' class="re' $key '"';
  2147.                 }
  2148.                 $stuff_to_parse str_replace("!REG3XP$key!""$attributes"$stuff_to_parse);
  2149.             }
  2150.         }
  2151.  
  2152.         // Replace <DOT> with . for urls
  2153.         $stuff_to_parse str_replace('<DOT>''.'$stuff_to_parse);
  2154.         // Replace <|UR1| with <a href= for urls also
  2155.         if (isset($this->link_styles[GESHI_LINK])) {
  2156.             if ($this->use_classes{
  2157.                 $stuff_to_parse str_replace('<|UR1|''<a' $this->link_target ' href='$stuff_to_parse);
  2158.             else {
  2159.                 $stuff_to_parse str_replace('<|UR1|''<a' $this->link_target ' style="' $this->link_styles[GESHI_LINK'" href='$stuff_to_parse);
  2160.             }
  2161.         else {
  2162.             $stuff_to_parse str_replace('<|UR1|''<a' $this->link_target ' href='$stuff_to_parse);
  2163.         }
  2164.  
  2165.         //
  2166.         // NOW we add the span thingy ;)
  2167.         //
  2168.  
  2169.         $stuff_to_parse str_replace('<|''<span'$stuff_to_parse);
  2170.         $stuff_to_parse str_replace '|>''</span>'$stuff_to_parse );
  2171.  
  2172.         return substr(stripslashes($stuff_to_parse)1);
  2173.     }
  2174.  
  2175.     /**
  2176.      * Sets the time taken to parse the code
  2177.      *
  2178.      * @param microtime The time when parsing started
  2179.      * @param microtime The time when parsing ended
  2180.      * @since 1.0.2
  2181.      * @access private
  2182.      */
  2183.     function set_time ($start_time$end_time)
  2184.     {
  2185.         $start explode(' '$start_time);
  2186.         $end explode(' '$end_time);
  2187.         $this->time $end[0$end[1$start[0$start[1];
  2188.     }
  2189.  
  2190.     /**
  2191.      * Gets the time taken to parse the code
  2192.      *
  2193.      * @return double The time taken to parse the code
  2194.      * @since  1.0.2
  2195.      */
  2196.     function get_time ()
  2197.     {
  2198.         return $this->time;
  2199.     }
  2200.  
  2201.     /**
  2202.      * Gets language information and stores it for later use
  2203.      *
  2204.      * @access private
  2205.      * @todo Needs to load keys for lexic permissions for keywords, regexps etc
  2206.      */
  2207.     function load_language ($file_name)
  2208.     {
  2209.         $language_data array();
  2210.         require $file_name;
  2211.         // Perhaps some checking might be added here later to check that
  2212.         // $language data is a valid thing but maybe not
  2213.         $this->language_data $language_data;
  2214.         // Set strict mode if should be set
  2215.         if ($this->language_data['STRICT_MODE_APPLIES'== GESHI_ALWAYS{
  2216.             $this->strict_mode true;
  2217.         }
  2218.         // Set permissions for all lexics to true
  2219.         // so they'll be highlighted by default
  2220.         foreach ($this->language_data['KEYWORDS'as $key => $words{
  2221.             $this->lexic_permissions['KEYWORDS'][$keytrue;
  2222.         }
  2223.         foreach ($this->language_data['COMMENT_SINGLE'as $key => $comment{
  2224.             $this->lexic_permissions['COMMENTS'][$keytrue;
  2225.         }
  2226.         foreach ($this->language_data['REGEXPS'as $key => $regexp{
  2227.             $this->lexic_permissions['REGEXPS'][$keytrue;
  2228.         }
  2229.         $this->enable_highlighting();
  2230.         // Set default class for CSS
  2231.         $this->overall_class $this->language;
  2232.     }
  2233.  
  2234.     /**
  2235.      * Takes the parsed code and various options, and creates the HTML
  2236.      * surrounding it to make it look nice.
  2237.      *
  2238.      * @param  string The code already parsed
  2239.      * @return string The code nicely finalised
  2240.      * @since  1.0.0
  2241.      * @access private
  2242.      */
  2243.     function finalise ($parsed_code)
  2244.     {
  2245.         // Remove end parts of important declarations
  2246.         // This is BUGGY!! My fault for bad code: fix coming in 1.2
  2247.         // @todo Remove this crap
  2248.         if ($this->enable_important_blocks &&
  2249.             (strstr($parsed_code@htmlspecialchars(GESHI_START_IMPORTANTENT_COMPAT$this->encoding)) === false)) {
  2250.             $parsed_code str_replace(@htmlspecialchars(GESHI_END_IMPORTANTENT_COMPAT$this->encoding)''$parsed_code);
  2251.         }
  2252.  
  2253.         // Add HTML whitespace stuff if we're using the <div> header
  2254.         if ($this->header_type != GESHI_HEADER_PRE{
  2255.             $parsed_code $this->indent($parsed_code);
  2256.         }
  2257.  
  2258.         // If we're using line numbers, we insert <li>s and appropriate
  2259.         // markup to style them (otherwise we don't need to do anything)
  2260.         if ($this->line_numbers != GESHI_NO_LINE_NUMBERS{
  2261.             // If we're using the <pre> header, we shouldn't add newlines because
  2262.             // the <pre> will line-break them (and the <li>s already do this for us)
  2263.             $ls ($this->header_type != GESHI_HEADER_PRE"\n" '';
  2264.             // Get code into lines
  2265.             $code explode("\n"$parsed_code);
  2266.             // Set vars to defaults for following loop
  2267.             $parsed_code '';
  2268.             $i 0;
  2269.             // Foreach line...
  2270.             foreach ($code as $line{
  2271.                 $line $line $line '&nbsp;';
  2272.                 // If this is a "special line"...
  2273.                 if ($this->line_numbers == GESHI_FANCY_LINE_NUMBERS &&
  2274.                     $i $this->line_nth_row == ($this->line_nth_row 1)) {
  2275.                     // Set the attributes to style the line
  2276.                     if ($this->use_classes{
  2277.                         $attr ' class="li2"';
  2278.                         $def_attr ' class="de2"';
  2279.                     else {
  2280.                         $attr ' style="' $this->line_style2 '"';
  2281.                         // This style "covers up" the special styles set for special lines
  2282.                         // so that styles applied to special lines don't apply to the actual
  2283.                         // code on that line
  2284.                         $def_attr ' style="' $this->code_style '"';
  2285.                     }
  2286.                     // Span or div?
  2287.                     $start "<div$def_attr>";
  2288.                     $end '</div>';
  2289.                 else {
  2290.                     if ($this->use_classes{
  2291.                         $attr ' class="li1"';
  2292.                         $def_attr ' class="de1"';
  2293.                     else {
  2294.                         $attr ' style="' $this->line_style1 '"';
  2295.                         $def_attr ' style="' $this->code_style '"';
  2296.                     }
  2297.                     $start "<div$def_attr>";
  2298.                     $end '</div>';
  2299.                 }
  2300.  
  2301.                 ++$i;
  2302.                 // Are we supposed to use ids? If so, add them
  2303.                 if ($this->add_ids{
  2304.                     $attr .= " id=\"{$this->overall_id}-{$i}\"";
  2305.                 }
  2306.                 if ($this->use_classes && in_array($i$this->highlight_extra_lines)) {
  2307.                     $attr .= " class=\"ln-xtra\"";
  2308.                 }
  2309.                 if (!$this->use_classes && in_array($i$this->highlight_extra_lines)) {
  2310.                     $attr .= " style=\"{$this->highlight_extra_lines_style}\"";
  2311.                 }
  2312.  
  2313.                 // Add in the line surrounded by appropriate list HTML
  2314.                 $parsed_code .= "<li$attr>$start$line$end</li>$ls";
  2315.             }
  2316.         } else {
  2317.             // No line numbers, but still need to handle highlighting lines extra.
  2318.             // Have to use divs so the full width of the code is highlighted
  2319.             $code = explode("\n", $parsed_code);
  2320.             $parsed_code = '';
  2321.             $i = 0;
  2322.             foreach ($code as $line)
  2323.             {
  2324.                 // Make lines have at least one space in them if they're empty
  2325.                 $line = ($line) ? $line : '&nbsp;';
  2326.                 if (in_array(++$i, $this->highlight_extra_lines)) {
  2327.                     if ($this->use_classes{
  2328.                         $parsed_code .= '<div class="ln-xtra">';
  2329.                     } else {
  2330.                         $parsed_code .= "<div style=\"{$this->highlight_extra_lines_style}\">";
  2331.                     }
  2332.                     $parsed_code .= $line . "</div>\n";
  2333.                 } else {
  2334.                     $parsed_code .= $line . "\n";
  2335.                 }
  2336.             }
  2337.         }
  2338.  
  2339.         // purge some unnecessary stuff
  2340.         $parsed_code = preg_replace('#<span[^>]+>(\s*)</span>#', '\\1', $parsed_code);
  2341.         $parsed_code = preg_replace('#<div[^>]+>(\s*)</div>#', '\\1', $parsed_code);
  2342.  
  2343.         if ($this->header_type == GESHI_HEADER_PRE{
  2344.             // enforce line numbers when using pre
  2345.             $parsed_code = str_replace('<li></li>', '<li>&nbsp;</li>', $parsed_code);
  2346.         }
  2347.  
  2348.         return $this->header(chop($parsed_code$this->footer();
  2349.     }
  2350.  
  2351.     /**
  2352.      * Creates the header for the code block (with correct attributes)
  2353.      *
  2354.      * @return string The header for the code block
  2355.      * @since  1.0.0
  2356.      * @access private
  2357.      */
  2358.     function header ()
  2359.     {
  2360.         // Get attributes needed
  2361.         $attributes = $this->get_attributes();
  2362.  
  2363.         $ol_attributes '';
  2364.  
  2365.         if ($this->line_numbers_start != 1{
  2366.             $ol_attributes .= ' start="' . $this->line_numbers_start '"';
  2367.         }
  2368.  
  2369.         // Get the header HTML
  2370.         $header = $this->format_header_content();
  2371.  
  2372.         if (GESHI_HEADER_NONE == $this->header_type{
  2373.             if ($this->line_numbers != GESHI_NO_LINE_NUMBERS{
  2374.                 return "$header<ol$ol_attributes>";
  2375.             }
  2376.             return $header;
  2377.         }
  2378.  
  2379.         // Work out what to return and do it
  2380.         if ($this->line_numbers != GESHI_NO_LINE_NUMBERS{
  2381.             if ($this->header_type == GESHI_HEADER_PRE{
  2382.                 return "<pre$attributes>$header<ol$ol_attributes>";
  2383.             } elseif ($this->header_type == GESHI_HEADER_DIV{
  2384.                 return "<div$attributes>$header<ol$ol_attributes>";
  2385.             }
  2386.         } else {
  2387.             if ($this->header_type == GESHI_HEADER_PRE{
  2388.                 return "<pre$attributes>$header";
  2389.             } elseif ($this->header_type == GESHI_HEADER_DIV{
  2390.                 return "<div$attributes>$header";
  2391.             }
  2392.         }
  2393.     }
  2394.  
  2395.     /**
  2396.      * Returns the header content, formatted for output
  2397.      *
  2398.      * @return string The header content, formatted for output
  2399.      * @since  1.0.2
  2400.      * @access private
  2401.      */
  2402.     function format_header_content ()
  2403.     {
  2404.         $header = $this->header_content;
  2405.         if ($header{
  2406.             if ($this->header_type == GESHI_HEADER_PRE{
  2407.                 $header = str_replace("\n", '', $header);
  2408.             }
  2409.             $header = $this->replace_keywords($header);
  2410.  
  2411.             if ($this->use_classes{
  2412.                 $attr = ' class="head"';
  2413.             } else {
  2414.                 $attr = " style=\"{$this->header_content_style}\"";
  2415.             }
  2416.             return "<div$attr>$header</div>";
  2417.         }
  2418.     }
  2419.  
  2420.     /**
  2421.      * Returns the footer for the code block.
  2422.      *
  2423.      * @return string The footer for the code block
  2424.      * @since  1.0.0
  2425.      * @access private
  2426.      */
  2427.     function footer ()
  2428.     {
  2429.         $footer_content = $this->format_footer_content();
  2430.  
  2431.         if (GESHI_HEADER_NONE == $this->header_type{
  2432.             return ($this->line_numbers != GESHI_NO_LINE_NUMBERS'</ol>' $footer_content
  2433.                 : $footer_content;
  2434.         }
  2435.  
  2436.         if ($this->header_type == GESHI_HEADER_DIV{
  2437.             if ($this->line_numbers != GESHI_NO_LINE_NUMBERS{
  2438.                 return "</ol>$footer_content</div>";
  2439.             }
  2440.             return "$footer_content</div>";
  2441.         } else {
  2442.             if ($this->line_numbers != GESHI_NO_LINE_NUMBERS{
  2443.                 return "</ol>$footer_content</pre>";
  2444.             }
  2445.             return "$footer_content</pre>";
  2446.         }
  2447.     }
  2448.  
  2449.     /**
  2450.      * Returns the footer content, formatted for output
  2451.      *
  2452.      * @return string The footer content, formatted for output
  2453.      * @since  1.0.2
  2454.      * @access private
  2455.      */
  2456.     function format_footer_content ()
  2457.     {
  2458.         $footer = $this->footer_content;
  2459.         if ($footer{
  2460.             if ($this->header_type == GESHI_HEADER_PRE{
  2461.                 $footer = str_replace("\n", '', $footer);;
  2462.             }
  2463.             $footer = $this->replace_keywords($footer);
  2464.  
  2465.             if ($this->use_classes{
  2466.                 $attr = ' class="foot"';
  2467.             } else {
  2468.                 $attr = " style=\"{$this->footer_content_style}\"";
  2469.             }
  2470.             return "<div$attr>$footer</div>";
  2471.         }
  2472.     }
  2473.  
  2474.     /**
  2475.      * Replaces certain keywords in the header and footer with
  2476.      * certain configuration values
  2477.      *
  2478.      * @param  string The header or footer content to do replacement on
  2479.      * @return string The header or footer with replaced keywords
  2480.      * @since  1.0.2
  2481.      * @access private
  2482.      */
  2483.     function replace_keywords ($instr)
  2484.     {
  2485.         $keywords = $replacements = array();
  2486.  
  2487.         $keywords[] = '<TIME>';
  2488.         $replacements[] = number_format($this->get_time()3);
  2489.  
  2490.         $keywords['<LANGUAGE>';
  2491.         $replacements[$this->language;
  2492.  
  2493.         $keywords['<VERSION>';
  2494.         $replacements[GESHI_VERSION;
  2495.  
  2496.         return str_replace($keywords$replacements$instr);
  2497.     }
  2498.  
  2499.     /**
  2500.      * Gets the CSS attributes for this code
  2501.      *
  2502.      * @return The CSS attributes for this code
  2503.      * @since  1.0.0
  2504.      * @access private
  2505.      * @todo   Document behaviour change - class is outputted regardless of whether we're using classes or not.
  2506.      *         Same with style
  2507.      */
  2508.     function get_attributes ()
  2509.     {
  2510.         $attributes = '';
  2511.  
  2512.         if ($this->overall_class != ''{
  2513.             $attributes .= " class=\"{$this->overall_class}\"";
  2514.         }
  2515.         if ($this->overall_id != ''{
  2516.             $attributes .= " id=\"{$this->overall_id}\"";
  2517.         }
  2518.         if ($this->overall_style != ''{
  2519.             $attributes .= ' style="' . $this->overall_style '"';
  2520.         }
  2521.         return $attributes;
  2522.     }
  2523.  
  2524.     /**
  2525.      * Returns a stylesheet for the highlighted code. If $economy mode
  2526.      * is true, we only return the stylesheet declarations that matter for
  2527.      * this code block instead of the whole thing
  2528.      *
  2529.      * @param  boolean Whether to use economy mode or not
  2530.      * @return string A stylesheet built on the data for the current language
  2531.      * @since  1.0.0
  2532.      */
  2533.     function get_stylesheet ($economy_mode = true)
  2534.     {
  2535.         // If there's an error, chances are that the language file
  2536.         // won't have populated the language data file, so we can't
  2537.         // risk getting a stylesheet...
  2538.         if ($this->error{
  2539.             return '';
  2540.         }
  2541.         // First, work out what the selector should be. If there's an ID,
  2542.         // that should be used, the same for a class. Otherwise, a selector
  2543.         // of '' means that these styles will be applied anywhere
  2544.         $selector = ($this->overall_id != ''"#{$this->overall_id" : '';
  2545.         $selector = ($selector == '' && $this->overall_class != ''".{$this->overall_class" : $selector;
  2546.  
  2547.         // Header of the stylesheet
  2548.         if (!$economy_mode) {
  2549.             $stylesheet = "/**\n * GeSHi Dynamically Generated Stylesheet\n * --------------------------------------\n * Dynamically generated stylesheet for {$this->language}\n * CSS class: {$this->overall_class}CSS id: {$this->overall_id}\n * GeSHi (cNigel McNie 2004 (http://qbnz.com/highlighter)\n */\n";
  2550.          } else {
  2551.             $stylesheet = '/* GeSHi (c) Nigel McNie 2004 (http://qbnz.com/highlighter) */' . "\n";
  2552.         }
  2553.  
  2554.         // Set the <ol> to have no effect at all if there are line numbers
  2555.         // (<ol>s have margins that should be destroyed so all layout is
  2556.         // controlled by the set_overall_style method, which works on the
  2557.         // <pre> or <div> container). Additionally, set default styles for lines
  2558.         if (!$economy_mode || $this->line_numbers != GESHI_NO_LINE_NUMBERS{
  2559.             //$stylesheet .= "$selector, {$selector}ol, {$selector}ol li {margin: 0;}\n";
  2560.             $stylesheet .= "$selector.de1$selector.de2 {{$this->code_style}}\n";
  2561.         }
  2562.  
  2563.         // Add overall styles
  2564.         if (!$economy_mode || $this->overall_style != ''{
  2565.             $stylesheet .= "$selector {{$this->overall_style}}\n";
  2566.         }
  2567.  
  2568.         // Add styles for links
  2569.         foreach ($this->link_styles as $key => $style{
  2570.             if (!$economy_mode || $key == GESHI_LINK && $style != '') {
  2571.                 $stylesheet .= "{$selector}a:link {{$style}}\n";
  2572.             }
  2573.             if (!$economy_mode || $key == GESHI_HOVER && $style != '') {
  2574.                 $stylesheet .= "{$selector}a:hover {{$style}}\n";
  2575.             }
  2576.             if (!$economy_mode || $key == GESHI_ACTIVE && $style != '') {
  2577.                 $stylesheet .= "{$selector}a:active {{$style}}\n";
  2578.             }
  2579.             if (!$economy_mode || $key == GESHI_VISITED && $style != '') {
  2580.                 $stylesheet .= "{$selector}a:visited {{$style}}\n";
  2581.             }
  2582.         }
  2583.  
  2584.         // Header and footer
  2585.         if (!$economy_mode || $this->header_content_style != ''{
  2586.             $stylesheet .= "$selector.head {{$this->header_content_style}}\n";
  2587.         }
  2588.         if (!$economy_mode || $this->footer_content_style != ''{
  2589.             $stylesheet .= "$selector.foot {{$this->footer_content_style}}\n";
  2590.         }
  2591.  
  2592.         // Styles for important stuff
  2593.         if (!$economy_mode || $this->important_styles != ''{
  2594.             $stylesheet .= "$selector.imp {{$this->important_styles}}\n";
  2595.         }
  2596.  
  2597.         // Styles for lines being highlighted extra
  2598.         if (!$economy_mode || count($this->highlight_extra_lines)) {
  2599.             $stylesheet .= "$selector.ln-xtra {{$this->highlight_extra_lines_style}}\n";
  2600.         }
  2601.  
  2602.         // Simple line number styles
  2603.         if (!$economy_mode || ($this->line_numbers != GESHI_NO_LINE_NUMBERS && $this->line_style1 != '')) {
  2604.             $stylesheet .= "{$selector}li {{$this->line_style1}}\n";
  2605.         }
  2606.  
  2607.         // If there is a style set for fancy line numbers, echo it out
  2608.         if (!$economy_mode || ($this->line_numbers == GESHI_FANCY_LINE_NUMBERS && $this->line_style2 != '')) {
  2609.             $stylesheet .= "{$selector}li.li2 {{$this->line_style2}}\n";
  2610.         }
  2611.  
  2612.         foreach ($this->language_data['STYLES']['KEYWORDS'as $group => $styles{
  2613.             if (!$economy_mode || !($economy_mode && (!$this->lexic_permissions['KEYWORDS'][$group|| $styles == ''))) {
  2614.                 $stylesheet .= "$selector.kw$group {{$styles}}\n";
  2615.             }
  2616.         }
  2617.         foreach ($this->language_data['STYLES']['COMMENTS'as $group => $styles{
  2618.             if (!$economy_mode || !($economy_mode && $styles == '') &&
  2619.                 !($economy_mode && !$this->lexic_permissions['COMMENTS'][$group])) {
  2620.                 $stylesheet .= "$selector.co$group {{$styles}}\n";
  2621.             }
  2622.         }
  2623.         foreach ($this->language_data['STYLES']['ESCAPE_CHAR'as $group => $styles{
  2624.             if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode &&
  2625.                 !$this->lexic_permissions['ESCAPE_CHAR'])) {
  2626.                 $stylesheet .= "$selector.es$group {{$styles}}\n";
  2627.             }
  2628.         }
  2629.         foreach ($this->language_data['STYLES']['SYMBOLS'as $group => $styles{
  2630.             if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode &&
  2631.                 !$this->lexic_permissions['BRACKETS'])) {
  2632.                 $stylesheet .= "$selector.br$group {{$styles}}\n";
  2633.             }
  2634.         }
  2635.         foreach ($this->language_data['STYLES']['STRINGS'as $group => $styles{
  2636.             if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode &&
  2637.                 !$this->lexic_permissions['STRINGS'])) {
  2638.                 $stylesheet .= "$selector.st$group {{$styles}}\n";
  2639.             }
  2640.         }
  2641.         foreach ($this->language_data['STYLES']['NUMBERS'as $group => $styles{
  2642.             if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode &&
  2643.                 !$this->lexic_permissions['NUMBERS'])) {
  2644.                 $stylesheet .= "$selector.nu$group {{$styles}}\n";
  2645.             }
  2646.         }
  2647.         foreach ($this->language_data['STYLES']['METHODS'as $group => $styles{
  2648.             if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode &&
  2649.                 !$this->lexic_permissions['METHODS'])) {
  2650.                 $stylesheet .= "$selector.me$group {{$styles}}\n";
  2651.             }
  2652.         }
  2653.         foreach ($this->language_data['STYLES']['SCRIPT'as $group => $styles{
  2654.             if (!$economy_mode || !($economy_mode && $styles == '')) {
  2655.                 $stylesheet .= "$selector.sc$group {{$styles}}\n";
  2656.             }
  2657.         }
  2658.         foreach ($this->language_data['STYLES']['REGEXPS'as $group => $styles{
  2659.             if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode &&
  2660.                 !$this->lexic_permissions['REGEXPS'][$group])) {
  2661.                 $stylesheet .= "$selector.re$group {{$styles}}\n";
  2662.             }
  2663.         }
  2664.  
  2665.         return $stylesheet;
  2666.     }
  2667.  
  2668. } // End Class GeSHi
  2669.  
  2670. if (!function_exists('geshi_highlight')) {
  2671.     /**
  2672.      * Easy way to highlight stuff. Behaves just like highlight_string
  2673.      *
  2674.      * @param string The code to highlight
  2675.      * @param string The language to highlight the code in
  2676.      * @param string The path to the language files. You can leave this blank if you need
  2677.      *               as from version 1.0.7 the path should be automatically detected
  2678.      * @param boolean Whether to return the result or to echo
  2679.      * @return string The code highlighted (if $return is true)
  2680.      * @since 1.0.2
  2681.      */
  2682.     function geshi_highlight ($string, $language, $path, $return = false)
  2683.     {
  2684.         $geshi = new GeSHi($string, $language, $path);
  2685.         $geshi->set_header_type(GESHI_HEADER_NONE);
  2686.         if ($return{
  2687.             return '<code>' . $geshi->parse_code('</code>';
  2688.         }
  2689.         echo '<code>' . $geshi->parse_code('</code>';
  2690.         if ($geshi->error()) {
  2691.             return false;
  2692.         }
  2693.         return true;
  2694.     }
  2695. }
  2696.  

Documentation generated on Mon, 05 Mar 2007 21:05:07 +0000 by phpDocumentor 1.3.1