b2evolution PHP Cross Reference Blogging Systems

Source: /plugins/code_highlight_plugin/_code_highlight.plugin.php - 732 lines - 25408 bytes - Summary - Text - Print

Description: This file implements the AstonishMe Code plugin. This file is part of the b2evolution project - {@link http://b2evolution.net/}

   1  <?php
   2  /**
   3   * This file implements the AstonishMe Code plugin.
   4   *
   5   * This file is part of the b2evolution project - {@link http://b2evolution.net/}
   6   *
   7   * @copyright (c)2003-2014 by Francois Planque - {@link http://fplanque.com/}
   8   * Parts of this file are copyright (c)2005-2007 by Yabba/Scott - {@link http://astonishme.co.uk/contact/}.
   9   *
  10   * {@internal License choice
  11   * - If you have received this file as part of a package, please find the license.txt file in
  12   *   the same folder or the closest folder above for complete license terms.
  13   * - If you have received this file individually (e-g: from http://cvs.sourceforge.net/viewcvs.py/evocms/)
  14   *   then you must choose one of the following licenses before using the file:
  15   *   - GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php
  16   *   - Mozilla Public License 1.1 (MPL) - http://www.opensource.org/licenses/mozilla1.1.php
  17   * }}
  18   *
  19   * {@internal Open Source relicensing agreement:
  20   * Yabba/Scott grant Francois PLANQUE the right to license
  21   * Yabba's/Scott's contributions to this file and the b2evolution project
  22   * under any OSI approved OSS license (http://www.opensource.org/licenses/).
  23   * }}
  24   *
  25   * @package plugins
  26   *
  27   * @author Yabba: Paul Jones - {@link http://astonishme.co.uk/}
  28   * @author Stk: Scott Kimler - {@link http://astonishme.co.uk/}
  29   *
  30   * @version $Id: _code_highlight.plugin.php 6136 2014-03-08 07:59:48Z manuel $
  31   */
  32  
  33  /**
  34   * AstonishMe Display Code plugin.
  35   *
  36   *    Features:
  37   *        1) Character entity rendering on-the-fly
  38   *        2) Easy to use, just cut'n-paste your code
  39   *        3) Automatically adds line numbers and alternate colouring
  40   *        4) Customizable CSS for integrating for your site
  41   *        5) XHTML (Strict) and CSS valid code
  42   *        6) Auto-senses code block length
  43   *        7) BBCode tags pass through and allow to highlight the code
  44   *        8) No accidental smilie rendering
  45   *        9) PHP Syntax highlighting
  46   *       10) Variable start line numbers
  47   *       11) Links php functions to the php.net documentation
  48   *       12) Code is preserved if plugin uninstalled ( stored as : <!--amphp--><pre>&lt;php echo 'hello world'; ?&gt;</pre><!--/amphp--> )
  49   *
  50   *    To use:
  51   *        ************************************** THIS WILL NEED REWRITING ******************************************
  52   *        * Upload and install the plugin via the back office
  53   *        * Paste your code between <amcode> </amcode> tags
  54   *        * Paste your php between <amphp> </amphp> tags
  55   *        * start from any line number with the line attribute
  56   *        * <amcode line="99"> or <amphp line="999">
  57   *
  58   * @todo fp> for semantic purposes, <code> </code> should be automagically added
  59   * yabs > I assume you mean that <code> block </code> should also be converted/highlighted
  60   * in which case "done", I also added in <php> </php> ( was an easier regex :p )
  61   * obviously we originally picked the tag names to suit ourselves
  62   *
  63   * @todo dh> I'd like to be able to disable line numbering in codeblocks.
  64   *           Line numbers do not make sense for short code blocks (and also for longer ones
  65   *           not necessarily).
  66   *       fp> how about if no line=".." attribute is specified then no numbering?
  67   *       dh> Well.. this would change the default "[codeblock]" though. Yes, when
  68   *           clicking the button it uses '[codeblock lang="" line="1"][/codeblock]', but
  69   *           the "default" is still just the tag, and at least I've been using that
  70   *           (typing instead of clicking).
  71   */
  72  
  73  if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
  74  
  75  /**
  76   * @package plugins
  77   */
  78  
  79  class code_highlight_plugin extends Plugin
  80  {
  81      var $name = 'Code highlight';
  82      var $code = 'evo_code';
  83      var $priority = 10;
  84      var $version = '5.0.0';
  85      var $author = 'Astonish Me';
  86      var $group = 'rendering';
  87      var $help_url = 'http://b2evolution.net/man/technical-reference/renderer-plugins/code-highlight-plugin';
  88      var $number_of_installs = 1;
  89  
  90      /**
  91       * Text php functions array
  92       *
  93       * @access private
  94       */
  95      var $php_functions = array();
  96  
  97      /**
  98       * Text php syntax highlighting colours array
  99       *
 100       * @access private
 101       */
 102      var $highlight_colours = array();
 103  
 104  
 105      /**
 106       * EXPERIMENTAL - array language classes cache
 107       *
 108       * @access private
 109       */
 110      var $languageCache = array();
 111  
 112  
 113      /**
 114       * TRUE when HTML tags are allowed for content of current rendered post/comment/message
 115       * In this case we should prepare a content with function htmlspecialchars() to display a code as it is
 116       *
 117       * @var boolean
 118       */
 119      var $allow_html = false;
 120  
 121      /**
 122       * Init
 123       */
 124  	function PluginInit( & $params )
 125      {
 126          $this->short_desc = T_( 'Display computer code in a post.' );
 127          $this->long_desc = T_( 'Display computer code easily with syntax coloring and allowing for easy copy/paste.' );
 128      }
 129  
 130  
 131      /**
 132       * Get the settings that the plugin can use.
 133       *
 134       * Those settings are transfered into a Settings member object of the plugin
 135       * and can be edited in the backoffice (Settings / Plugins).
 136       *
 137       * @see Plugin::GetDefaultSettings()
 138       * @see PluginSettings
 139       * @see Plugin::PluginSettingsValidateSet()
 140       * @return array
 141       */
 142  	function GetDefaultSettings( & $params )
 143      {
 144          $r = array(
 145              'strict' => array(
 146                      'label' => $this->T_( 'XHTML strict' ),
 147                      'type' => 'checkbox',
 148                      'defaultvalue' => '0', // use transitional as default
 149                      'note' => $this->T_( 'If enabled this will remove the \' target="_blank" \' from the PHP documentation links' ),
 150                  ),
 151              'toolbar_default' => array(
 152                      'label' => $this->T_( 'Display code toolbar' ),
 153                      'type' => 'checkbox',
 154                      'defaultvalue' => '1',
 155                      'note' => $this->T_( 'Display code toolbar in expert mode and on comment form (indivdual users can override this).' ),
 156                  ),
 157              );
 158          return $r;
 159      }
 160  
 161  
 162      /**
 163       * Allowing the user to override the display of the toolbar.
 164       *
 165       * @see Plugin::GetDefaultSettings()
 166       * @see PluginSettings
 167       * @see Plugin::PluginSettingsValidateSet()
 168       *
 169       * @return array
 170       */
 171  	function GetDefaultUserSettings()
 172      {
 173          return array(
 174                  'display_toolbar' => array(
 175                      'label' => T_( 'Display code toolbar' ),
 176                      'defaultvalue' => $this->Settings->get('toolbar_default'),
 177                      'type' => 'checkbox',
 178                      'note' => $this->T_( 'Display the code toolbar' ),
 179                  ),
 180              );
 181      }
 182  
 183  
 184      /**
 185       * Define here default collection/blog settings that are to be made available in the backoffice.
 186       *
 187       * @param array Associative array of parameters.
 188       * @return array See {@link Plugin::GetDefaultSettings()}.
 189       */
 190  	function get_coll_setting_definitions( & $params )
 191      {
 192          $default_params = array_merge( $params, array( 'default_comment_rendering' => 'never' ) );
 193          return parent::get_coll_setting_definitions( $default_params );
 194      }
 195  
 196  
 197      /**
 198       * Event handler: Called when displaying editor toolbars.
 199       *
 200       * @param array Associative array of parameters
 201       * @return boolean did we display a toolbar?
 202       */
 203  	function DisplayCommentToolbar( & $params )
 204      {
 205          if( !empty( $params['Comment'] ) )
 206          { // Comment is set, get Blog from comment
 207              $Comment = & $params['Comment'];
 208              if( !empty( $Comment->item_ID ) )
 209              {
 210                  $comment_Item = & $Comment->get_Item();
 211                  $Blog = & $comment_Item->get_Blog();
 212              }
 213          }
 214  
 215          if( empty( $Blog ) )
 216          { // Comment is not set, try global Blog
 217              global $Blog;
 218              if( empty( $Blog ) )
 219              { // We can't get a Blog, this way "apply_comment_rendering" plugin collection setting is not available
 220                  return false;
 221              }
 222          }
 223  
 224          $apply_rendering = $this->get_coll_setting( 'coll_apply_comment_rendering', $Blog );
 225          if( !empty( $apply_rendering ) && $apply_rendering != 'never'
 226          && ( ( is_logged_in() && $this->UserSettings->get( 'display_toolbar' ) )
 227              || ( !is_logged_in() && $this->Settings->get( 'toolbar_default' ) ) ) )
 228          {
 229              return $this->DisplayCodeToolbar();
 230          }
 231          return false;
 232      }
 233  
 234  
 235      /**
 236       * Display a toolbar in admin
 237       *
 238       * @param array Associative array of parameters
 239       * @return boolean did we display a toolbar?
 240       */
 241  	function AdminDisplayToolbar( & $params )
 242      {
 243          if( !empty( $params['Item'] ) )
 244          {    // Item is set, get Blog from post
 245              $edited_Item = & $params['Item'];
 246              $Blog = & $edited_Item->get_Blog();
 247          }
 248  
 249          if( empty( $Blog ) )
 250          {    // Item is not set, try global Blog
 251              global $Blog;
 252              if( empty( $Blog ) )
 253              {    // We can't get a Blog, this way "apply_rendering" plugin collection setting is not available
 254                  return false;
 255              }
 256          }
 257  
 258          $coll_setting_name = ( $params['target_type'] == 'Comment' ) ? 'coll_apply_comment_rendering' : 'coll_apply_rendering';
 259          $apply_rendering = $this->get_coll_setting( $coll_setting_name, $Blog );
 260          if( empty( $apply_rendering ) || $apply_rendering == 'never' ||
 261              $params['edit_layout'] == 'simple' || !$this->UserSettings->get( 'display_toolbar' ) )
 262          {    // This is too complex for simple mode, or user doesn't want the toolbar, don't display it:
 263              return false;
 264          }
 265          $this->DisplayCodeToolbar();
 266      }
 267  
 268  
 269  	function DisplayCodeToolbar()
 270      {
 271          echo '<div class="edit_toolbar code_toolbar">';
 272          // TODO: dh> make this optional.. just like with line numbers, this "Code" line is not feasible with oneliners.
 273          echo T_('Code').': ';
 274          echo '<input type="button" id="codespan" title="'.T_('Insert codespan').'" class="quicktags" onclick="codespan_tag(\'\');" value="'.T_('codespan').'" />';
 275          echo '<input type="button" id="codeblock" title="'.T_('Insert codeblock').'" style="margin-left:8px;" class="quicktags" onclick="codeblock_tag(\'\');" value="'.T_('codeblock').'" />';
 276          echo '<input type="button" id="codeblock_xml" title="'.T_('Insert XML codeblock').'" class="quicktags" onclick="codeblock_tag(\'xml\');" value="'.T_('XML').'" />';
 277          echo '<input type="button" id="codeblock_html" title="'.T_('Insert HTML codeblock').'" class="quicktags" onclick="codeblock_tag(\'html\');" value="'.T_('HTML').'" />';
 278          echo '<input type="button" id="codeblock_php" title="'.T_('Insert PHP codeblock').'" class="quicktags" onclick="codeblock_tag(\'php\');" value="'.T_('PHP').'" />';
 279          echo '<input type="button" id="codeblock_css" title="'.T_('Insert CSS codeblock').'" class="quicktags" onclick="codeblock_tag(\'css\');" value="'.T_('CSS').'" />';
 280          echo '<input type="button" id="codeblock_shell" title="'.T_('Insert Shell codeblock').'" class="quicktags" onclick="codeblock_tag(\'shell\');" value="'.T_('Shell').'" />';
 281          echo '</div>';
 282  
 283          ?>
 284          <script type="text/javascript">
 285              //<![CDATA[
 286  			function codespan_tag( lang )
 287              {
 288                  tag = '[codespan]';
 289  
 290                  textarea_wrap_selection( b2evoCanvas, tag, '[/codespan]', 0 );
 291              }
 292  			function codeblock_tag( lang )
 293              {
 294                  tag = '[codeblock lang="'+lang+'" line="1"]';
 295  
 296                  textarea_wrap_selection( b2evoCanvas, tag, '[/codeblock]', 0 );
 297              }
 298              //]]>
 299          </script>
 300          <?php
 301  
 302          return true;
 303      }
 304  
 305  
 306    /**
 307     * Filters out the custom tag that would not validate, PLUS escapes the actual code.
 308     *
 309       * @param mixed $params
 310       */
 311  	function FilterItemContents( & $params )
 312      {
 313          $title   = & $params['title'];
 314          $content = & $params['content'];
 315  
 316          // echo 'FILTERING CODE';
 317  
 318          // Note : This regex is different from the original - just in case it gets moved again ;)
 319  
 320          // change all <codeblock> || [codeblock]  segments before format_to_post() gets a hold of them
 321          // 1 - amcode or codeblock
 322          // 2 - attribs : lang &| line
 323          // 3 - code block
 324          $content = preg_replace_callback( '#[<\[](codeblock)([^>\]]*?)[>\]]([\s\S]+?)?[<\[]/\1[>\]]#i',
 325                                  array( $this, 'filter_codeblock_callback' ), $content );
 326  
 327          // Quick and dirty escaping of inline code <codespan> || [codespan]:
 328              // fp> please provide example of what the following fix does: it looks weird to me :p
 329              // $content = preg_replace_callback( '#[<\[]codespan[^>\]](.*?)[<\[]/codespan[>\]]#',
 330          $content = preg_replace_callback( '#[<\[]codespan[>\]]([\s\S]*?)[<\[]/codespan[>\]]#',
 331                                  array( $this, 'filter_codespan_callback' ), $content );
 332  
 333          return true;
 334      }
 335  
 336  
 337      /**
 338       * Format codespan for display
 339       *
 340       * @todo This is a bit quick 'n dirty.
 341       * @todo We might want to unfilter this too.
 342       * @todo We might want to highlight this too (based on a lang attribute).
 343       */
 344  	function filter_codespan_callback( $matches )
 345      {
 346          $code = $matches[1];
 347  
 348          return '<code class="codespan">'.$code.'</code>';
 349      }
 350  
 351  
 352      /**
 353       * Formats post contents ready for editing
 354       *
 355       * @param mixed $params
 356       */
 357  	function UnfilterItemContents( & $params )
 358      {
 359          $title   = & $params['title'];
 360          $content = & $params['content'];
 361  
 362          // 1 - attribs : lang &| line
 363          // 2 - codeblock
 364          $content = preg_replace_callback( '#<\!--\s*codeblock([^-]*?)\s*--><pre[^>]*><code>(.+?)</code></pre><\!--\s+/codeblock\s*-->#is', array( $this, 'format_to_edit' ), $content );
 365  
 366          $content = preg_replace_callback( '#<code class="codespan">(.+?)</code>#is', array( $this, 'format_span_to_edit' ), $content );
 367  
 368          return true;
 369      }
 370  
 371  
 372  	function CommentFormSent( & $params )
 373      {
 374          $ItemCache = & get_ItemCache();
 375          $comment_Item = & $ItemCache->get_by_ID( $params['comment_post_ID'], false );
 376          if( !$comment_Item )
 377          {    // Incorrect item
 378              return false;
 379          }
 380  
 381          $item_Blog = & $comment_Item->get_Blog();
 382          if( $this->get_coll_setting( 'coll_apply_comment_rendering', $item_Blog ) )
 383          {    // render code blocks in comment
 384              $params['content' ] = & $params['comment'];
 385              $this->FilterItemContents( $params );
 386              if( empty( $params['dont_remove_pre'] ) || !$params['dont_remove_pre'] )
 387              {    // remove <pre>
 388                  $params['comment'] = preg_replace( '#(<\!--\s*codeblock[^-]*?\s*-->)<pre[^>]*><code>(.+?)</code></pre>(<\!--\s+/codeblock\s*-->)#is', '$1<code>$2</code>$3', $params['comment'] );
 389              }
 390          }
 391      }
 392  
 393  
 394  	function BeforeCommentFormInsert( $params )
 395      {
 396          $Comment = & $params['Comment'];
 397          $comment_Item = & $Comment->get_Item();
 398          $item_Blog = & $comment_Item->get_Blog();
 399          if( $this->get_coll_setting( 'coll_apply_comment_rendering', $item_Blog ) )
 400          {    // render code blocks in comment
 401              // add <pre> back in so highlighting is done, will be removed by highlighter
 402              $params['Comment']->content =  preg_replace( '#(<\!--\s*codeblock[^-]*?\s*-->)<code>(.+?)</code>(<\!--\s+/codeblock\s*-->)#is', '$1<pre class="codeblock"><code>$2</code></pre>$3', $params['Comment']->content );
 403          }
 404      }
 405  
 406  
 407      /**
 408       * Perform rendering
 409       *
 410       * @see Plugin::RenderItemAsHtml()
 411       */
 412  	function RenderItemAsHtml( & $params )
 413      {
 414          $content = & $params['data'];
 415  
 416          // Get a setting "Allow HTML" for:
 417          if( ! empty( $params['Comment'] ) )
 418          { // Comment
 419              $Comment = & $params['Comment'];
 420              $comment_Item = & $Comment->get_Item();
 421              $item_Blog = $comment_Item->get_Blog();
 422              $this->allow_html = $item_Blog->get_setting( 'allow_html_comment' );
 423          }
 424          else if( ! empty( $params['Item'] ) )
 425          { // Item
 426              $Item = & $params['Item'];
 427              $item_Blog = $Item->get_Blog();
 428              $this->allow_html = $item_Blog->get_setting( 'allow_html_post' );
 429          }
 430          else if( ! empty( $params['Message'] ) )
 431          { // Message
 432              global $Settings;
 433              $this->allow_html = $Settings->get( 'allow_html_message' );
 434          }
 435  
 436          // 2 - attribs : lang &| line
 437          // 4 - codeblock
 438          $content = preg_replace_callback( '#(\<p>)?\<!--\s*codeblock([^-]*?)\s*-->(\</p>)?\<pre[^>]*><code>([\s\S]+?)</code>\</pre>(\<p>)?\<!--\s*/codeblock\s*-->(\</p>)?#i',
 439                                  array( $this, 'render_codeblock_callback' ), $content );
 440  
 441          return true;
 442      }
 443  
 444  
 445      /**
 446       * Perform rendering
 447       *
 448       * @see Plugin::RenderItemAsXml()
 449       *
 450       * Note : Do we actually want to do this? - yabs
 451       */
 452  	function RenderItemAsXml( & $params )
 453      {
 454          $this->RenderItemAsHtml( $params );
 455      }
 456  
 457  
 458      /**
 459       * Tidys up a block of code ready for numbering
 460       *
 461       * @param string $block - the code to be tidied up
 462       * @param string $line_seperator - the seperator between lines of code ( default \n )
 463       * @return string - the tidied code
 464       */
 465  	function tidy_code_output( $block, $line_seperator = "\n" )
 466      {
 467          // lets split the block into individual lines
 468          $code = explode( $line_seperator,
 469              // after removing windows garbage
 470              str_replace( "\r", '', $block ) );
 471  
 472          // time to rock and roll ;)
 473          $still_open = array(); // this holds all the spans that need closing and re-opening on the following code line
 474          for( $i = 0; $i < count( $code ); $i++ )
 475          {
 476              // we need to note all opening spans
 477              $spans =
 478                  // get rid of the first element, it's always empty
 479                  array_slice(
 480                  // split line at each opening span
 481                  explode( '<span class="',
 482                  // add any open spans back in
 483                  implode( '', $still_open )
 484                  .$code[$i] )
 485                  , 1 );
 486  //            pre_dump( $spans );
 487              // reset still_open array
 488              $still_open = array();
 489              // $spans now contains a list of opening spans
 490              for( $z = 0; $z < count( $spans ); $z++ )
 491              {
 492                  // add the span to the still_open array
 493                  $still_open[] = '<span class="'.substr( $spans[$z], 0, strpos( $spans[$z], '"' ) ).'">';
 494  //                pre_dump( $still_open );
 495                  // count all closing spans and remove them from the open spans list
 496                  if( $closed = substr_count( $spans[$z], '</span>' ) )
 497                      $still_open = array_slice( $still_open, 0, $closed * -1 );
 498              }
 499              // lets rebuild the code line and close any remaining spans
 500              $code[$i] = '<span class="'.implode( '<span class="', $spans ).str_repeat( '</span>', count( $still_open ) );
 501          }
 502          // lets stitch it all back together again
 503          $cleaned = implode( "\n", $code );
 504          // and get rid of any empty spans
 505          while( preg_match( '#\<span[^>]+?>\</span>#', $cleaned ) )
 506              $cleaned = preg_replace( '#\<span[^>]+?>\</span>#', '', $cleaned );
 507  //        pre_dump( $cleaned );
 508          // return the cleaned up code
 509          return $cleaned;
 510      }
 511  
 512  
 513      /**
 514       * Formats code ready for the database
 515       *
 516       * @param array $block ( 2 - attributes, 3 - the code )
 517       * @return string formatted code || empty
 518       */
 519  	function filter_codeblock_callback( $block )
 520      { // if code block exists then tidy everything up for the database, otherwise just remove the pointless tag
 521          $attributes = str_replace( array( '"', '\'' ), '', html_entity_decode( $block[2] ) );
 522          return ( empty( $block[3] ) ||  !trim( $block[3] ) ? '' : '<!-- codeblock'.$attributes.' --><pre class="codeblock"><code>'
 523                          .$block[3]
 524                          .'</code></pre><!-- /codeblock -->' );
 525      }
 526  
 527  
 528      /**
 529       * Formats codeblock ready for editing
 530       *
 531       * @param array $block ( 1 - attributes, 2 - the code )
 532       * @return string formatted code
 533       */
 534  	function format_to_edit( $block )
 535      {
 536          return '[codeblock'.$block[1].']'.html_entity_decode( $block[2] ).'[/codeblock]';
 537      }
 538  
 539  
 540      /**
 541       * Formats codespan ready for editing
 542       *
 543       * @param array $span ( 1 - the code )
 544       * @return string formatted code
 545       */
 546  	function format_span_to_edit( $span )
 547      {
 548          return '[codespan]'.html_entity_decode( $span[1] ).'[/codespan]';
 549      }
 550  
 551  
 552      /**
 553       * Spits out the styles used
 554       *
 555       * @see Plugin::SkinBeginHtmlHead()
 556       */
 557  	function SkinBeginHtmlHead()
 558      {
 559          require_js( 'functions.js', 'blog' );
 560  
 561          add_css_headline('/* AstonishMe code plugin styles */'
 562              .'.amc0,.amc1,.amc2,.amc3,.amc4,.amc5,.amc6,.amc7,.amc8,.amc9 {'
 563              .'background:url('.$this->get_plugin_url().'img/numbers.gif) no-repeat; }');
 564  
 565          require_css($this->get_plugin_url().'amcode.css',true);
 566  
 567          // TODO: dh> move this to a IE-specific file, e.g. add_css_headline, but which is specific for IE
 568          //           Or easier: fix it with a hack in amcode.css itself?!
 569          add_headline('<!--[if IE]>'
 570              .'<style type="text/css">'
 571              .'/* IE: make sure the last line is not hidden by a scrollbar */'
 572              .'div.codeblock.amc_short table {'
 573              .'    margin-bottom: 2ex;'
 574              .'}'
 575              .'</style>'
 576              .'<![endif]-->');
 577      }
 578  
 579  
 580      /**
 581       * Spits out the styles used
 582       *
 583       * @see Plugin::AdminEndHtmlHead()
 584       */
 585  	function AdminEndHtmlHead()
 586      {
 587          $this->SkinBeginHtmlHead();
 588      }
 589  
 590  
 591      /**
 592       * Formats code ready for displaying
 593       * With "SwipeFriendly" line numbers
 594       *
 595       * @todo fp> no table should be needed. Also since we have odd/even coloring, word wrapping may be ok.
 596       * yabs > unfortunately the tables are required to make the odd/even colouring and swipeable numbers work :(
 597       *
 598       * an example :
 599       * Danny uploaded a page to demo his colours of choice ( http://brendoman.com/dev/youtube.html )
 600       * if you scroll down to RenderItemAsHtml() you'll see it has 4 problems
 601       * 1) some lines wrap - but they wrap underneath the line number
 602       * 2) some lines don't wrap so they force a browser scrollbar
 603       *   ( although a scrollable div will cure this)
 604       * 3) the lines that don't wrap lose their background colour once they become longer than the browser width
 605       *   ( a scrollable div has the same problem )
 606       * 4) If you try and copy/paste the code you also get the line numbers
 607       *   ( like every other code formatter that we've seen on the web that uses line numbers ... which is why most don't ;) )
 608       *
 609       * compare that to the same section of code using this plugin ( http://cvs.astonishme.co.uk/index.php/2007/04/01/a_demo )
 610       *
 611       * fp> almost all that can be fixed with proper CSS and will have the advantage of not adding extra space in front of the lines when copy/pasting. I'll work on it when I have time.
 612       *
 613       * yabs> The operative word in that sentance is "almost" ;) ... I've removed the trailing spaces for all but empty lines ( required to "prop them open" ) ... I'd forgotten all about them :p
 614       *
 615       * @param string the code block to be wrapped up
 616       * @param string the attributes - currently only the starting line number
 617       * @param string the code type ( code || php )
 618       *
 619       * @return string formatted code
 620       */
 621  	function do_numbering( $code, $offset = 0, $type = 'code' )
 622      {
 623          $temp = str_replace( array( '&nbsp;&nbsp;', '  ', "\t", '[x', '[/x' ),  array( '&#160;&#160;', '&#160;&#160;', '&#160;&#160;', '[', '[/' ), $code );
 624          $temp = explode( "\n", $temp );
 625          $count = 0;
 626          $output = '';
 627          $odd_line = false;
 628          foreach( $temp as $line )
 629          {
 630              $output .= '<tr class="amc_code_'.( ( $odd_line = !$odd_line ) ? 'odd' : 'even' ).'"><td class="amc_line">'
 631                                      .$this->create_number( ++$count + $offset ).'</td><td><code>'.$line
 632                                      // add an &nbsp; to empty lines to stop them "collapsing"
 633                                      .( empty( $line ) ? '&nbsp;' : '' )
 634                                      .'</code></td></tr>';//."\n"; yura: I commented this because Auto-P plugin creates the tags <p></p> from this symbol
 635          }
 636          // make "long" value a setting ? - yabs
 637          return '<p class="codeblock_title">'.$this->languageCache[ $type ]->language_title.'</p><div class="codeblock codeblock_with_title amc_'.$type.' '.( $count < 26 ? 'amc_short' : 'amc_long' ).'"><table>'.$output.'</table></div>';
 638      }
 639  
 640  
 641      /**
 642       * Creates the SwipeFriendly line numbers
 643       *
 644       * @param integer the line number to produce
 645       * @return string the html for the line number
 646       */
 647  	function create_number( $num )
 648      {    // part of Swipe 'n' Paste magic ;)
 649          $result = '';
 650          $count = 0;
 651          while ( $num )
 652          {
 653              $result .= '<div class="amc'.( $num - ( floor( $num / 10 ) * 10 ) ).'">';
 654              $num = floor( $num / 10 );
 655              $count++;
 656          }
 657          $result .= str_repeat( '</div>', $count );
 658          return $result;
 659      }
 660  
 661  
 662      /**
 663       * Formats codeblock ready for displaying
 664       * Each language is stored as a classfile
 665       * This would allow new languages to be added more easily
 666       * It would also allow Geshi to be used as the highlighter with no code changes ;)
 667       *
 668       * Replaces both (current) highlighter functions
 669       *
 670       * ..... still requires some more thought though :p
 671       *
 672       * @param array $block ( 2 - attributes, 4 - the code )
 673       * @return string formatted code
 674       */
 675  	function render_codeblock_callback( $block )
 676      {
 677          // set the offset if present - default : 0
 678          preg_match( '#line=("|\'?)([0-9]+?)(["\']?)$#', $block[2], $match );
 679          $offset = ( empty( $match[2] ) ? 0 : $match[2] - 1 );
 680  
 681          // set the language if present - default : code
 682          preg_match( '#lang=("|\'?)([^\1]+?)([\s"\']+?)#', $block[2], $match );
 683          $language = strtolower( ( empty( $match[2] ) ? 'code' : $match[2] ) );
 684  
 685          if( $code = trim( $block[4] ) )
 686          { // we have a code block
 687              if( $this->allow_html )
 688              { // If HTML is allowed in content we should disallow this for <code> content
 689                  $code = htmlspecialchars( $code );
 690              }
 691              // is the relevant language highlighter already cached?
 692              if( empty( $this->languageCache[ $language ] ) )
 693              { // lets attempt to load the language
 694                  $language_file = dirname(__FILE__).'/highlighters/'.$language.'.highlighter.php';
 695                  if( is_file( $language_file ) )
 696                  { // language class exists, lets load and cache an instance of it
 697                      require_once $language_file;
 698                      $class = 'am_'.$language.'_highlighter';
 699                      $this->languageCache[ $language ] = new $class( $this );
 700                  }
 701                  else
 702                  { // language class doesn't exists, fallback to default highlighter
 703                      $language = 'code';
 704                      if( empty( $this->languageCache[ $language ] ) )
 705                      { // lets attempt to load the default language
 706                          $language_file = dirname(__FILE__).'/highlighters/'.$language.'.highlighter.php';
 707                          if( is_file( $language_file ) )
 708                          { // default lanugage exists
 709                              require_once $language_file;
 710                              $class = 'am_'.$language.'_highlighter';
 711                              // add the language to the cache
 712                              $this->languageCache[ $language ] = new $class( $this );
 713                          }
 714                          else
 715                          { // if we hit this we might as well go to the pub ;)
 716                              // echo '***** error ***** no language or default language file is present';
 717                              return $code;
 718                          }
 719                      }
 720                  }
 721              }
 722              $this->languageCache[ $language ]->requested_language = $language;
 723              $this->languageCache[ $language ]->strict_mode = $this->Settings->get( 'strict' );
 724              $code = $this->languageCache[ $language ]->highlight_code( $code );
 725              // add the line numbers
 726              $code = $this->do_numbering( $code, $offset, $language );
 727          }
 728          return $code;
 729      }
 730  }
 731  
 732  ?>

title

Description

title

Description

title

Description

title

title

Body