b2evolution PHP Cross Reference Blogging Systems

Source: /plugins/_auto_p.plugin.php - 665 lines - 18689 bytes - Summary - Text - Print

Description: This file implements the Auto P plugin for b2evolution

   1  <?php
   2  /**
   3   * This file implements the Auto P plugin for b2evolution
   4   *
   5   * @author blueyed: Daniel HAHLER - {@link http://daniel.hahler.de/}
   6   *
   7   * @package plugins
   8   */
   9  if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
  10  
  11  
  12  /**
  13   * The Auto-P Plugin.
  14   *
  15   * It wraps text blocks, which are devided by newline(s) into HTML P tags (paragraphs)
  16   * and optionally replaces single newlines with BR tags (line breaks).
  17   *
  18   * @package plugins
  19   */
  20  class auto_p_plugin extends Plugin
  21  {
  22      var $code = 'b2WPAutP';
  23      var $name = 'Auto P';
  24      var $priority = 70;
  25      var $version = '5.0.0';
  26      var $group = 'rendering';
  27      var $short_desc;
  28      var $long_desc;
  29      var $help_url = 'http://b2evolution.net/man/technical-reference/renderer-plugins/auto-p-plugin';
  30      var $number_of_installs = 1;
  31  
  32      /**
  33       * List of block elements (we want a paragraph before and after), excludes: address, added: td, th
  34       * @var string
  35       */
  36      var $block_tags = 'blockquote|dd|div|dl|dt|fieldset|form|h[1-6]|hr|li|object|ol|p|pre|select|script|table|td|th|ul';
  37  
  38  
  39      var $p_allowed_in = array('address', 'applet', 'blockquote', 'body', 'button', 'center', 'dd', 'del', 'div', 'fieldset', 'form', 'iframe', 'ins', 'li', 'map', 'noframes', 'noscript', 'object', 'td', 'th' );
  40  
  41  
  42      var $br_allowed_in = array(
  43          // Block level:
  44          'address', 'center', 'dd', 'dir', 'div', 'dt', 'fieldset', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'isindex', 'menu', 'noframes', 'noscript', 'p', 'pre',
  45          // Inline:
  46          'a', 'abbr', 'acronym', 'applet', 'b', 'basefont', 'bdo', 'big', 'button', 'cite', 'code', 'dfn', 'em', 'font', 'i', 'img', 'input', 'iframe', 'kbd', 'label', 'li', 'map', 'object', 'q', 'samp', 'select', 'small', 'span', 'strong', 'sub', 'sup', 'textarea', 'td', 'th', 'tt', 'var' );
  47  
  48  
  49      /**
  50       * Init
  51       */
  52  	function PluginInit( & $params )
  53      {
  54          $this->short_desc = T_('Automatic &lt;P&gt; and &lt;BR&gt; tags');
  55          $this->long_desc = T_('This renderer will automatically detect paragraphs on double line-breaks and mark them with appropriate HTML &lt;P&gt; tags.<br />
  56              Optionally, it will also mark single line breaks with HTML &lt;BR&gt; tags.');
  57      }
  58  
  59  
  60      /**
  61       * @return array
  62       */
  63  	function GetDefaultSettings()
  64      {
  65          return array(
  66                  'br' => array(
  67                      'label' => T_('Line breaks'),
  68                      'type' => 'checkbox',
  69                      'defaultvalue' => 1,
  70                      'note' => T_('Make line breaks (&lt;br /&gt;) for single newlines.'),
  71                  ),
  72                  'add_p_in_block' => array(
  73                      'label' => T_('Add P tags in blocks (e.g. DIV)'),
  74                      'type' => 'checkbox',
  75                      'defaultvalue' => 1,
  76                      'note' => '',
  77                  ),
  78                  'skip_tags' => array(
  79                      'label' => T_('Ignore tags'),
  80                      'type' => 'text',
  81                      'defaultvalue' => 'pre',
  82                      'note' => T_('A list of tags, in which no P or BR tags should get added.'),
  83                  ),
  84              );
  85      }
  86  
  87  
  88      /**
  89       * Perform rendering
  90       */
  91  	function RenderItemAsHtml( & $params )
  92      {
  93          #echo '<hr style="border:1em solid blue;" />';
  94  
  95          $this->use_auto_br = $this->Settings->get('br');
  96          $this->add_p_in_block = $this->Settings->get('add_p_in_block');
  97          $this->skip_tags = preg_split( '~\s+~', $this->Settings->get('skip_tags'), -1, PREG_SPLIT_NO_EMPTY );
  98  
  99          $content = & $params['data'];
 100  
 101          $content = preg_replace( "~(\r\n|\r)~", "\n", $content ); // cross-platform newlines
 102  
 103          // Handle blocks, splitted by comments. This includes especially the "meta-comments"
 104          // ("<!--more-->", "<!--nextpage-->" or "<!--noteaser-->"):
 105          $content_parts = preg_split( '~(<!--.*?-->)~s', $content, -1, PREG_SPLIT_DELIM_CAPTURE);
 106          $content_parts[] = '';
 107  
 108          $content = '';
 109          for( $i = 0; $i < count($content_parts); $i = $i+2 )
 110          {
 111              /*
 112              yura: If it is used to render outside code/pre tags it closes a <p> tag before each <code>/<pre> block and again open new <p> after that.
 113                    So it looks as blocks <code>/<pre> is contained in separate <p>, and the line is broken.
 114                    Probably we should forgot about preventing this render plugin in <code>/<pre> blocks.
 115              if( stristr( $content_parts[$i], '<code' ) !== false || stristr( $content_parts[$i], '<pre' ) !== false )
 116              {    // Call handle_blocks() on everything outside code/pre:
 117                  $content .= callback_on_non_matching_blocks( $content_parts[$i],
 118                      '~<(code|pre)[^>]*>.*?</\1>~is',
 119                      array( $this, 'handle_blocks' ) );
 120              }
 121              else
 122              {    // No code/pre blocks, replace on the whole thing
 123              */
 124                  $content .= $this->handle_blocks( $content_parts[$i] );
 125              //}
 126              $content .= $content_parts[$i+1];
 127          }
 128  
 129          return true;
 130      }
 131  
 132  
 133      /**
 134       * Define here default collection/blog settings that are to be made available in the backoffice.
 135       *
 136       * @param array Associative array of parameters.
 137       * @return array See {@link Plugin::get_coll_setting_definitions()}.
 138       */
 139  	function get_coll_setting_definitions( & $params )
 140      {
 141          $default_params = array_merge( $params,
 142              array(
 143                  'default_comment_rendering' => 'stealth',
 144                  'default_post_rendering' => 'opt-out'
 145              )
 146          );
 147          return parent::get_coll_setting_definitions( $default_params );
 148      }
 149  
 150  
 151      /**
 152       * - Split text into blocks, using $block_tags pattern.
 153       *
 154       * @param string Text
 155       * @param string The HTML tag where $text is in
 156       * @return string
 157       */
 158  	function handle_blocks( $text, $in_tag = '' )
 159      {
 160          #echo '<h1>HANDLE_BLOCKS</h1>'; pre_dump( $text, $in_tag );
 161  
 162          $new_text = '';
 163  
 164          if( preg_match( '~^(.*?)(<\s*('.$this->block_tags.')(\b[^>]*)?>)~is', $text, $match ) )
 165          { // there's a block tag:
 166              $tag = $match[3];
 167              $before_tag = $match[1];
 168  
 169              if( ! empty($before_tag) )
 170              { // Recurse (one pattern/callback deeper):
 171                  $new_text .= $this->handle_pre_blocks( $before_tag, $in_tag );
 172              }
 173  
 174              $text_after_tag = substr( $text, strlen($match[0]) );
 175  
 176              if( empty($match[4]) || substr(rtrim($match[4]), -1) != '/' )
 177              { // Opening tag (and not self-closing): handle text in tag:
 178                  $new_text .= $match[2];
 179  
 180                  // Find closing tag:
 181                  list( $text_in_tag, $closing_tag, $NL_before, $NL_after ) = $this->split_text_for_tag($tag, $text_after_tag);
 182  
 183                  if( ! empty($text_in_tag) )
 184                  { // Recurse (same level):
 185                      $text_in_tag = $this->handle_blocks( $text_in_tag, $tag );
 186                  }
 187  
 188                  $new_text .= $NL_before.$text_in_tag.$NL_after;
 189  
 190                  $new_text .= $closing_tag;
 191              }
 192              else
 193              { // self-closing tag:
 194                  $new_text .= $match[2];
 195              }
 196  
 197              if( ! empty($text_after_tag) )
 198              {
 199                  #echo '<h1>RECURSE: text_after_tag (block)</h1>';
 200                  // Recurse (same level):
 201                  $new_text .= $this->handle_blocks( $text_after_tag, $in_tag );
 202              }
 203          }
 204          else
 205          { // No BLOCKS in this $text:
 206              $new_text = $this->handle_pre_blocks($text, $in_tag);
 207          }
 208  
 209          #pre_dump( 'HANDLE_BLOCKS return: ', $new_text, $in_tag );
 210          return $new_text;
 211      }
 212  
 213  
 214      /**
 215       * Handle text which may contain inline tags
 216       *
 217       * - Explode by \n\n
 218       * - Merge blocks that span over multiple tags
 219       * - Apply BR to blocks
 220       * - Wrap block in P
 221       *
 222       * @param string Text
 223       * @param string Tag where $text is in
 224       * @return string
 225       */
 226  	function handle_pre_blocks( $text, $in_tag )
 227      {
 228          #echo '<h2>HANDLE_PRE_BLOCKS</h2>'; pre_dump( $text, $in_tag );
 229  
 230          if( $in_tag )
 231          {
 232              if( in_array($in_tag, $this->skip_tags) )
 233              {
 234                  return $text;
 235              }
 236  
 237              if( ! in_array($in_tag, $this->p_allowed_in) )
 238              { // we're in a tag, where no P tags are allowed, so just do the BRs:
 239                  return $this->handle_br( $text, $in_tag );
 240              }
 241          }
 242  
 243          $text_lines = preg_split( '~(\n\n+)~', $text, -1, /*PREG_SPLIT_NO_EMPTY |*/ PREG_SPLIT_DELIM_CAPTURE );
 244          $text_lines[] = ''; // dummy
 245  
 246          #echo '<strong>text_lines</strong><br />'; pre_dump( $text_lines, $in_tag );
 247  
 248          $new_blocks = array();
 249          $count_new_blocks = 0;
 250          for( $i = 0, $n = count($text_lines); $i < $n; $i = $i+2 /* every second block is a real one */ )
 251          {
 252              if( ! isset($new_blocks[$count_new_blocks]) )
 253              {
 254                  $new_blocks[$count_new_blocks] = '';
 255              }
 256              if( $text_lines[$i] == '' )
 257              {
 258                  $new_blocks[$count_new_blocks] .= $text_lines[$i+1];
 259                  $new_blocks[$count_new_blocks+1] = ''; // dummy
 260                  continue;
 261              }
 262              $new_blocks[$count_new_blocks] .= $text_lines[$i];
 263              $new_blocks[$count_new_blocks+1] = $text_lines[$i+1];
 264              $count_new_blocks = $count_new_blocks+2;
 265          }
 266  
 267          $text_lines = $new_blocks;
 268          #echo '<strong>new text_lines</strong><br />'; pre_dump( $new_blocks );
 269  
 270          if( trim($text) == '' )
 271          { // there's only whitespace
 272              return $text;
 273          }
 274  
 275  
 276          // fix it, so no (inline) tags span across multiple blocks:
 277          $new_blocks = array();
 278          $new_blocks_nowrap = array(); // blocks that should not be wrapped in P (opening without closing tag or vice versa)
 279          $count_new_blocks = 0;
 280          $looking_for_close_tag = array();
 281  
 282          for( $i = 0, $n = count($text_lines); $i < $n; $i = $i+2 /* every 2nd line is a real block */ )
 283          {
 284              $line = $text_lines[$i];
 285              if( ! isset($new_blocks[$count_new_blocks]) )
 286              {
 287                  $new_blocks[$count_new_blocks] = '';
 288  
 289                  $line_copy = $line;
 290  
 291                  while( preg_match( '~^(.*?)(<\s*/\s*(\w+)(\s+[^>]*?)?>)~is', $line_copy, $match )
 292                      && !(preg_match( '~^(.*?)(<\s*'.$match[3].'(\s+[^>]*?(\s*/\s*)?)?>)~is', $new_blocks[$count_new_blocks].$match[1] )) )
 293                  { // a closing tag:
 294                      $new_blocks[$count_new_blocks] .= $match[0];
 295                      $line_copy = substr($line_copy, strlen($match[0]));
 296                  }
 297                  if( ! empty($new_blocks[$count_new_blocks]) )
 298                  { // we've found a closing tag with no opening tag, this must not get wrapped in P:
 299                      $new_blocks_nowrap[] = $count_new_blocks;
 300                      $new_blocks[$count_new_blocks+1] = ''; // dummy
 301                      $new_blocks[$count_new_blocks+2] = ''; // init new
 302                      $line = substr( $line, strlen($new_blocks[$count_new_blocks]) );
 303                      $count_new_blocks = $count_new_blocks+2;
 304                  }
 305              }
 306              else
 307              { // we're looking for a closing tag:
 308                  // Find closing tag:
 309                  $line_copy = $line;
 310                  list( $text_in_tag, $closing_tag, $NL_before, $NL_after ) = $this->split_text_for_tag( $looking_for_close_tag['tag'], $line_copy /* by ref */ );
 311                  if( empty($closing_tag) )
 312                  { // not in this whole block:
 313                      $new_blocks[ $count_new_blocks ] .= $line.$text_lines[$i+1];
 314                      continue;
 315                  }
 316  
 317                  // Tag has been found:
 318                  $looking_for_close_tag = array();
 319              }
 320  
 321              if( preg_match( '~^(.*?)(<\s*(\w+)(\s+[^>/]*)?(\s*/\s*)?>)~is', $line, $match ) )
 322              { // a opening tag:
 323                  $tag = $match[3];
 324                  $pos_after_tag = strlen($match[0]);
 325  
 326                  while( ! empty($match[5]) /* "/" */ )
 327                  { // self-closing tag, find next:
 328                      $tag = false;
 329                      if( preg_match( '~^(.*?)(<\s*(\w+)(\s+[^>/]*)?(\s*/\s*)?>)~is', substr($line, $pos_after_tag), $match ) )
 330                      {
 331                          $tag = $match[3];
 332                          $pos_after_tag += strlen($match[0]);
 333                      }
 334                  }
 335                  $text_after_tag = substr($line, $pos_after_tag);
 336  
 337                  if( $tag )
 338                  {
 339                      // Find closing tag:
 340                      list( $text_in_tag, $closing_tag, $NL_before, $NL_after ) = $this->split_text_for_tag($tag, $text_after_tag /* by ref */ );
 341                      if( empty($closing_tag) )
 342                      {
 343                          $looking_for_close_tag = array(
 344                                  'tag' => $tag,
 345                                  'block' => $i,
 346                                  'pos' => strlen($new_blocks[ $count_new_blocks ])+strlen($match[1]), // position where the unclosed tag begins
 347                              );
 348                          $new_blocks[ $count_new_blocks ] .= $line.$text_lines[$i+1];
 349                          continue;
 350                      }
 351                  }
 352              }
 353              $new_blocks[ $count_new_blocks ] .= $line;
 354              $new_blocks[ ++$count_new_blocks ] = $text_lines[$i+1];
 355  
 356              $count_new_blocks++;
 357          }
 358          if( $looking_for_close_tag )
 359          {
 360              #echo '<h1>looking_for_close_tag</h1>'; pre_dump( $looking_for_close_tag );
 361              $new_blocks[ $count_new_blocks+1 ] = ''; // dummy
 362  
 363              if( $looking_for_close_tag['pos'] > 0 )
 364              { // move part of last block without closing tag to an own block:
 365                  $new_blocks[ $count_new_blocks+2 ] = substr($new_blocks[ $count_new_blocks ], $looking_for_close_tag['pos']);
 366                  $new_blocks[ $count_new_blocks ] = substr($new_blocks[ $count_new_blocks ], 0, $looking_for_close_tag['pos']);
 367                  $new_blocks[ $count_new_blocks+3 ] = ''; // dummy
 368  
 369                  $new_blocks_nowrap[] = $count_new_blocks+2;
 370              }
 371              else
 372              { // the whole block should not get wrapped!
 373                  $new_blocks_nowrap[] = $count_new_blocks;
 374              }
 375          }
 376  
 377          #echo '<h1>new_blocks:</h1>'; pre_dump( $new_blocks, $new_blocks_nowrap );
 378  
 379          $after_block_wp = '';
 380          $before_block_wp = '';
 381          if( empty($in_tag) )
 382          {
 383              $wrap_in_p = true;
 384          }
 385          elseif( in_array($in_tag, $this->p_allowed_in) )
 386          {
 387              if( ! $this->add_p_in_block )
 388              {
 389                  $wrap_in_p = false;
 390              }
 391              elseif( count($new_blocks) > 2 )
 392              {
 393                  $wrap_in_p = true;
 394              }
 395              else
 396              {
 397                  $wrap_in_p = false;
 398                  if( substr( $new_blocks[0], 0, 1 ) == "\n" )
 399                  {
 400                      $before_block_wp = "\n";
 401                      $new_blocks[0] = substr( $new_blocks[0], 1 );
 402                      $wrap_in_p = true;
 403                  }
 404                  if( substr( $new_blocks[0], -1 ) == "\n" )
 405                  {
 406                      $after_block_wp = "\n";
 407                      $new_blocks[0] = substr( $new_blocks[0], 0, -1 );
 408                      $wrap_in_p = true;
 409                  }
 410              }
 411          }
 412          else
 413          {
 414              $wrap_in_p = false;
 415          }
 416  
 417          if( $new_blocks[count($new_blocks)-2] == '' )
 418          {
 419              array_pop($new_blocks);
 420              array_pop($new_blocks);
 421          }
 422  
 423          $new_text = '';
 424  
 425          for( $i = 0, $n = count($new_blocks); $i < $n; $i = $i+2 )
 426          {
 427              #echo '<h2>--new_blocks['.$i.']: '; pre_dump( $new_blocks[$i] ); echo '</h2>';
 428  
 429              $this_wrap_in_p = $wrap_in_p && ! in_array( $i, $new_blocks_nowrap ); // only wrap this, if it's a valid block
 430  
 431              if( empty($new_blocks[$i]) )
 432              {
 433                  if( $this_wrap_in_p )
 434                  { // not the last one
 435                      $block = '<p></p>';
 436                  }
 437                  else
 438                  {
 439                      $block = '';
 440                  }
 441                  $new_text .= $before_block_wp.$block.$after_block_wp.$new_blocks[$i+1];
 442                  continue;
 443              }
 444  
 445              list($new_block, $has_p) = $this->handle_pre_blocks_helper( $new_blocks[$i], $in_tag, $this_wrap_in_p );
 446  
 447  
 448              $new_text .= $before_block_wp.$new_block.$after_block_wp.$new_blocks[$i+1];
 449          }
 450  
 451          #pre_dump( 'HANDLE_PRE_BLOCKS return: ', $new_text, $in_tag );
 452          return $new_text;
 453      }
 454  
 455  
 456      /**
 457       * This is a helper for handling blocks from {@link handle_pre_blocks_helper()}.
 458       *
 459       * What comes here is supposed to have no block tags.
 460       *
 461       * @return array array( $text, $has_p )
 462       */
 463  	function handle_pre_blocks_helper( $block, $in_tag, $wrap_in_p, $ignore_NL = true )
 464      {
 465          #pre_dump( 'HANDLE_PRE_BLOCKS_HELPER begin', $block, $in_tag );
 466          $has_p = NULL;
 467          $r = '';
 468  
 469          if( $in_tag == 'blockquote' )
 470          { // XHTML strict: blockquote content needs to be in block tag
 471              $in_tag = 'p';
 472              $wrap_in_p = true; // at the end
 473          }
 474  
 475          // Remove newlines at start and end (will get re-applied later):
 476          $NL_start = '';
 477          $NL_end = '';
 478          if( $ignore_NL )
 479          {
 480              while( $block{0} == "\n" )
 481              {
 482                  $NL_start .= $block{0};
 483                  $block = substr($block, 1);
 484              }
 485              while( substr($block, -1) == "\n" )
 486              {
 487                  $NL_end .= substr($block, -1);
 488                  $block = substr($block, 0, -1);
 489              }
 490          }
 491  
 492          if( preg_match( '~^(.*?)(<\s*(\w+)(\s+[^>]*)?>)~is', $block, $match ) )
 493          { // a tag:
 494              $tag = $match[3];
 495              $before_tag = $match[1];
 496  
 497              if( ! empty($before_tag) )
 498              { // Delegate to handle_br:
 499                  $r .= $this->handle_br( $before_tag, $in_tag );
 500              }
 501  
 502              // Opening tag:
 503              $r .= $match[2];
 504  
 505              $text_after_tag = substr( $block, strlen($match[0]) );
 506  
 507              // Find closing tag:
 508              list( $text_in_tag, $closing_tag, $NL_before, $NL_after ) = $this->split_text_for_tag($tag, $text_after_tag);
 509  
 510              if( ! empty($text_in_tag) )
 511              { // Recurse (same level) - with the optional newlines at start and end, because in an inline tag every linebreak should become a BR:
 512                  list($text_in_tag, $sub_has_p) = $this->handle_pre_blocks_helper( $NL_before.$text_in_tag.$NL_after, $tag, false, false );
 513              }
 514              $r .= $text_in_tag;
 515  
 516              $r .= $closing_tag;
 517  
 518              if( ! empty($text_after_tag) )
 519              {
 520                  #echo '<h1>RECURSE: text_after_tag (handle_pre_blocks)</h1>';
 521                  // Recurse (same level):
 522                  list( $text_after_tag, $sub_has_p ) = $this->handle_pre_blocks_helper( $text_after_tag, $in_tag, false, false );
 523                  $r .= $text_after_tag;
 524              }
 525          }
 526          else
 527          { // No tags in this $text:
 528              $r .= $this->handle_br( $block, $in_tag );
 529          }
 530  
 531          if( ! empty($wrap_in_p) )
 532          {
 533              $r = '<p>'.$r.'</p>';
 534              $has_p = true;
 535          }
 536  
 537          // re-apply newlines from start and end:
 538          $r = $NL_start.$r.$NL_end;
 539  
 540          #pre_dump( 'HANDLE_PRE_BLOCKS_HELPER return: ', $r, $has_p );
 541          return array( $r, $has_p );
 542      }
 543  
 544  
 545      /**
 546       * Handles adding BR.
 547       *
 548       * @return string
 549       */
 550  	function handle_br( $text, $in_tag )
 551      {
 552          #echo '<h3>LEVEL>1 (BR)</h3>'; pre_dump( $text );
 553  
 554          if( empty($in_tag) || in_array($in_tag, $this->br_allowed_in) )
 555          {
 556              $new_text = $this->autobr($text);
 557          }
 558          else
 559          {
 560              $new_text = $text;
 561          }
 562  
 563          #pre_dump( 'HANDLE_BR return: ', $new_text, $in_tag );
 564          return $new_text;
 565      }
 566  
 567  
 568      /**
 569       * Split the text for a given tag, mainly to find the closing tag.
 570       *
 571       * @return array
 572       */
 573  	function split_text_for_tag($tag, & $text_after_tag)
 574      {
 575          #echo '<strong>split_text_for_tag</strong><br />'; pre_dump( $tag, $text_after_tag );
 576          $depth = 1;
 577          $text_in_tag = '';
 578  
 579          $loop_text = $text_after_tag;
 580          while( 1 )
 581          {
 582              #echo '<hr />loop_text:'; pre_dump( $loop_text );
 583              if( preg_match( '~^(.*?)(<\s*(/)?\s*'.preg_quote( $tag, '~' ).'\s*(/\s*)?>)~is', $loop_text, $after_match ) )
 584              {
 585                  #pre_dump( 'after_match', $after_match );
 586                  $text_in_tag .= $after_match[1];
 587                  $found_tag = $after_match[2];
 588                  $is_closing = ( ! empty($after_match[3]) || ! empty($after_match[4]) /* self-closing */ );
 589  
 590                  if( $is_closing )
 591                  {
 592                      $depth--;
 593                      if( $depth == 0 )
 594                      { // found the matching closing tag:
 595                          $closing_tag = $found_tag;
 596                          break;
 597                      }
 598                      else
 599                      { // this closing tag is part of the outer:
 600                          $text_in_tag .= $found_tag;
 601                      }
 602                  }
 603                  else
 604                  { // found the same, but opening tag (nested)
 605                      $text_in_tag .= $found_tag;
 606                      $depth++;
 607                  }
 608  
 609                  // skip what we've matched:
 610                  $loop_text = substr($loop_text, strlen($after_match[0]) );
 611              }
 612              else
 613              { // did not find the closing tag.. :/
 614                  $closing_tag = '';
 615                  return array( false, false, false, false );
 616              }
 617          }
 618  
 619          // remove newline at start and end:
 620          if( substr($text_in_tag, 0, 1) == "\n" )
 621          {
 622              $NL_before = "\n";
 623              $text_in_tag = substr($text_in_tag, 1);
 624          }
 625          else
 626          {
 627              $NL_before = '';
 628          }
 629          if( substr($text_in_tag, -1) == "\n" )
 630          {
 631              $NL_after = "\n";
 632              $text_in_tag = substr($text_in_tag, 0, -1);
 633          }
 634          else
 635          {
 636              $NL_after = '';
 637          }
 638  
 639          $text_after_tag = substr( $text_after_tag, strlen($NL_before.$text_in_tag.$NL_after)+strlen($closing_tag) );
 640          $r = array( $text_in_tag, $closing_tag, $NL_before, $NL_after );
 641  
 642          #pre_dump( 'return: ', $r, $text_after_tag );
 643          return $r;
 644      }
 645  
 646  
 647      /**
 648       * Add "<br />" to the end of newlines, which do not end with "<br />" already and which aren't
 649       * the last line, if the "Auto-BR" setting is enabled.
 650       *
 651       * @return string
 652       */
 653  	function autobr( $text, $replace_last = true )
 654      {
 655          if( ! $this->use_auto_br )
 656          { // don't make <br />'s
 657              return $text;
 658          }
 659  
 660          return preg_replace( '~(?<!<br />)\n'.( $replace_last ? '' : '(?!\z)' ).'~i', "<br />\n", $text );
 661      }
 662  
 663  }
 664  
 665  ?>

title

Description

title

Description

title

Description

title

title

Body