[ Index ]

PHP Cross Reference of BBPress

title

Body

[close]

/src/includes/admin/ -> parser.php (source)

   1  <?php
   2  
   3  /**
   4   * bbPress BBCode Parser
   5   *
   6   * @package bbPress
   7   * @subpackage Administration
   8   */
   9  
  10  /*
  11  This is a compressed copy of NBBC. Do not edit!
  12  
  13  Copyright (c) 2008-9, the Phantom Inker.  All rights reserved.
  14  Portions Copyright (c) 2004-2008 AddedBytes.com
  15  
  16  Redistribution and use in source and binary forms, with or without
  17  modification, are permitted provided that the following conditions
  18  are met:
  19  
  20  * Redistributions of source code must retain the above copyright
  21    notice, this list of conditions and the following disclaimer.
  22  
  23  * Redistributions in binary form must reproduce the above copyright
  24    notice, this list of conditions and the following disclaimer in
  25    the documentation and/or other materials provided with the
  26    distribution.
  27  
  28  THIS SOFTWARE IS PROVIDED BY THE PHANTOM INKER "AS IS" AND ANY EXPRESS
  29  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  30  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  31  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  32  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  33  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  34  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
  35  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  36  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
  37  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
  38  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  39  */
  40  
  41  define("BBCODE_VERSION", "1.4.5");
  42  define("BBCODE_RELEASE", "2010-09-17");
  43  define("BBCODE_VERBATIM", 2);
  44  define("BBCODE_REQUIRED", 1);
  45  define("BBCODE_OPTIONAL", 0);
  46  define("BBCODE_PROHIBIT", -1);
  47  define("BBCODE_CHECK", 1);
  48  define("BBCODE_OUTPUT", 2);
  49  define("BBCODE_ENDTAG", 5);
  50  define("BBCODE_TAG", 4);
  51  define("BBCODE_TEXT", 3);
  52  define("BBCODE_NL", 2);
  53  define("BBCODE_WS", 1);
  54  define("BBCODE_EOI", 0);
  55  define("BBCODE_LEXSTATE_TEXT", 0);
  56  define("BBCODE_LEXSTATE_TAG", 1);
  57  define("BBCODE_MODE_SIMPLE", 0);
  58  define("BBCODE_MODE_CALLBACK", 1);
  59  define("BBCODE_MODE_INTERNAL", 2);
  60  define("BBCODE_MODE_LIBRARY", 3);
  61  define("BBCODE_MODE_ENHANCED", 4);
  62  define("BBCODE_STACK_TOKEN", 0);
  63  define("BBCODE_STACK_TEXT", 1);
  64  define("BBCODE_STACK_TAG", 2);
  65  define("BBCODE_STACK_CLASS", 3);
  66  if (!function_exists('str_split')) {
  67  function str_split($string, $split_length = 1) {
  68  $array = explode("\r\n", chunk_split($string, $split_length));
  69  array_pop($array);
  70  return $array;
  71  }
  72  }
  73  $BBCode_SourceDir = dirname(__FILE__);
  74  
  75  class BBCodeLexer {
  76  var $token;
  77  var $text;
  78  var $tag;
  79  var $state;
  80  var $input;
  81  var $ptr;
  82  var $unget;
  83  var $verbatim;
  84  var $debug;
  85  var $tagmarker;
  86  var $end_tagmarker;
  87  var $pat_main;
  88  var $pat_comment;
  89  var $pat_comment2;
  90  var $pat_wiki;
  91  function __construct($string, $tagmarker = '[') {
  92  $regex_beginmarkers = Array( '[' => '\[', '<' => '<', '{' => '\{', '(' => '\(' );
  93  $regex_endmarkers = Array( '[' => '\]', '<' => '>', '{' => '\}', '(' => '\)' );
  94  $endmarkers = Array( '[' => ']', '<' => '>', '{' => '}', '(' => ')' );
  95  if (!isset($regex_endmarkers[$tagmarker])) $tagmarker = '[';
  96  $e = $regex_endmarkers[$tagmarker];
  97  $b = $regex_beginmarkers[$tagmarker];
  98  $this->tagmarker = $tagmarker;
  99  $this->end_tagmarker = $endmarkers[$tagmarker];
 100  $this->pat_main = "/( "
 101  . "{$b}"
 102  . "(?! -- | ' | !-- | {$b}{$b} )"
 103  . "(?: [^\\n\\r{$b}{$e}] | \\\" [^\\\"\\n\\r]* \\\" | \\' [^\\'\\n\\r]* \\' )*"
 104  . "{$e}"
 105  . "| {$b}{$b} (?: [^{$e}\\r\\n] | {$e}[^{$e}\\r\\n] )* {$e}{$e}"
 106  . "| {$b} (?: -- | ' ) (?: [^{$e}\\n\\r]* ) {$e}"
 107  . "| {$b}!-- (?: [^-] | -[^-] | --[^{$e}] )* --{$e}"
 108  . "| -----+"
 109  . "| \\x0D\\x0A | \\x0A\\x0D | \\x0D | \\x0A"
 110  . "| [\\x00-\\x09\\x0B-\\x0C\\x0E-\\x20]+(?=[\\x0D\\x0A{$b}]|-----|$)"
 111  . "| (?<=[\\x0D\\x0A{$e}]|-----|^)[\\x00-\\x09\\x0B-\\x0C\\x0E-\\x20]+"
 112  . " )/Dx";
 113  $this->input = preg_split($this->pat_main, $string, -1, PREG_SPLIT_DELIM_CAPTURE);
 114  $this->pat_comment = "/^ {$b} (?: -- | ' ) /Dx";
 115  $this->pat_comment2 = "/^ {$b}!-- (?: [^-] | -[^-] | --[^{$e}] )* --{$e} $/Dx";
 116  $this->pat_wiki = "/^ {$b}{$b} ([^\\|]*) (?:\\|(.*))? {$e}{$e} $/Dx";
 117  $this->ptr = 0;
 118  $this->unget = false;
 119  $this->state = BBCODE_LEXSTATE_TEXT;
 120  $this->verbatim = false;
 121  $this->token = BBCODE_EOI;
 122  $this->tag = false;
 123  $this->text = "";
 124  }
 125  function BBCodeLexer($string, $tagmarker = '[') {
 126  $this->__construct($string, $tagmarker);
 127  }
 128  function GuessTextLength() {
 129  $length = 0;
 130  $ptr = 0;
 131  $state = BBCODE_LEXSTATE_TEXT;
 132  while ($ptr < count($this->input)) {
 133  $text = $this->input[$ptr++];
 134  if ($state == BBCODE_LEXSTATE_TEXT) {
 135  $state = BBCODE_LEXSTATE_TAG;
 136  $length += strlen($text);
 137  }
 138  else {
 139  switch (ord(substr($this->text, 0, 1))) {
 140  case 10:
 141  case 13:
 142  $state = BBCODE_LEXSTATE_TEXT;
 143  $length++;
 144  break;
 145  default:
 146  $state = BBCODE_LEXSTATE_TEXT;
 147  $length += strlen($text);
 148  break;
 149  case 40:
 150  case 60:
 151  case 91:
 152  case 123:
 153  $state = BBCODE_LEXSTATE_TEXT;
 154  break;
 155  }
 156  }
 157  }
 158  return $length;
 159  }
 160  function NextToken() {
 161  if ($this->unget) {
 162  $this->unget = false;
 163  return $this->token;
 164  }
 165  while (true) {
 166  if ($this->ptr >= count($this->input)) {
 167  $this->text = "";
 168  $this->tag = false;
 169  return $this->token = BBCODE_EOI;
 170  }
 171  $this->text = preg_replace("/[\\x00-\\x08\\x0B-\\x0C\\x0E-\\x1F]/", "",
 172  $this->input[$this->ptr++]);
 173  if ($this->verbatim) {
 174  $this->tag = false;
 175  if ($this->state == BBCODE_LEXSTATE_TEXT) {
 176  $this->state = BBCODE_LEXSTATE_TAG;
 177  $token_type = BBCODE_TEXT;
 178  }
 179  else {
 180  $this->state = BBCODE_LEXSTATE_TEXT;
 181  switch (ord(substr($this->text, 0, 1))) {
 182  case 10:
 183  case 13:
 184  $token_type = BBCODE_NL;
 185  break;
 186  default:
 187  $token_type = BBCODE_WS;
 188  break;
 189  case 45:
 190  case 40:
 191  case 60:
 192  case 91:
 193  case 123:
 194  $token_type = BBCODE_TEXT;
 195  break;
 196  }
 197  }
 198  if (strlen($this->text) > 0)
 199  return $this->token = $token_type;
 200  }
 201  else if ($this->state == BBCODE_LEXSTATE_TEXT) {
 202  $this->state = BBCODE_LEXSTATE_TAG;
 203  $this->tag = false;
 204  if (strlen($this->text) > 0)
 205  return $this->token = BBCODE_TEXT;
 206  }
 207  else {
 208  switch (ord(substr($this->text, 0, 1))) {
 209  case 10:
 210  case 13:
 211  $this->tag = false;
 212  $this->state = BBCODE_LEXSTATE_TEXT;
 213  return $this->token = BBCODE_NL;
 214  case 45:
 215  if (preg_match("/^-----/", $this->text)) {
 216  $this->tag = Array('_name' => 'rule', '_endtag' => false, '_default' => '');
 217  $this->state = BBCODE_LEXSTATE_TEXT;
 218  return $this->token = BBCODE_TAG;
 219  }
 220  else {
 221  $this->tag = false;
 222  $this->state = BBCODE_LEXSTATE_TEXT;
 223  if (strlen($this->text) > 0)
 224  return $this->token = BBCODE_TEXT;
 225  continue 2;
 226  }
 227  default:
 228  $this->tag = false;
 229  $this->state = BBCODE_LEXSTATE_TEXT;
 230  return $this->token = BBCODE_WS;
 231  case 40:
 232  case 60:
 233  case 91:
 234  case 123:
 235  if (preg_match($this->pat_comment, $this->text)) {
 236  $this->state = BBCODE_LEXSTATE_TEXT;
 237  continue 2;
 238  }
 239  if (preg_match($this->pat_comment2, $this->text)) {
 240  $this->state = BBCODE_LEXSTATE_TEXT;
 241  continue 2;
 242  }
 243  if (preg_match($this->pat_wiki, $this->text, $matches)) {
 244  $this->tag = Array('_name' => 'wiki', '_endtag' => false,
 245  '_default' => @$matches[1], 'title' => @$matches[2]);
 246  $this->state = BBCODE_LEXSTATE_TEXT;
 247  return $this->token = BBCODE_TAG;
 248  }
 249  $this->tag = $this->Internal_DecodeTag($this->text);
 250  $this->state = BBCODE_LEXSTATE_TEXT;
 251  return $this->token = ($this->tag['_end'] ? BBCODE_ENDTAG : BBCODE_TAG);
 252  }
 253  }
 254  }
 255  }
 256  function UngetToken() {
 257  if ($this->token !== BBCODE_EOI)
 258  $this->unget = true;
 259  }
 260  function PeekToken() {
 261  $result = $this->NextToken();
 262  if ($this->token !== BBCODE_EOI)
 263  $this->unget = true;
 264  return $result;
 265  }
 266  function SaveState() {
 267  return Array(
 268  'token' => $this->token,
 269  'text' => $this->text,
 270  'tag' => $this->tag,
 271  'state' => $this->state,
 272  'input' => $this->input,
 273  'ptr' => $this->ptr,
 274  'unget' => $this->unget,
 275  'verbatim' => $this->verbatim
 276  );
 277  }
 278  function RestoreState($state) {
 279  if (!is_array($state)) return;
 280  $this->token = @$state['token'];
 281  $this->text = @$state['text'];
 282  $this->tag = @$state['tag'];
 283  $this->state = @$state['state'];
 284  $this->input = @$state['input'];
 285  $this->ptr = @$state['ptr'];
 286  $this->unget = @$state['unget'];
 287  $this->verbatim = @$state['verbatim'];
 288  }
 289  function Internal_StripQuotes($string) {
 290  if (preg_match("/^\\\"(.*)\\\"$/", $string, $matches))
 291  return $matches[1];
 292  else if (preg_match("/^\\'(.*)\\'$/", $string, $matches))
 293  return $matches[1];
 294  else return $string;
 295  }
 296  function Internal_ClassifyPiece($ptr, $pieces) {
 297  if ($ptr >= count($pieces)) return -1;
 298  $piece = $pieces[$ptr];
 299  if ($piece == '=') return '=';
 300  else if (preg_match("/^[\\'\\\"]/", $piece)) return '"';
 301  else if (preg_match("/^[\\x00-\\x20]+$/", $piece)) return ' ';
 302  else return 'A';
 303  }
 304  function Internal_DecodeTag($tag) {
 305  $result = Array('_tag' => $tag, '_endtag' => '', '_name' => '',
 306  '_hasend' => false, '_end' => false, '_default' => false);
 307  $tag = substr($tag, 1, strlen($tag)-2);
 308  $ch = ord(substr($tag, 0, 1));
 309  if ($ch >= 0 && $ch <= 32) return $result;
 310  $pieces = preg_split("/(\\\"[^\\\"]+\\\"|\\'[^\\']+\\'|=|[\\x00-\\x20]+)/",
 311  $tag, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
 312  $ptr = 0;
 313  if (count($pieces) < 1) return $result;
 314  if (@substr($pieces[$ptr], 0, 1) == '/') {
 315  $result['_name'] = strtolower(substr($pieces[$ptr++], 1));
 316  $result['_end'] = true;
 317  }
 318  else {
 319  $result['_name'] = strtolower($pieces[$ptr++]);
 320  $result['_end'] = false;
 321  }
 322  while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ')
 323  $ptr++;
 324  $params = Array();
 325  if ($type != '=') {
 326  $result['_default'] = false;
 327  $params[] = Array('key' => '', 'value' => '');
 328  }
 329  else {
 330  $ptr++;
 331  while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ')
 332  $ptr++;
 333  if ($type == "\"")
 334  $value = $this->Internal_StripQuotes($pieces[$ptr++]);
 335  else {
 336  $after_space = false;
 337  $start = $ptr;
 338  while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != -1) {
 339  if ($type == ' ') $after_space = true;
 340  if ($type == '=' && $after_space) break;
 341  $ptr++;
 342  }
 343  if ($type == -1) $ptr--;
 344  if ($type == '=') {
 345  $ptr--;
 346  while ($ptr > $start && $this->Internal_ClassifyPiece($ptr, $pieces) == ' ')
 347  $ptr--;
 348  while ($ptr > $start && $this->Internal_ClassifyPiece($ptr, $pieces) != ' ')
 349  $ptr--;
 350  }
 351  $value = "";
 352  for (; $start <= $ptr; $start++) {
 353  if ($this->Internal_ClassifyPiece($start, $pieces) == ' ')
 354  $value .= " ";
 355  else $value .= $this->Internal_StripQuotes($pieces[$start]);
 356  }
 357  $value = trim($value);
 358  $ptr++;
 359  }
 360  $result['_default'] = $value;
 361  $params[] = Array('key' => '', 'value' => $value);
 362  }
 363  while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != -1) {
 364  while ($type == ' ') {
 365  $ptr++;
 366  $type = $this->Internal_ClassifyPiece($ptr, $pieces);
 367  }
 368  if ($type == 'A' || $type == '"')
 369  $key = strtolower($this->Internal_StripQuotes(@$pieces[$ptr++]));
 370  else if ($type == '=') {
 371  $ptr++;
 372  continue;
 373  }
 374  else if ($type == -1) break;
 375  while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ')
 376  $ptr++;
 377  if ($type != '=')
 378  $value = $this->Internal_StripQuotes($key);
 379  else {
 380  $ptr++;
 381  while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ')
 382  $ptr++;
 383  if ($type == '"') {
 384  $value = $this->Internal_StripQuotes($pieces[$ptr++]);
 385  }
 386  else if ($type != -1) {
 387  $value = $pieces[$ptr++];
 388  while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != -1
 389  && $type != ' ')
 390  $value .= $pieces[$ptr++];
 391  }
 392  else $value = "";
 393  }
 394  if (substr($key, 0, 1) != '_')
 395  $result[$key] = $value;
 396  $params[] = Array('key' => $key, 'value' => $value);
 397  }
 398  $result['_params'] = $params;
 399  return $result;
 400  }
 401  }
 402  
 403  class BBCodeLibrary {
 404  var $default_smileys = Array(
 405  ':)' => 'smile.gif', ':-)' => 'smile.gif',
 406  '=)' => 'smile.gif', '=-)' => 'smile.gif',
 407  ':(' => 'frown.gif', ':-(' => 'frown.gif',
 408  '=(' => 'frown.gif', '=-(' => 'frown.gif',
 409  ':D' => 'bigsmile.gif', ':-D' => 'bigsmile.gif',
 410  '=D' => 'bigsmile.gif', '=-D' => 'bigsmile.gif',
 411  '>:('=> 'angry.gif', '>:-('=> 'angry.gif',
 412  '>=('=> 'angry.gif', '>=-('=> 'angry.gif',
 413  'D:' => 'angry.gif', 'D-:' => 'angry.gif',
 414  'D=' => 'angry.gif', 'D-=' => 'angry.gif',
 415  '>:)'=> 'evil.gif', '>:-)'=> 'evil.gif',
 416  '>=)'=> 'evil.gif', '>=-)'=> 'evil.gif',
 417  '>:D'=> 'evil.gif', '>:-D'=> 'evil.gif',
 418  '>=D'=> 'evil.gif', '>=-D'=> 'evil.gif',
 419  '>;)'=> 'sneaky.gif', '>;-)'=> 'sneaky.gif',
 420  '>;D'=> 'sneaky.gif', '>;-D'=> 'sneaky.gif',
 421  'O:)' => 'saint.gif', 'O:-)' => 'saint.gif',
 422  'O=)' => 'saint.gif', 'O=-)' => 'saint.gif',
 423  ':O' => 'surprise.gif', ':-O' => 'surprise.gif',
 424  '=O' => 'surprise.gif', '=-O' => 'surprise.gif',
 425  ':?' => 'confuse.gif', ':-?' => 'confuse.gif',
 426  '=?' => 'confuse.gif', '=-?' => 'confuse.gif',
 427  ':s' => 'worry.gif', ':-S' => 'worry.gif',
 428  '=s' => 'worry.gif', '=-S' => 'worry.gif',
 429  ':|' => 'neutral.gif', ':-|' => 'neutral.gif',
 430  '=|' => 'neutral.gif', '=-|' => 'neutral.gif',
 431  ':I' => 'neutral.gif', ':-I' => 'neutral.gif',
 432  '=I' => 'neutral.gif', '=-I' => 'neutral.gif',
 433  ':/' => 'irritated.gif', ':-/' => 'irritated.gif',
 434  '=/' => 'irritated.gif', '=-/' => 'irritated.gif',
 435  ':\\' => 'irritated.gif', ':-\\' => 'irritated.gif',
 436  '=\\' => 'irritated.gif', '=-\\' => 'irritated.gif',
 437  ':P' => 'tongue.gif', ':-P' => 'tongue.gif',
 438  '=P' => 'tongue.gif', '=-P' => 'tongue.gif',
 439  'X-P' => 'tongue.gif',
 440  '8)' => 'bigeyes.gif', '8-)' => 'bigeyes.gif',
 441  'B)' => 'cool.gif', 'B-)' => 'cool.gif',
 442  ';)' => 'wink.gif', ';-)' => 'wink.gif',
 443  ';D' => 'bigwink.gif', ';-D' => 'bigwink.gif',
 444  '^_^'=> 'anime.gif', '^^;' => 'sweatdrop.gif',
 445  '>_>'=> 'lookright.gif', '>.>' => 'lookright.gif',
 446  '<_<'=> 'lookleft.gif', '<.<' => 'lookleft.gif',
 447  'XD' => 'laugh.gif', 'X-D' => 'laugh.gif',
 448  ';D' => 'bigwink.gif', ';-D' => 'bigwink.gif',
 449  ':3' => 'smile3.gif', ':-3' => 'smile3.gif',
 450  '=3' => 'smile3.gif', '=-3' => 'smile3.gif',
 451  ';3' => 'wink3.gif', ';-3' => 'wink3.gif',
 452  '<g>' => 'teeth.gif', '<G>' => 'teeth.gif',
 453  'o.O' => 'boggle.gif', 'O.o' => 'boggle.gif',
 454  ':blue:' => 'blue.gif',
 455  ':zzz:' => 'sleepy.gif',
 456  '<3' => 'heart.gif',
 457  ':star:' => 'star.gif',
 458  );
 459  var $default_tag_rules = Array(
 460  'b' => Array(
 461  'simple_start' => "<b>",
 462  'simple_end' => "</b>",
 463  'class' => 'inline',
 464  'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'),
 465  'plain_start' => "<b>",
 466  'plain_end' => "</b>",
 467  ),
 468  'i' => Array(
 469  'simple_start' => "<i>",
 470  'simple_end' => "</i>",
 471  'class' => 'inline',
 472  'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'),
 473  'plain_start' => "<i>",
 474  'plain_end' => "</i>",
 475  ),
 476  'u' => Array(
 477  'simple_start' => "<u>",
 478  'simple_end' => "</u>",
 479  'class' => 'inline',
 480  'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'),
 481  'plain_start' => "<u>",
 482  'plain_end' => "</u>",
 483  ),
 484  's' => Array(
 485  'simple_start' => "<strike>",
 486  'simple_end' => "</strike>",
 487  'class' => 'inline',
 488  'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'),
 489  'plain_start' => "<i>",
 490  'plain_end' => "</i>",
 491  ),
 492  'font' => Array(
 493  'mode' => BBCODE_MODE_LIBRARY,
 494  'allow' => Array('_default' => '/^[a-zA-Z0-9._ -]+$/'),
 495  'method' => 'DoFont',
 496  'class' => 'inline',
 497  'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'),
 498  ),
 499  'color' => Array(
 500  'mode' => BBCODE_MODE_ENHANCED,
 501  'allow' => Array('_default' => '/^#?[a-zA-Z0-9._ -]+$/'),
 502  'template' => '<span style="color:{$_default/tw}">{$_content/v}</span>',
 503  'class' => 'inline',
 504  'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'),
 505  ),
 506  'size' => Array(
 507  'mode' => BBCODE_MODE_LIBRARY,
 508  'allow' => Array('_default' => '/^[0-9.]+$/D'),
 509  'method' => 'DoSize',
 510  'class' => 'inline',
 511  'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'),
 512  ),
 513  'sup' => Array(
 514  'simple_start' => "<sup>",
 515  'simple_end' => "</sup>",
 516  'class' => 'inline',
 517  'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'),
 518  ),
 519  'sub' => Array(
 520  'simple_start' => "<sub>",
 521  'simple_end' => "</sub>",
 522  'class' => 'inline',
 523  'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'),
 524  ),
 525  'spoiler' => Array(
 526  'simple_start' => "<span class=\"bbcode_spoiler\">",
 527  'simple_end' => "</span>",
 528  'class' => 'inline',
 529  'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'),
 530  ),
 531  'acronym' => Array(
 532  'mode' => BBCODE_MODE_ENHANCED,
 533  'template' => '<span class="bbcode_acronym" title="{$_default/e}">{$_content/v}</span>',
 534  'class' => 'inline',
 535  'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'),
 536  ),
 537  'url' => Array(
 538  'mode' => BBCODE_MODE_LIBRARY,
 539  'method' => 'DoURL',
 540  'class' => 'link',
 541  'allow_in' => Array('listitem', 'block', 'columns', 'inline'),
 542  'content' => BBCODE_REQUIRED,
 543  'plain_start' => "<a href=\"{\$link}\">",
 544  'plain_end' => "</a>",
 545  'plain_content' => Array('_content', '_default'),
 546  'plain_link' => Array('_default', '_content'),
 547  ),
 548  'email' => Array(
 549  'mode' => BBCODE_MODE_LIBRARY,
 550  'method' => 'DoEmail',
 551  'class' => 'link',
 552  'allow_in' => Array('listitem', 'block', 'columns', 'inline'),
 553  'content' => BBCODE_REQUIRED,
 554  'plain_start' => "<a href=\"mailto:{\$link}\">",
 555  'plain_end' => "</a>",
 556  'plain_content' => Array('_content', '_default'),
 557  'plain_link' => Array('_default', '_content'),
 558  ),
 559  'wiki' => Array(
 560  'mode' => BBCODE_MODE_LIBRARY,
 561  'method' => "DoWiki",
 562  'class' => 'link',
 563  'allow_in' => Array('listitem', 'block', 'columns', 'inline'),
 564  'end_tag' => BBCODE_PROHIBIT,
 565  'content' => BBCODE_PROHIBIT,
 566  'plain_start' => "<b>[",
 567  'plain_end' => "]</b>",
 568  'plain_content' => Array('title', '_default'),
 569  'plain_link' => Array('_default', '_content'),
 570  ),
 571  'img' => Array(
 572  'mode' => BBCODE_MODE_LIBRARY,
 573  'method' => "DoImage",
 574  'class' => 'image',
 575  'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'),
 576  'end_tag' => BBCODE_REQUIRED,
 577  'content' => BBCODE_REQUIRED,
 578  'plain_start' => "[image]",
 579  'plain_content' => Array(),
 580  ),
 581  'rule' => Array(
 582  'mode' => BBCODE_MODE_LIBRARY,
 583  'method' => "DoRule",
 584  'class' => 'block',
 585  'allow_in' => Array('listitem', 'block', 'columns'),
 586  'end_tag' => BBCODE_PROHIBIT,
 587  'content' => BBCODE_PROHIBIT,
 588  'before_tag' => "sns",
 589  'after_tag' => "sns",
 590  'plain_start' => "\n-----\n",
 591  'plain_end' => "",
 592  'plain_content' => Array(),
 593  ),
 594  'br' => Array(
 595  'mode' => BBCODE_MODE_SIMPLE,
 596  'simple_start' => "<br />\n",
 597  'simple_end' => "",
 598  'class' => 'inline',
 599  'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'),
 600  'end_tag' => BBCODE_PROHIBIT,
 601  'content' => BBCODE_PROHIBIT,
 602  'before_tag' => "s",
 603  'after_tag' => "s",
 604  'plain_start' => "\n",
 605  'plain_end' => "",
 606  'plain_content' => Array(),
 607  ),
 608  'left' => Array(
 609  'simple_start' => "\n<div class=\"bbcode_left\" style=\"text-align:left\">\n",
 610  'simple_end' => "\n</div>\n",
 611  'allow_in' => Array('listitem', 'block', 'columns'),
 612  'before_tag' => "sns",
 613  'after_tag' => "sns",
 614  'before_endtag' => "sns",
 615  'after_endtag' => "sns",
 616  'plain_start' => "\n",
 617  'plain_end' => "\n",
 618  ),
 619  'right' => Array(
 620  'simple_start' => "\n<div class=\"bbcode_right\" style=\"text-align:right\">\n",
 621  'simple_end' => "\n</div>\n",
 622  'allow_in' => Array('listitem', 'block', 'columns'),
 623  'before_tag' => "sns",
 624  'after_tag' => "sns",
 625  'before_endtag' => "sns",
 626  'after_endtag' => "sns",
 627  'plain_start' => "\n",
 628  'plain_end' => "\n",
 629  ),
 630  'center' => Array(
 631  'simple_start' => "\n<div class=\"bbcode_center\" style=\"text-align:center\">\n",
 632  'simple_end' => "\n</div>\n",
 633  'allow_in' => Array('listitem', 'block', 'columns'),
 634  'before_tag' => "sns",
 635  'after_tag' => "sns",
 636  'before_endtag' => "sns",
 637  'after_endtag' => "sns",
 638  'plain_start' => "\n",
 639  'plain_end' => "\n",
 640  ),
 641  'indent' => Array(
 642  'simple_start' => "\n<div class=\"bbcode_indent\" style=\"margin-left:4em\">\n",
 643  'simple_end' => "\n</div>\n",
 644  'allow_in' => Array('listitem', 'block', 'columns'),
 645  'before_tag' => "sns",
 646  'after_tag' => "sns",
 647  'before_endtag' => "sns",
 648  'after_endtag' => "sns",
 649  'plain_start' => "\n",
 650  'plain_end' => "\n",
 651  ),
 652  'columns' => Array(
 653  'simple_start' => "\n<table class=\"bbcode_columns\"><tbody><tr><td class=\"bbcode_column bbcode_firstcolumn\">\n",
 654  'simple_end' => "\n</td></tr></tbody></table>\n",
 655  'class' => 'columns',
 656  'allow_in' => Array('listitem', 'block', 'columns'),
 657  'end_tag' => BBCODE_REQUIRED,
 658  'content' => BBCODE_REQUIRED,
 659  'before_tag' => "sns",
 660  'after_tag' => "sns",
 661  'before_endtag' => "sns",
 662  'after_endtag' => "sns",
 663  'plain_start' => "\n",
 664  'plain_end' => "\n",
 665  ),
 666  'nextcol' => Array(
 667  'simple_start' => "\n</td><td class=\"bbcode_column\">\n",
 668  'class' => 'nextcol',
 669  'allow_in' => Array('columns'),
 670  'end_tag' => BBCODE_PROHIBIT,
 671  'content' => BBCODE_PROHIBIT,
 672  'before_tag' => "sns",
 673  'after_tag' => "sns",
 674  'before_endtag' => "sns",
 675  'after_endtag' => "sns",
 676  'plain_start' => "\n",
 677  'plain_end' => "",
 678  ),
 679  'code' => Array(
 680  'mode' => BBCODE_MODE_ENHANCED,
 681  'template' => "\n<div class=\"bbcode_code\">\n<div class=\"bbcode_code_head\">Code:</div>\n<div class=\"bbcode_code_body\" style=\"white-space:pre\">{\$_content/v}</div>\n</div>\n",
 682  'class' => 'code',
 683  'allow_in' => Array('listitem', 'block', 'columns'),
 684  'content' => BBCODE_VERBATIM,
 685  'before_tag' => "sns",
 686  'after_tag' => "sn",
 687  'before_endtag' => "sn",
 688  'after_endtag' => "sns",
 689  'plain_start' => "\n<b>Code:</b>\n",
 690  'plain_end' => "\n",
 691  ),
 692  'quote' => Array(
 693  'mode' => BBCODE_MODE_LIBRARY,
 694  'method' => "DoQuote",
 695  'allow_in' => Array('listitem', 'block', 'columns'),
 696  'before_tag' => "sns",
 697  'after_tag' => "sns",
 698  'before_endtag' => "sns",
 699  'after_endtag' => "sns",
 700  'plain_start' => "\n<b>Quote:</b>\n",
 701  'plain_end' => "\n",
 702  ),
 703  'list' => Array(
 704  'mode' => BBCODE_MODE_LIBRARY,
 705  'method' => 'DoList',
 706  'class' => 'list',
 707  'allow_in' => Array('listitem', 'block', 'columns'),
 708  'before_tag' => "sns",
 709  'after_tag' => "sns",
 710  'before_endtag' => "sns",
 711  'after_endtag' => "sns",
 712  'plain_start' => "\n",
 713  'plain_end' => "\n",
 714  ),
 715  '*' => Array(
 716  'simple_start' => "<li>",
 717  'simple_end' => "</li>\n",
 718  'class' => 'listitem',
 719  'allow_in' => Array('list'),
 720  'end_tag' => BBCODE_OPTIONAL,
 721  'before_tag' => "s",
 722  'after_tag' => "s",
 723  'before_endtag' => "sns",
 724  'after_endtag' => "sns",
 725  'plain_start' => "\n * ",
 726  'plain_end' => "\n",
 727  ),
 728  );
 729  function DoURL($bbcode, $action, $name, $default, $params, $content) {
 730  if ($action == BBCODE_CHECK) return true;
 731  $url = is_string($default) ? $default : $bbcode->UnHTMLEncode(strip_tags($content));
 732  if ($bbcode->IsValidURL($url)) {
 733  if ($bbcode->debug)
 734  print "ISVALIDURL<br />";
 735  if ($bbcode->url_targetable !== false && isset($params['target']))
 736  $target = " target=\"" . htmlspecialchars($params['target']) . "\"";
 737  else $target = "";
 738  if ($bbcode->url_target !== false)
 739  if (!($bbcode->url_targetable == 'override' && isset($params['target'])))
 740  $target = " target=\"" . htmlspecialchars($bbcode->url_target) . "\"";
 741  return '<a href="' . htmlspecialchars($url) . '" class="bbcode_url"' . $target . '>' . $content . '</a>';
 742  }
 743  else return htmlspecialchars($params['_tag']) . $content . htmlspecialchars($params['_endtag']);
 744  }
 745  function DoEmail($bbcode, $action, $name, $default, $params, $content) {
 746  if ($action == BBCODE_CHECK) return true;
 747  $email = is_string($default) ? $default : $bbcode->UnHTMLEncode(strip_tags($content));
 748  if ($bbcode->IsValidEmail($email))
 749  return '<a href="mailto:' . htmlspecialchars($email) . '" class="bbcode_email">' . $content . '</a>';
 750  else return htmlspecialchars($params['_tag']) . $content . htmlspecialchars($params['_endtag']);
 751  }
 752  function DoSize($bbcode, $action, $name, $default, $params, $content) {
 753  switch ($default) {
 754  case '0': $size = '.5em'; break;
 755  case '1': $size = '.67em'; break;
 756  case '2': $size = '.83em'; break;
 757  default:
 758  case '3': $size = '1.0em'; break;
 759  case '4': $size = '1.17em'; break;
 760  case '5': $size = '1.5em'; break;
 761  case '6': $size = '2.0em'; break;
 762  case '7': $size = '2.5em'; break;
 763  }
 764  return "<span style=\"font-size:$size\">$content</span>";
 765  }
 766  function DoFont($bbcode, $action, $name, $default, $params, $content) {
 767  $fonts = explode(",", $default);
 768  $result = "";
 769  $special_fonts = Array(
 770  'serif' => 'serif',
 771  'sans-serif' => 'sans-serif',
 772  'sans serif' => 'sans-serif',
 773  'sansserif' => 'sans-serif',
 774  'sans' => 'sans-serif',
 775  'cursive' => 'cursive',
 776  'fantasy' => 'fantasy',
 777  'monospace' => 'monospace',
 778  'mono' => 'monospace',
 779  );
 780  foreach ($fonts as $font) {
 781  $font = trim($font);
 782  if (isset($special_fonts[$font])) {
 783  if (strlen($result) > 0) $result .= ",";
 784  $result .= $special_fonts[$font];
 785  }
 786  else if (strlen($font) > 0) {
 787  if (strlen($result) > 0) $result .= ",";
 788  $result .= "'$font'";
 789  }
 790  }
 791  return "<span style=\"font-family:$result\">$content</span>";
 792  }
 793  function DoWiki($bbcode, $action, $name, $default, $params, $content) {
 794  $name = $bbcode->Wikify($default);
 795  if ($action == BBCODE_CHECK)
 796  return strlen($name) > 0;
 797  $title = trim(@$params['title']);
 798  if (strlen($title) <= 0) $title = trim($default);
 799  return "<a href=\"{$bbcode->wiki_url}$name\" class=\"bbcode_wiki\">"
 800  . htmlspecialchars($title) . "</a>";
 801  }
 802  function DoImage($bbcode, $action, $name, $default, $params, $content) {
 803  if ($action == BBCODE_CHECK) return true;
 804  $content = trim($bbcode->UnHTMLEncode(strip_tags($content)));
 805  if (preg_match("/\\.(?:gif|jpeg|jpg|jpe|png)$/", $content)) {
 806  if (preg_match("/^[a-zA-Z0-9_][^:]+$/", $content)) {
 807  if (!preg_match("/(?:\\/\\.\\.\\/)|(?:^\\.\\.\\/)|(?:^\\/)/", $content)) {
 808  $info = @getimagesize("{$bbcode->local_img_dir}/{$content}");
 809  if ($info[2] == IMAGETYPE_GIF || $info[2] == IMAGETYPE_JPEG || $info[2] == IMAGETYPE_PNG) {
 810  return "<img src=\""
 811  . htmlspecialchars("{$bbcode->local_img_url}/{$content}") . "\" alt=\""
 812  . htmlspecialchars(basename($content)) . "\" width=\""
 813  . htmlspecialchars($info[0]) . "\" height=\""
 814  . htmlspecialchars($info[1]) . "\" class=\"bbcode_img\" />";
 815  }
 816  }
 817  }
 818  else if ($bbcode->IsValidURL($content, false)) {
 819  return "<img src=\"" . htmlspecialchars($content) . "\" alt=\""
 820  . htmlspecialchars(basename($content)) . "\" class=\"bbcode_img\" />";
 821  }
 822  }
 823  return htmlspecialchars($params['_tag']) . htmlspecialchars($content) . htmlspecialchars($params['_endtag']);
 824  }
 825  function DoRule($bbcode, $action, $name, $default, $params, $content) {
 826  if ($action == BBCODE_CHECK) return true;
 827  else return $bbcode->rule_html;
 828  }
 829  function DoQuote($bbcode, $action, $name, $default, $params, $content) {
 830  if ($action == BBCODE_CHECK) return true;
 831  if (isset($params['name'])) {
 832  $title = htmlspecialchars(trim($params['name'])) . " wrote";
 833  if (isset($params['date']))
 834  $title .= " on " . htmlspecialchars(trim($params['date']));
 835  $title .= ":";
 836  if (isset($params['url'])) {
 837  $url = trim($params['url']);
 838  if ($bbcode->IsValidURL($url))
 839  $title = "<a href=\"" . htmlspecialchars($params['url']) . "\">" . $title . "</a>";
 840  }
 841  }
 842  else if (!is_string($default))
 843  $title = "Quote:";
 844  else $title = htmlspecialchars(trim($default)) . " wrote:";
 845  return "\n<div class=\"bbcode_quote\">\n<div class=\"bbcode_quote_head\">"
 846  . $title . "</div>\n<div class=\"bbcode_quote_body\">"
 847  . $content . "</div>\n</div>\n";
 848  }
 849  function DoList($bbcode, $action, $name, $default, $params, $content) {
 850  $list_styles = Array(
 851  '1' => 'decimal',
 852  '01' => 'decimal-leading-zero',
 853  'i' => 'lower-roman',
 854  'I' => 'upper-roman',
 855  'a' => 'lower-alpha',
 856  'A' => 'upper-alpha',
 857  );
 858  $ci_list_styles = Array(
 859  'circle' => 'circle',
 860  'disc' => 'disc',
 861  'square' => 'square',
 862  'greek' => 'lower-greek',
 863  'armenian' => 'armenian',
 864  'georgian' => 'georgian',
 865  );
 866  $ul_types = Array(
 867  'circle' => 'circle',
 868  'disc' => 'disc',
 869  'square' => 'square',
 870  );
 871  $default = trim($default);
 872  if ($action == BBCODE_CHECK) {
 873  if (!is_string($default) || strlen($default) == "") return true;
 874  else if (isset($list_styles[$default])) return true;
 875  else if (isset($ci_list_styles[strtolower($default)])) return true;
 876  else return false;
 877  }
 878  if (!is_string($default) || strlen($default) == "") {
 879  $elem = 'ul';
 880  $type = '';
 881  }
 882  else if ($default == '1') {
 883  $elem = 'ol';
 884  $type = '';
 885  }
 886  else if (isset($list_styles[$default])) {
 887  $elem = 'ol';
 888  $type = $list_styles[$default];
 889  }
 890  else {
 891  $default = strtolower($default);
 892  if (isset($ul_types[$default])) {
 893  $elem = 'ul';
 894  $type = $ul_types[$default];
 895  }
 896  else if (isset($ci_list_styles[$default])) {
 897  $elem = 'ol';
 898  $type = $ci_list_styles[$default];
 899  }
 900  }
 901  if (strlen($type))
 902  return "\n<$elem class=\"bbcode_list\" style=\"list-style-type:$type\">\n$content</$elem>\n";
 903  else return "\n<$elem class=\"bbcode_list\">\n$content</$elem>\n";
 904  }
 905  }
 906  
 907  class BBCodeEmailAddressValidator {
 908  function check_email_address($strEmailAddress) {
 909  if (preg_match('/[\x00-\x1F\x7F-\xFF]/', $strEmailAddress)) {
 910  return false;
 911  }
 912  $intAtSymbol = strrpos($strEmailAddress, '@');
 913  if ($intAtSymbol === false) {
 914  return false;
 915  }
 916  $arrEmailAddress[0] = substr($strEmailAddress, 0, $intAtSymbol);
 917  $arrEmailAddress[1] = substr($strEmailAddress, $intAtSymbol + 1);
 918  $arrTempAddress[0] = preg_replace('/"[^"]+"/'
 919  ,''
 920  ,$arrEmailAddress[0]);
 921  $arrTempAddress[1] = $arrEmailAddress[1];
 922  $strTempAddress = $arrTempAddress[0] . $arrTempAddress[1];
 923  if (strrpos($strTempAddress, '@') !== false) {
 924  return false;
 925  }
 926  if (!$this->check_local_portion($arrEmailAddress[0])) {
 927  return false;
 928  }
 929  if (!$this->check_domain_portion($arrEmailAddress[1])) {
 930  return false;
 931  }
 932  return true;
 933  }
 934  function check_local_portion($strLocalPortion) {
 935  if (!$this->check_text_length($strLocalPortion, 1, 64)) {
 936  return false;
 937  }
 938  $arrLocalPortion = explode('.', $strLocalPortion);
 939  for ($i = 0, $max = sizeof($arrLocalPortion); $i < $max; $i++) {
 940  if (!preg_match('.^('
 941  . '([A-Za-z0-9!#$%&\'*+/=?^_`{|}~-]'
 942  . '[A-Za-z0-9!#$%&\'*+/=?^_`{|}~-]{0,63})'
 943  .'|'
 944  . '("[^\\\"]{0,62}")'
 945  .')$.'
 946  ,$arrLocalPortion[$i])) {
 947  return false;
 948  }
 949  }
 950  return true;
 951  }
 952  function check_domain_portion($strDomainPortion) {
 953  if (!$this->check_text_length($strDomainPortion, 1, 255)) {
 954  return false;
 955  }
 956  if (preg_match('/^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])'
 957  .'(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}$/'
 958  ,$strDomainPortion) ||
 959  preg_match('/^\[(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])'
 960  .'(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}\]$/'
 961  ,$strDomainPortion)) {
 962  return true;
 963  } else {
 964  $arrDomainPortion = explode('.', $strDomainPortion);
 965  if (sizeof($arrDomainPortion) < 2) {
 966  return false;
 967  }
 968  for ($i = 0, $max = sizeof($arrDomainPortion); $i < $max; $i++) {
 969  if (!$this->check_text_length($arrDomainPortion[$i], 1, 63)) {
 970  return false;
 971  }
 972  if (!preg_match('/^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|'
 973  .'([A-Za-z0-9]+))$/', $arrDomainPortion[$i])) {
 974  return false;
 975  }
 976  }
 977  }
 978  return true;
 979  }
 980  function check_text_length($strText, $intMinimum, $intMaximum) {
 981  $intTextLength = strlen($strText);
 982  if (($intTextLength < $intMinimum) || ($intTextLength > $intMaximum)) {
 983  return false;
 984  } else {
 985  return true;
 986  }
 987  }
 988  }
 989  
 990  class BBCode {
 991  var $tag_rules;
 992  var $defaults;
 993  var $current_class;
 994  var $root_class;
 995  var $lost_start_tags;
 996  var $start_tags;
 997  var $allow_ampersand;
 998  var $tag_marker;
 999  var $ignore_newlines;
1000  var $plain_mode;
1001  var $detect_urls;
1002  var $url_pattern;
1003  var $output_limit;
1004  var $text_length;
1005  var $was_limited;
1006  var $limit_tail;
1007  var $limit_precision;
1008  var $smiley_dir;
1009  var $smiley_url;
1010  var $smileys;
1011  var $smiley_regex;
1012  var $enable_smileys;
1013  var $wiki_url;
1014  var $local_img_dir;
1015  var $local_img_url;
1016  var $url_targetable;
1017  var $url_target;
1018  var $rule_html;
1019  var $pre_trim;
1020  var $post_trim;
1021  var $debug;
1022  
1023  
1024  /* ADDED */
1025  // singleton instance
1026  private static $instance;
1027  
1028  // private constructor function
1029  // to prevent external instantiation
1030  private function __construct()
1031  {
1032      $this->defaults = new BBCodeLibrary;
1033      $this->tag_rules = $this->defaults->default_tag_rules;
1034      $this->smileys = $this->defaults->default_smileys;
1035      $this->enable_smileys = true;
1036      $this->smiley_regex = false;
1037      $this->smiley_dir = $this->GetDefaultSmileyDir();
1038      $this->smiley_url = $this->GetDefaultSmileyURL();
1039      $this->wiki_url = $this->GetDefaultWikiURL();
1040      $this->local_img_dir = $this->GetDefaultLocalImgDir();
1041      $this->local_img_url = $this->GetDefaultLocalImgURL();
1042      $this->rule_html = $this->GetDefaultRuleHTML();
1043      $this->pre_trim = "";
1044      $this->post_trim = "";
1045      $this->root_class = 'block';
1046      $this->lost_start_tags = Array();
1047      $this->start_tags = Array();
1048      $this->tag_marker = '[';
1049      $this->allow_ampsersand = false;
1050      $this->current_class = $this->root_class;
1051      $this->debug = false;
1052      $this->ignore_newlines = false;
1053      $this->output_limit = 0;
1054      $this->plain_mode = false;
1055      $this->was_limited = false;
1056      $this->limit_tail = "...";
1057      $this->limit_precision = 0.15;
1058      $this->detect_urls = false;
1059      $this->url_pattern = '<a href="{$url/h}">{$text/h}</a>';
1060      $this->url_targetable = false;
1061      $this->url_target = false;
1062  }
1063  
1064  // getInstance method
1065  public static function getInstance()
1066  {
1067      if(!self::$instance)
1068      {
1069        self::$instance = new self();
1070      }
1071  
1072      return self::$instance;
1073  }
1074  /* ADDED */
1075  
1076  
1077  function SetPreTrim($trim = "a") { $this->pre_trim = $trim; }
1078  function GetPreTrim() { return $this->pre_trim; }
1079  function SetPostTrim($trim = "a") { $this->post_trim = $trim; }
1080  function GetPostTrim() { return $this->post_trim; }
1081  function SetRoot($class = 'block') { $this->root_class = $class; }
1082  function SetRootInline() { $this->root_class = 'inline'; }
1083  function SetRootBlock() { $this->root_class = 'block'; }
1084  function GetRoot() { return $this->root_class; }
1085  function SetDebug($enable = true) { $this->debug = $enable; }
1086  function GetDebug() { return $this->debug; }
1087  function SetAllowAmpersand($enable = true) { $this->allow_ampersand = $enable; }
1088  function GetAllowAmpersand() { return $this->allow_ampersand; }
1089  function SetTagMarker($marker = '[') { $this->tag_marker = $marker; }
1090  function GetTagMarker() { return $this->tag_marker; }
1091  function SetIgnoreNewlines($ignore = true) { $this->ignore_newlines = $ignore; }
1092  function GetIgnoreNewlines() { return $this->ignore_newlines; }
1093  function SetLimit($limit = 0) { $this->output_limit = $limit; }
1094  function GetLimit() { return $this->output_limit; }
1095  function SetLimitTail($tail = "...") { $this->limit_tail = $tail; }
1096  function GetLimitTail() { return $this->limit_tail; }
1097  function SetLimitPrecision($prec = 0.15) { $this->limit_precision = $prec; }
1098  function GetLimitPrecision() { return $this->limit_precision; }
1099  function WasLimited() { return $this->was_limited; }
1100  function SetPlainMode($enable = true) { $this->plain_mode = $enable; }
1101  function GetPlainMode() { return $this->plain_mode; }
1102  function SetDetectURLs($enable = true) { $this->detect_urls = $enable; }
1103  function GetDetectURLs() { return $this->detect_urls; }
1104  function SetURLPattern($pattern) { $this->url_pattern = $pattern; }
1105  function GetURLPattern() { return $this->url_pattern; }
1106  function SetURLTargetable($enable) { $this->url_targetable = $enable; }
1107  function GetURLTargetable() { return $this->url_targetable; }
1108  function SetURLTarget($target) { $this->url_target = $target; }
1109  function GetURLTarget() { return $this->url_target; }
1110  function AddRule($name, $rule) { $this->tag_rules[$name] = $rule; }
1111  function RemoveRule($name) { unset($this->tag_rules[$name]); }
1112  function GetRule($name) { return isset($this->tag_rules[$name])
1113  ? $this->tag_rules[$name] : false; }
1114  function ClearRules() { $this->tag_rules = Array(); }
1115  function GetDefaultRule($name) { return isset($this->defaults->default_tag_rules[$name])
1116  ? $this->defaults->default_tag_rules[$name] : false; }
1117  function SetDefaultRule($name) { if (isset($this->defaults->default_tag_rules[$name]))
1118  $this->AddRule($name, $this->defaults->default_tag_rules[$name]);
1119  else $this->RemoveRule($name); }
1120  function GetDefaultRules() { return $this->defaults->default_tag_rules; }
1121  function SetDefaultRules() { $this->tag_rules = $this->defaults->default_tag_rules; }
1122  function SetWikiURL($url) { $this->wiki_url = $url; }
1123  function GetWikiURL($url) { return $this->wiki_url; }
1124  function GetDefaultWikiURL() { return '/?page='; }
1125  function SetLocalImgDir($path) { $this->local_img_dir = $path; }
1126  function GetLocalImgDir() { return $this->local_img_dir; }
1127  function GetDefaultLocalImgDir() { return "img"; }
1128  function SetLocalImgURL($path) { $this->local_img_url = $path; }
1129  function GetLocalImgURL() { return $this->local_img_url; }
1130  function GetDefaultLocalImgURL() { return "img"; }
1131  function SetRuleHTML($html) { $this->rule_html = $html; }
1132  function GetRuleHTML() { return $this->rule_html; }
1133  function GetDefaultRuleHTML() { return "\n<hr class=\"bbcode_rule\" />\n"; }
1134  function AddSmiley($code, $image) { $this->smileys[$code] = $image; $this->smiley_regex = false; }
1135  function RemoveSmiley($code) { unset($this->smileys[$code]); $this->smiley_regex = false; }
1136  function GetSmiley($code) { return isset($this->smileys[$code])
1137  ? $this->smileys[$code] : false; }
1138  function ClearSmileys() { $this->smileys = Array(); $this->smiley_regex = false; }
1139  function GetDefaultSmiley($code) { return isset($this->defaults->default_smileys[$code])
1140  ? $this->defaults->default_smileys[$code] : false; }
1141  function SetDefaultSmiley($code) { $this->smileys[$code] = @$this->defaults->default_smileys[$code];
1142  $this->smiley_regex = false; }
1143  function GetDefaultSmileys() { return $this->defaults->default_smileys; }
1144  function SetDefaultSmileys() { $this->smileys = $this->defaults->default_smileys;
1145  $this->smiley_regex = false; }
1146  function SetSmileyDir($path) { $this->smiley_dir = $path; }
1147  function GetSmileyDir() { return $this->smiley_dir; }
1148  function GetDefaultSmileyDir() { return "smileys"; }
1149  function SetSmileyURL($path) { $this->smiley_url = $path; }
1150  function GetSmileyURL() { return $this->smiley_url; }
1151  function GetDefaultSmileyURL() { return "smileys"; }
1152  function SetEnableSmileys($enable = true) { $this->enable_smileys = $enable; }
1153  function GetEnableSmileys() { return $this->enable_smileys; }
1154  function nl2br($string) {
1155  return preg_replace("/\\x0A|\\x0D|\\x0A\\x0D|\\x0D\\x0A/", "<br />\n", $string);
1156  }
1157  function UnHTMLEncode($string) {
1158  if (function_exists("html_entity_decode"))
1159  return html_entity_decode($string);
1160  $string = preg_replace_callback('~&#x([0-9a-f]+);~i', array( $this, '_UnHTMLEncode_chr_callback'), $string);
1161  $string = preg_replace_callback('~&#([0-9]+);~', array($this, '_UnHTMLEncode_chr_hexdec_callback'), $string);
1162  $trans_tbl = get_html_translation_table(HTML_ENTITIES);
1163  $trans_tbl = array_flip($trans_tbl);
1164  return strtr($string, $trans_tbl);
1165  }
1166  function _UnHTMLEncode_chr_callback($match) {
1167  return chr(hexdec($match[1]));
1168  }
1169  function _UnHTMLEncode_chr_hexdec_callback($match) {
1170  return chr(hexdec($match[1]));
1171  }
1172  function Wikify($string) {
1173  return rawurlencode(str_replace(" ", "_",
1174  trim(preg_replace("/[!?;@#\$%\\^&*<>=+`~\\x00-\\x20_-]+/", " ", $string))));
1175  }
1176  function IsValidURL($string, $email_too = true) {
1177  if (preg_match("/^
1178  (?:https?|ftp):\\/\\/
1179  (?:
1180  (?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+
1181  [a-zA-Z0-9]
1182  (?:[a-zA-Z0-9-]*[a-zA-Z0-9])?
1183  |
1184  \\[
1185  (?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}
1186  (?:
1187  25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-zA-Z0-9-]*[a-zA-Z0-9]:
1188  (?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21-\\x5A\\x53-\\x7F]
1189  |\\\\[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F])+
1190  )
1191  \\]
1192  )
1193  (?::[0-9]{1,5})?
1194  (?:[\\/\\?\\#][^\\n\\r]*)?
1195  $/Dx", $string)) return true;
1196  if (preg_match("/^[^:]+([\\/\\\\?#][^\\r\\n]*)?$/D", $string))
1197  return true;
1198  if ($email_too)
1199  if (substr($string, 0, 7) == "mailto:")
1200  return $this->IsValidEmail(substr($string, 7));
1201  return false;
1202  }
1203  function IsValidEmail($string) {
1204  $validator = new BBCodeEmailAddressValidator;
1205  return $validator->check_email_address($string);
1206  /*
1207  return preg_match("/^
1208  (?:
1209  [a-z0-9\\!\\#\\\$\\%\\&\\'\\*\\+\\/=\\?\\^_`\\{\\|\\}~-]+
1210  (?:\.[a-z0-9\\!\\#\\\$\\%\\&\\'\\*\\+\\/=\\?\\^_`\\{\\|\\}~-]+)*
1211  |
1212  \"(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F]
1213  |\\\\[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F])*\"
1214  )
1215  @
1216  (?:
1217  (?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+
1218  [a-z0-9]
1219  (?:[a-z0-9-]*[a-z0-9])?
1220  |
1221  \\[
1222  (?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}
1223  (?:
1224  25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:
1225  (?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21-\\x5A\\x53-\\x7F]
1226  |\\\\[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F])+
1227  )
1228  \\]
1229  )
1230  $/Dx", $string);
1231  */
1232  }
1233  function HTMLEncode($string) {
1234  if (!$this->allow_ampersand)
1235  return htmlspecialchars($string);
1236  else return str_replace(Array('<', '>', '"'),
1237  Array('&lt;', '&gt;', '&quot;'), $string);
1238  }
1239  function FixupOutput($string) {
1240  if (!$this->detect_urls) {
1241  $output = $this->Internal_ProcessSmileys($string);
1242  }
1243  else {
1244  $chunks = $this->Internal_AutoDetectURLs($string);
1245  $output = Array();
1246  if (count($chunks)) {
1247  $is_a_url = false;
1248  foreach ($chunks as $index => $chunk) {
1249  if (!$is_a_url) {
1250  $chunk = $this->Internal_ProcessSmileys($chunk);
1251  }
1252  $output[] = $chunk;
1253  $is_a_url = !$is_a_url;
1254  }
1255  }
1256  $output = implode("", $output);
1257  }
1258  return $output;
1259  }
1260  function Internal_ProcessSmileys($string) {
1261  if (!$this->enable_smileys || $this->plain_mode) {
1262  $output = $this->HTMLEncode($string);
1263  }
1264  else {
1265  if ($this->smiley_regex === false) {
1266  $this->Internal_RebuildSmileys();
1267  }
1268  $tokens = preg_split($this->smiley_regex, $string, -1, PREG_SPLIT_DELIM_CAPTURE);
1269  if (count($tokens) <= 1) {
1270  $output = $this->HTMLEncode($string);
1271  }
1272  else {
1273  $output = "";
1274  $is_a_smiley = false;
1275  foreach ($tokens as $token) {
1276  if (!$is_a_smiley) {
1277  $output .= $this->HTMLEncode($token);
1278  }
1279  else {
1280  if (isset($this->smiley_info[$token])) {
1281  $info = $this->smiley_info[$token];
1282  }
1283  else {
1284  $info = @getimagesize($this->smiley_dir . '/' . $this->smileys[$token]);
1285  $this->smiley_info[$token] = $info;
1286  }
1287  $alt = htmlspecialchars($token);
1288  $output .= "<img src=\"" . htmlspecialchars($this->smiley_url . '/' . $this->smileys[$token])
1289  . "\" width=\"{$info[0]}\" height=\"{$info[1]}\""
1290  . " alt=\"$alt\" title=\"$alt\" class=\"bbcode_smiley\" />";
1291  }
1292  $is_a_smiley = !$is_a_smiley;
1293  }
1294  }
1295  }
1296  return $output;
1297  }
1298  function Internal_RebuildSmileys() {
1299  $regex = Array("/(?<![\\w])(");
1300  $first = true;
1301  foreach ($this->smileys as $code => $filename) {
1302  if (!$first) $regex[] = "|";
1303  $regex[] = preg_quote("$code", '/');
1304  $first = false;
1305  }
1306  $regex[] = ")(?![\\w])/";
1307  $this->smiley_regex = implode("", $regex);
1308  }
1309  function Internal_AutoDetectURLs($string) {
1310  $output = preg_split("/( (?:
1311  (?:https?|ftp) : \\/*
1312  (?:
1313  (?: (?: [a-zA-Z0-9-]{2,} \\. )+
1314  (?: arpa | com | org | net | edu | gov | mil | int | [a-z]{2}
1315  | aero | biz | coop | info | museum | name | pro
1316  | example | invalid | localhost | test | local | onion | swift ) )
1317  | (?: [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} )
1318  | (?: [0-9A-Fa-f:]+ : [0-9A-Fa-f]{1,4} )
1319  )
1320  (?: : [0-9]+ )?
1321  (?! [a-zA-Z0-9.:-] )
1322  (?:
1323  \\/
1324  [^&?#\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]*
1325  )?
1326  (?:
1327  [?#]
1328  [^\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]+
1329  )?
1330  ) | (?:
1331  (?:
1332  (?: (?: [a-zA-Z0-9-]{2,} \\. )+
1333  (?: arpa | com | org | net | edu | gov | mil | int | [a-z]{2}
1334  | aero | biz | coop | info | museum | name | pro
1335  | example | invalid | localhost | test | local | onion | swift ) )
1336  | (?: [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} )
1337  )
1338  (?: : [0-9]+ )?
1339  (?! [a-zA-Z0-9.:-] )
1340  (?:
1341  \\/
1342  [^&?#\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]*
1343  )?
1344  (?:
1345  [?#]
1346  [^\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]+
1347  )?
1348  ) | (?:
1349  [a-zA-Z0-9._-]{2,} @
1350  (?:
1351  (?: (?: [a-zA-Z0-9-]{2,} \\. )+
1352  (?: arpa | com | org | net | edu | gov | mil | int | [a-z]{2}
1353  | aero | biz | coop | info | museum | name | pro
1354  | example | invalid | localhost | test | local | onion | swift ) )
1355  | (?: [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} )
1356  )
1357  ) )/Dx", $string, -1, PREG_SPLIT_DELIM_CAPTURE);
1358  if (count($output) > 1) {
1359  $is_a_url = false;
1360  foreach ($output as $index => $token) {
1361  if ($is_a_url) {
1362  if (preg_match("/^[a-zA-Z0-9._-]{2,}@/", $token)) {
1363  $url = "mailto:" . $token;
1364  }
1365  else if (preg_match("/^(https?:|ftp:)\\/*([^\\/&?#]+)\\/*(.*)\$/", $token, $matches)) {
1366  $url = $matches[1] . '/' . '/' . $matches[2] . "/" . $matches[3];
1367  }
1368  else {
1369  preg_match("/^([^\\/&?#]+)\\/*(.*)\$/", $token, $matches);
1370  $url = "http:/" . "/" . $matches[1] . "/" . $matches[2];
1371  }
1372  $params = @parse_url($url);
1373  if (!is_array($params)) $params = Array();
1374  $params['url'] = $url;
1375  $params['link'] = $url;
1376  $params['text'] = $token;
1377  $output[$index] = $this->FillTemplate($this->url_pattern, $params);
1378  }
1379  $is_a_url = !$is_a_url;
1380  }
1381  }
1382  return $output;
1383  }
1384  function FillTemplate($template, $insert_array, $default_array = Array()) {
1385  $pieces = preg_split('/(\{\$[a-zA-Z0-9_.:\/-]+\})/', $template,
1386  -1, PREG_SPLIT_DELIM_CAPTURE);
1387  if (count($pieces) <= 1)
1388  return $template;
1389  $result = Array();
1390  $is_an_insert = false;
1391  foreach ($pieces as $piece) {
1392  if (!$is_an_insert) {
1393  $result[] = $piece;
1394  }
1395  else if (!preg_match('/\{\$([a-zA-Z0-9_:-]+)((?:\\.[a-zA-Z0-9_:-]+)*)(?:\/([a-zA-Z0-9_:-]+))?\}/', $piece, $matches)) {
1396  $result[] = $piece;
1397  }
1398  else {
1399  if (isset($insert_array[$matches[1]]))
1400  $value = @$insert_array[$matches[1]];
1401  else $value = @$default_array[$matches[1]];
1402  if (strlen(@$matches[2])) {
1403  foreach (explode(".", substr($matches[2], 1)) as $index) {
1404  if (is_array($value))
1405  $value = @$value[$index];
1406  else if (is_object($value)) {
1407  $value = (array)$value;
1408  $value = @$value[$index];
1409  }
1410  else $value = "";
1411  }
1412  }
1413  switch (gettype($value)) {
1414  case 'boolean': $value = $value ? "true" : "false"; break;
1415  case 'integer': $value = (string)$value; break;
1416  case 'double': $value = (string)$value; break;
1417  case 'string': break;
1418  default: $value = ""; break;
1419  }
1420  if (strlen(@$matches[3]))
1421  $flags = array_flip(str_split($matches[3]));
1422  else $flags = Array();
1423  if (!isset($flags['v'])) {
1424  if (isset($flags['w']))
1425  $value = preg_replace("/[\\x00-\\x09\\x0B-\x0C\x0E-\\x20]+/", " ", $value);
1426  if (isset($flags['t'])) $value = trim($value);
1427  if (isset($flags['b'])) $value = basename($value);
1428  if (isset($flags['e'])) $value = $this->HTMLEncode($value);
1429  else if (isset($flags['k'])) $value = $this->Wikify($value);
1430  else if (isset($flags['h'])) $value = htmlspecialchars($value);
1431  else if (isset($flags['u'])) $value = urlencode($value);
1432  if (isset($flags['n'])) $value = $this->nl2br($value);
1433  }
1434  $result[] = $value;
1435  }
1436  $is_an_insert = !$is_an_insert;
1437  }
1438  return implode("", $result);
1439  }
1440  function Internal_CollectText($array, $start = 0) {
1441  ob_start();
1442  for ($start = intval($start), $end = count($array); $start < $end; $start++)
1443  print $array[$start][BBCODE_STACK_TEXT];
1444  $output = ob_get_contents();
1445  ob_end_clean();
1446  return $output;
1447  }
1448  function Internal_CollectTextReverse($array, $start = 0, $end = 0) {
1449  ob_start();
1450  for ($start = intval($start); $start >= $end; $start--)
1451  print $array[$start][BBCODE_STACK_TEXT];
1452  $output = ob_get_contents();
1453  ob_end_clean();
1454  return $output;
1455  }
1456  function Internal_GenerateOutput($pos) {
1457  $output = Array();
1458  while (count($this->stack) > $pos) {
1459  $token = array_pop($this->stack);
1460  if ($token[BBCODE_STACK_TOKEN] != BBCODE_TAG) {
1461  $output[] = $token;
1462  }
1463  else {
1464  $name = @$token[BBCODE_STACK_TAG]['_name'];
1465  $rule = @$this->tag_rules[$name];
1466  $end_tag = @$rule['end_tag'];
1467  if (!isset($rule['end_tag'])) $end_tag = BBCODE_REQUIRED;
1468  else $end_tag = $rule['end_tag'];
1469  array_pop($this->start_tags[$name]);
1470  if ($end_tag == BBCODE_PROHIBIT) {
1471  $output[] = Array(
1472  BBCODE_STACK_TOKEN => BBCODE_TEXT,
1473  BBCODE_STACK_TAG => false,
1474  BBCODE_STACK_TEXT => $token[BBCODE_STACK_TEXT],
1475  BBCODE_STACK_CLASS => $this->current_class,
1476  );
1477  }
1478  else {
1479  if ($end_tag == BBCODE_REQUIRED)
1480  @$this->lost_start_tags[$name] += 1;
1481  $end = $this->Internal_CleanupWSByIteratingPointer(@$rule['before_endtag'], 0, $output);
1482  $this->Internal_CleanupWSByPoppingStack(@$rule['after_tag'], $output);
1483  $tag_body = $this->Internal_CollectTextReverse($output, count($output)-1, $end);
1484  $this->Internal_CleanupWSByPoppingStack(@$rule['before_tag'], $this->stack);
1485  @$token[BBCODE_STACK_TAG]=@$this->Internal_UpdateParamsForMissingEndTag(@$token[BBCODE_STACK_TAG]);
1486  $tag_output = $this->DoTag(BBCODE_OUTPUT, $name,
1487  @$token[BBCODE_STACK_TAG]['_default'], @$token[BBCODE_STACK_TAG], $tag_body);
1488  $output = Array(Array(
1489  BBCODE_STACK_TOKEN => BBCODE_TEXT,
1490  BBCODE_STACK_TAG => false,
1491  BBCODE_STACK_TEXT => $tag_output,
1492  BBCODE_STACK_CLASS => $this->current_class
1493  ));
1494  }
1495  }
1496  }
1497  $this->Internal_ComputeCurrentClass();
1498  return $output;
1499  }
1500  function Internal_RewindToClass($class_list) {
1501  $pos = count($this->stack) - 1;
1502  while ($pos >= 0 && !in_array($this->stack[$pos][BBCODE_STACK_CLASS], $class_list))
1503  $pos--;
1504  if ($pos < 0) {
1505  if (!in_array($this->root_class, $class_list))
1506  return false;
1507  }
1508  $output = $this->Internal_GenerateOutput($pos+1);
1509  while (count($output)) {
1510  $token = array_pop($output);
1511  $token[BBCODE_STACK_CLASS] = $this->current_class;
1512  $this->stack[] = $token;
1513  }
1514  return true;
1515  }
1516  function Internal_FinishTag($tag_name) {
1517  if (strlen($tag_name) <= 0)
1518  return false;
1519  if (isset($this->start_tags[$tag_name])
1520  && count($this->start_tags[$tag_name]))
1521  $pos = array_pop($this->start_tags[$tag_name]);
1522  else $pos = -1;
1523  if ($pos < 0) return false;
1524  $newpos = $this->Internal_CleanupWSByIteratingPointer(@$this->tag_rules[$tag_name]['after_tag'],
1525  $pos+1, $this->stack);
1526  $delta = $newpos - ($pos+1);
1527  $output = $this->Internal_GenerateOutput($newpos);
1528  $newend = $this->Internal_CleanupWSByIteratingPointer(@$this->tag_rules[$tag_name]['before_endtag'],
1529  0, $output);
1530  $output = $this->Internal_CollectTextReverse($output, count($output) - 1, $newend);
1531  while ($delta-- > 0)
1532  array_pop($this->stack);
1533  $this->Internal_ComputeCurrentClass();
1534  return $output;
1535  }
1536  function Internal_ComputeCurrentClass() {
1537  if (count($this->stack) > 0)
1538  $this->current_class = $this->stack[count($this->stack)-1][BBCODE_STACK_CLASS];
1539  else $this->current_class = $this->root_class;
1540  }
1541  function Internal_DumpStack($array = false, $raw = false) {
1542  if (!$raw) $string = "<span style='color: #00C;'>";
1543  else $string = "";
1544  if ($array === false)
1545  $array = $this->stack;
1546  foreach ($array as $item) {
1547  switch (@$item[BBCODE_STACK_TOKEN]) {
1548  case BBCODE_TEXT:
1549  $string .= "\"" . htmlspecialchars(@$item[BBCODE_STACK_TEXT]) . "\" ";
1550  break;
1551  case BBCODE_WS:
1552  $string .= "WS ";
1553  break;
1554  case BBCODE_NL:
1555  $string .= "NL ";
1556  break;
1557  case BBCODE_TAG:
1558  $string .= "[" . htmlspecialchars(@$item[BBCODE_STACK_TAG]['_name']) . "] ";
1559  break;
1560  default:
1561  $string .= "unknown ";
1562  break;
1563  }
1564  }
1565  if (!$raw) $string .= "</span>";
1566  return $string;
1567  }
1568  function Internal_CleanupWSByPoppingStack($pattern, &$array) {
1569  if (strlen($pattern) <= 0) return;
1570  $oldlen = count($array);
1571  foreach (str_split($pattern) as $char) {
1572  switch ($char) {
1573  case 's':
1574  while (count($array) > 0 && $array[count($array)-1][BBCODE_STACK_TOKEN] == BBCODE_WS)
1575  array_pop($array);
1576  break;
1577  case 'n':
1578  if (count($array) > 0 && $array[count($array)-1][BBCODE_STACK_TOKEN] == BBCODE_NL)
1579  array_pop($array);
1580  break;
1581  case 'a':
1582  while (count($array) > 0
1583  && (($token = $array[count($array)-1][BBCODE_STACK_TOKEN]) == BBCODE_WS
1584  || $token == BBCODE_NL))
1585  array_pop($array);
1586  break;
1587  }
1588  }
1589  if (count($array) != $oldlen) {
1590  $this->Internal_ComputeCurrentClass();
1591  }
1592  }
1593  function Internal_CleanupWSByEatingInput($pattern) {
1594  if (strlen($pattern) <= 0) return;
1595  foreach (str_split($pattern) as $char) {
1596  switch ($char) {
1597  case 's':
1598  $token_type = $this->lexer->NextToken();
1599  while ($token_type == BBCODE_WS) {
1600  $token_type = $this->lexer->NextToken();
1601  }
1602  $this->lexer->UngetToken();
1603  break;
1604  case 'n':
1605  $token_type = $this->lexer->NextToken();
1606  if ($token_type != BBCODE_NL)
1607  $this->lexer->UngetToken();
1608  break;
1609  case 'a':
1610  $token_type = $this->lexer->NextToken();
1611  while ($token_type == BBCODE_WS || $token_type == BBCODE_NL) {
1612  $token_type = $this->lexer->NextToken();
1613  }
1614  $this->lexer->UngetToken();
1615  break;
1616  }
1617  }
1618  }
1619  function Internal_CleanupWSByIteratingPointer($pattern, $pos, $array) {
1620  if (strlen($pattern) <= 0) return $pos;
1621  foreach (str_split($pattern) as $char) {
1622  switch ($char) {
1623  case 's':
1624  while ($pos < count($array) && $array[$pos][BBCODE_STACK_TOKEN] == BBCODE_WS)
1625  $pos++;
1626  break;
1627  case 'n':
1628  if ($pos < count($array) && $array[$pos][BBCODE_STACK_TOKEN] == BBCODE_NL)
1629  $pos++;
1630  break;
1631  case 'a':
1632  while ($pos < count($array)
1633  && (($token = $array[$pos][BBCODE_STACK_TOKEN]) == BBCODE_WS || $token == BBCODE_NL))
1634  $pos++;
1635  break;
1636  }
1637  }
1638  return $pos;
1639  }
1640  function Internal_LimitText($string, $limit) {
1641  $chunks = preg_split("/([\\x00-\\x20]+)/", $string, -1, PREG_SPLIT_DELIM_CAPTURE);
1642  $output = "";
1643  foreach ($chunks as $chunk) {
1644  if (strlen($output) + strlen($chunk) > $limit)
1645  break;
1646  $output .= $chunk;
1647  }
1648  $output = rtrim($output);
1649  return $output;
1650  }
1651  function Internal_DoLimit() {
1652  $this->Internal_CleanupWSByPoppingStack("a", $this->stack);
1653  if (strlen($this->limit_tail) > 0) {
1654  $this->stack[] = Array(
1655  BBCODE_STACK_TOKEN => BBCODE_TEXT,
1656  BBCODE_STACK_TEXT => $this->limit_tail,
1657  BBCODE_STACK_TAG => false,
1658  BBCODE_STACK_CLASS => $this->current_class,
1659  );
1660  }
1661  $this->was_limited = true;
1662  }
1663  function DoTag($action, $tag_name, $default_value, $params, $contents) {
1664  $tag_rule = @$this->tag_rules[$tag_name];
1665  switch ($action) {
1666  case BBCODE_CHECK:
1667  if (isset($tag_rule['allow'])) {
1668  foreach ($tag_rule['allow'] as $param => $pattern) {
1669  if ($param == '_content') $value = $contents;
1670  else if ($param == '_defaultcontent') {
1671  if (strlen($default_value))
1672  $value = $default_value;
1673  else $value = $contents;
1674  }
1675  else {
1676  if (isset($params[$param]))
1677  $value = $params[$param];
1678  else $value = @$tag_rule['default'][$param];
1679  }
1680  if (!preg_match($pattern, $value)) {
1681  return false;
1682  }
1683  }
1684  return true;
1685  }
1686  switch (@$tag_rule['mode']) {
1687  default:
1688  case BBCODE_MODE_SIMPLE:
1689  $result = true;
1690  break;
1691  case BBCODE_MODE_ENHANCED:
1692  $result = true;
1693  break;
1694  case BBCODE_MODE_INTERNAL:
1695  $result = @call_user_func(Array($this, @$tag_rule['method']), BBCODE_CHECK,
1696  $tag_name, $default_value, $params, $contents);
1697  break;
1698  case BBCODE_MODE_LIBRARY:
1699  $result = @call_user_func(Array($this->defaults, @$tag_rule['method']), $this, BBCODE_CHECK,
1700  $tag_name, $default_value, $params, $contents);
1701  break;
1702  case BBCODE_MODE_CALLBACK:
1703  $result = @call_user_func(@$tag_rule['method'], $this, BBCODE_CHECK,
1704  $tag_name, $default_value, $params, $contents);
1705  break;
1706  }
1707  return $result;
1708  case BBCODE_OUTPUT:
1709  if ($this->plain_mode) {
1710  if (!isset($tag_rule['plain_content']))
1711  $plain_content = Array('_content');
1712  else $plain_content = $tag_rule['plain_content'];
1713  $result = $possible_content = "";
1714  foreach ($plain_content as $possible_content) {
1715  if ($possible_content == '_content'
1716  && strlen($contents) > 0) {
1717  $result = $contents;
1718  break;
1719  }
1720  if (isset($params[$possible_content])
1721  && strlen($params[$possible_content]) > 0) {
1722  $result = htmlspecialchars($params[$possible_content]);
1723  break;
1724  }
1725  }
1726  $start = @$tag_rule['plain_start'];
1727  $end = @$tag_rule['plain_end'];
1728  if (isset($tag_rule['plain_link'])) {
1729  $link = $possible_content = "";
1730  foreach ($tag_rule['plain_link'] as $possible_content) {
1731  if ($possible_content == '_content'
1732  && strlen($contents) > 0) {
1733  $link = $this->UnHTMLEncode(strip_tags($contents));
1734  break;
1735  }
1736  if (isset($params[$possible_content])
1737  && strlen($params[$possible_content]) > 0) {
1738  $link = $params[$possible_content];
1739  break;
1740  }
1741  }
1742  $params = @parse_url($link);
1743  if (!is_array($params)) $params = Array();
1744  $params['link'] = $link;
1745  $params['url'] = $link;
1746  $start = $this->FillTemplate($start, $params);
1747  $end = $this->FillTemplate($end, $params);
1748  }
1749  return $start . $result . $end;
1750  }
1751  switch (@$tag_rule['mode']) {
1752  default:
1753  case BBCODE_MODE_SIMPLE:
1754  $result = @$tag_rule['simple_start'] . $contents . @$tag_rule['simple_end'];
1755  break;
1756  case BBCODE_MODE_ENHANCED:
1757  $result = $this->Internal_DoEnhancedTag($tag_rule, $params, $contents);
1758  break;
1759  case BBCODE_MODE_INTERNAL:
1760  $result = @call_user_func(Array($this, @$tag_rule['method']), BBCODE_OUTPUT,
1761  $tag_name, $default_value, $params, $contents);
1762  break;
1763  case BBCODE_MODE_LIBRARY:
1764  $result = @call_user_func(Array($this->defaults, @$tag_rule['method']), $this, BBCODE_OUTPUT,
1765  $tag_name, $default_value, $params, $contents);
1766  break;
1767  case BBCODE_MODE_CALLBACK:
1768  $result = @call_user_func(@$tag_rule['method'], $this, BBCODE_OUTPUT,
1769  $tag_name, $default_value, $params, $contents);
1770  break;
1771  }
1772  return $result;
1773  default:
1774  return false;
1775  }
1776  }
1777  function Internal_DoEnhancedTag($tag_rule, $params, $contents) {
1778  $params['_content'] = $contents;
1779  $params['_defaultcontent'] = strlen(@$params['_default']) ? $params['_default'] : $contents;
1780  return $this->FillTemplate(@$tag_rule['template'], $params, @$tag_rule['default']);
1781  }
1782  function Internal_UpdateParamsForMissingEndTag($params) {
1783  switch ($this->tag_marker) {
1784  case '[': $tail_marker = ']'; break;
1785  case '<': $tail_marker = '>'; break;
1786  case '{': $tail_marker = '}'; break;
1787  case '(': $tail_marker = ')'; break;
1788  default: $tail_marker = $this->tag_marker; break;
1789  }
1790  $params['_endtag'] = $this->tag_marker . '/' . $params['_name'] . $tail_marker;
1791  return $params;
1792  }
1793  function Internal_ProcessIsolatedTag($tag_name, $tag_params, $tag_rule) {
1794  if (!$this->DoTag(BBCODE_CHECK, $tag_name, @$tag_params['_default'], $tag_params, "")) {
1795  $this->stack[] = Array(
1796  BBCODE_STACK_TOKEN => BBCODE_TEXT,
1797  BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text),
1798  BBCODE_STACK_TAG => false,
1799  BBCODE_STACK_CLASS => $this->current_class,
1800  );
1801  return;
1802  }
1803  $this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_tag'], $this->stack);
1804  $output = $this->DoTag(BBCODE_OUTPUT, $tag_name, @$tag_params['_default'], $tag_params, "");
1805  $this->Internal_CleanupWSByEatingInput(@$tag_rule['after_tag']);
1806  $this->stack[] = Array(
1807  BBCODE_STACK_TOKEN => BBCODE_TEXT,
1808  BBCODE_STACK_TEXT => $output,
1809  BBCODE_STACK_TAG => false,
1810  BBCODE_STACK_CLASS => $this->current_class,
1811  );
1812  }
1813  function Internal_ProcessVerbatimTag($tag_name, $tag_params, $tag_rule) {
1814  $state = $this->lexer->SaveState();
1815  $end_tag = $this->lexer->tagmarker . "/" . $tag_name . $this->lexer->end_tagmarker;
1816  $start = count($this->stack);
1817  $this->lexer->verbatim = true;
1818  while (($token_type = $this->lexer->NextToken()) != BBCODE_EOI) {
1819  if ($this->lexer->text == $end_tag) {
1820  $end_tag_params = $this->lexer->tag;
1821  break;
1822  }
1823  if ($this->output_limit > 0
1824  && $this->text_length + strlen($this->lexer->text) >= $this->output_limit) {
1825  $text = $this->Internal_LimitText($this->lexer->text,
1826  $this->output_limit - $this->text_length);
1827  if (strlen($text) > 0) {
1828  $this->text_length += strlen($text);
1829  $this->stack[] = Array(
1830  BBCODE_STACK_TOKEN => BBCODE_TEXT,
1831  BBCODE_STACK_TEXT => $this->FixupOutput($text),
1832  BBCODE_STACK_TAG => false,
1833  BBCODE_STACK_CLASS => $this->current_class,
1834  );
1835  }
1836  $this->Internal_DoLimit();
1837  break;
1838  }
1839  $this->text_length += strlen($this->lexer->text);
1840  $this->stack[] = Array(
1841  BBCODE_STACK_TOKEN => $token_type,
1842  BBCODE_STACK_TEXT => htmlspecialchars($this->lexer->text),
1843  BBCODE_STACK_TAG => $this->lexer->tag,
1844  BBCODE_STACK_CLASS => $this->current_class,
1845  );
1846  }
1847  $this->lexer->verbatim = false;
1848  if ($token_type == BBCODE_EOI) {
1849  $this->lexer->RestoreState($state);
1850  $this->stack[] = Array(
1851  BBCODE_STACK_TOKEN => BBCODE_TEXT,
1852  BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text),
1853  BBCODE_STACK_TAG => false,
1854  BBCODE_STACK_CLASS => $this->current_class,
1855  );
1856  return;
1857  }
1858  $newstart = $this->Internal_CleanupWSByIteratingPointer(@$tag_rule['after_tag'], $start, $this->stack);
1859  $this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_endtag'], $this->stack);
1860  $this->Internal_CleanupWSByEatingInput(@$tag_rule['after_endtag']);
1861  $content = $this->Internal_CollectText($this->stack, $newstart);
1862  array_splice($this->stack, $start);
1863  $this->Internal_ComputeCurrentClass();
1864  $this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_tag'], $this->stack);
1865  $tag_params['_endtag'] = $end_tag_params['_tag'];
1866  $tag_params['_hasend'] = true;
1867  $output = $this->DoTag(BBCODE_OUTPUT, $tag_name,
1868  @$tag_params['_default'], $tag_params, $content);
1869  $this->stack[] = Array(
1870  BBCODE_STACK_TOKEN => BBCODE_TEXT,
1871  BBCODE_STACK_TEXT => $output,
1872  BBCODE_STACK_TAG => false,
1873  BBCODE_STACK_CLASS => $this->current_class,
1874  );
1875  }
1876  function Internal_ParseStartTagToken() {
1877  $tag_params = $this->lexer->tag;
1878  $tag_name = @$tag_params['_name'];
1879  if (!isset($this->tag_rules[$tag_name])) {
1880  $this->stack[] = Array(
1881  BBCODE_STACK_TOKEN => BBCODE_TEXT,
1882  BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text),
1883  BBCODE_STACK_TAG => false,
1884  BBCODE_STACK_CLASS => $this->current_class,
1885  );
1886  return;
1887  }
1888  $tag_rule = $this->tag_rules[$tag_name];
1889  $allow_in = is_array($tag_rule['allow_in'])
1890  ? $tag_rule['allow_in'] : Array($this->root_class);
1891  if (!in_array($this->current_class, $allow_in)) {
1892  if (!$this->Internal_RewindToClass($allow_in)) {
1893  $this->stack[] = Array(
1894  BBCODE_STACK_TOKEN => BBCODE_TEXT,
1895  BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text),
1896  BBCODE_STACK_TAG => false,
1897  BBCODE_STACK_CLASS => $this->current_class,
1898  );
1899  return;
1900  }
1901  }
1902  $end_tag = isset($tag_rule['end_tag']) ? $tag_rule['end_tag'] : BBCODE_REQUIRED;
1903  if ($end_tag == BBCODE_PROHIBIT) {
1904  $this->Internal_ProcessIsolatedTag($tag_name, $tag_params, $tag_rule);
1905  return;
1906  }
1907  if (!$this->DoTag(BBCODE_CHECK, $tag_name, @$tag_params['_default'], $tag_params, "")) {
1908  $this->stack[] = Array(
1909  BBCODE_STACK_TOKEN => BBCODE_TEXT,
1910  BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text),
1911  BBCODE_STACK_TAG => false,
1912  BBCODE_STACK_CLASS => $this->current_class,
1913  );
1914  return;
1915  }
1916  if (@$tag_rule['content'] == BBCODE_VERBATIM) {
1917  $this->Internal_ProcessVerbatimTag($tag_name, $tag_params, $tag_rule);
1918  return;
1919  }
1920  if (isset($tag_rule['class']))
1921  $newclass = $tag_rule['class'];
1922  else $newclass = $this->root_class;
1923  $this->stack[] = Array(
1924  BBCODE_STACK_TOKEN => $this->lexer->token,
1925  BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text),
1926  BBCODE_STACK_TAG => $this->lexer->tag,
1927  BBCODE_STACK_CLASS => ($this->current_class = $newclass),
1928  );
1929  if (!isset($this->start_tags[$tag_name]))
1930  $this->start_tags[$tag_name] = Array(count($this->stack)-1);
1931  else $this->start_tags[$tag_name][] = count($this->stack)-1;
1932  }
1933  function Internal_ParseEndTagToken() {
1934  $tag_params = $this->lexer->tag;
1935  $tag_name = @$tag_params['_name'];
1936  $contents = $this->Internal_FinishTag($tag_name);
1937  if ($contents === false) {
1938  if (@$this->lost_start_tags[$tag_name] > 0) {
1939  $this->lost_start_tags[$tag_name]--;
1940  }
1941  else {
1942  $this->stack[] = Array(
1943  BBCODE_STACK_TOKEN => BBCODE_TEXT,
1944  BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text),
1945  BBCODE_STACK_TAG => false,
1946  BBCODE_STACK_CLASS => $this->current_class,
1947  );
1948  }
1949  return;
1950  }
1951  $start_tag_node = array_pop($this->stack);
1952  $start_tag_params = $start_tag_node[BBCODE_STACK_TAG];
1953  $this->Internal_ComputeCurrentClass();
1954  $this->Internal_CleanupWSByPoppingStack(@$this->tag_rules[$tag_name]['before_tag'], $this->stack);
1955  $start_tag_params['_endtag'] = $tag_params['_tag'];
1956  $start_tag_params['_hasend'] = true;
1957  $output = $this->DoTag(BBCODE_OUTPUT, $tag_name, @$start_tag_params['_default'],
1958  $start_tag_params, $contents);
1959  $this->Internal_CleanupWSByEatingInput(@$this->tag_rules[$tag_name]['after_endtag']);
1960  $this->stack[] = Array(
1961  BBCODE_STACK_TOKEN => BBCODE_TEXT,
1962  BBCODE_STACK_TEXT => $output,
1963  BBCODE_STACK_TAG => false,
1964  BBCODE_STACK_CLASS => $this->current_class,
1965  );
1966  }
1967  function Parse($string) {
1968  $this->lexer = new BBCodeLexer($string, $this->tag_marker);
1969  $this->lexer->debug = $this->debug;
1970  $old_output_limit = $this->output_limit;
1971  if ($this->output_limit > 0) {
1972  if (strlen($string) < $this->output_limit) {
1973  $this->output_limit = 0;
1974  }
1975  else if ($this->limit_precision > 0) {
1976  $guess_length = $this->lexer->GuessTextLength();
1977  if ($guess_length < $this->output_limit * ($this->limit_precision + 1.0)) {
1978  $this->output_limit = 0;
1979  }
1980  else {
1981  }
1982  }
1983  }
1984  $this->stack = Array();
1985  $this->start_tags = Array();
1986  $this->lost_start_tags = Array();
1987  $this->text_length = 0;
1988  $this->was_limited = false;
1989  if (strlen($this->pre_trim) > 0)
1990  $this->Internal_CleanupWSByEatingInput($this->pre_trim);
1991  $newline = $this->plain_mode ? "\n" : "<br />\n";
1992  while (true) {
1993  if (($token_type = $this->lexer->NextToken()) == BBCODE_EOI) {
1994  break;
1995  }
1996  switch ($token_type) {
1997  case BBCODE_TEXT:
1998  if ($this->output_limit > 0
1999  && $this->text_length + strlen($this->lexer->text) >= $this->output_limit) {
2000  $text = $this->Internal_LimitText($this->lexer->text,
2001  $this->output_limit - $this->text_length);
2002  if (strlen($text) > 0) {
2003  $this->text_length += strlen($text);
2004  $this->stack[] = Array(
2005  BBCODE_STACK_TOKEN => BBCODE_TEXT,
2006  BBCODE_STACK_TEXT => $this->FixupOutput($text),
2007  BBCODE_STACK_TAG => false,
2008  BBCODE_STACK_CLASS => $this->current_class,
2009  );
2010  }
2011  $this->Internal_DoLimit();
2012  break 2;
2013  }
2014  $this->text_length += strlen($this->lexer->text);
2015  $this->stack[] = Array(
2016  BBCODE_STACK_TOKEN => BBCODE_TEXT,
2017  BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text),
2018  BBCODE_STACK_TAG => false,
2019  BBCODE_STACK_CLASS => $this->current_class,
2020  );
2021  break;
2022  case BBCODE_WS:
2023  if ($this->output_limit > 0
2024  && $this->text_length + strlen($this->lexer->text) >= $this->output_limit) {
2025  $this->Internal_DoLimit();
2026  break 2;
2027  }
2028  $this->text_length += strlen($this->lexer->text);
2029  $this->stack[] = Array(
2030  BBCODE_STACK_TOKEN => BBCODE_WS,
2031  BBCODE_STACK_TEXT => $this->lexer->text,
2032  BBCODE_STACK_TAG => false,
2033  BBCODE_STACK_CLASS => $this->current_class,
2034  );
2035  break;
2036  case BBCODE_NL:
2037  if ($this->ignore_newlines) {
2038  if ($this->output_limit > 0
2039  && $this->text_length + 1 >= $this->output_limit) {
2040  $this->Internal_DoLimit();
2041  break 2;
2042  }
2043  $this->text_length += 1;
2044  $this->stack[] = Array(
2045  BBCODE_STACK_TOKEN => BBCODE_WS,
2046  BBCODE_STACK_TEXT => "\n",
2047  BBCODE_STACK_TAG => false,
2048  BBCODE_STACK_CLASS => $this->current_class,
2049  );
2050  }
2051  else {
2052  $this->Internal_CleanupWSByPoppingStack("s", $this->stack);
2053  if ($this->output_limit > 0
2054  && $this->text_length + 1 >= $this->output_limit) {
2055  $this->Internal_DoLimit();
2056  break 2;
2057  }
2058  $this->text_length += 1;
2059  $this->stack[] = Array(
2060  BBCODE_STACK_TOKEN => BBCODE_NL,
2061  BBCODE_STACK_TEXT => $newline,
2062  BBCODE_STACK_TAG => false,
2063  BBCODE_STACK_CLASS => $this->current_class,
2064  );
2065  $this->Internal_CleanupWSByEatingInput("s");
2066  }
2067  break;
2068  case BBCODE_TAG:
2069  $this->Internal_ParseStartTagToken();
2070  break;
2071  case BBCODE_ENDTAG:
2072  $this->Internal_ParseEndTagToken();
2073  break;
2074  default:
2075  break;
2076  }
2077  }
2078  if (strlen($this->post_trim) > 0)
2079  $this->Internal_CleanupWSByPoppingStack($this->post_trim, $this->stack);
2080  $result = $this->Internal_GenerateOutput(0);
2081  $result = $this->Internal_CollectTextReverse($result, count($result) - 1);
2082  $this->output_limit = $old_output_limit;
2083  if ($this->plain_mode) {
2084  $result = preg_replace("/[\\x00-\\x09\\x0B-\\x20]+/", " ", $result);
2085  $result = preg_replace("/(?:[\\x20]*\\n){2,}[\\x20]*/", "\n\n", $result);
2086  $result = trim($result);
2087  }
2088  return $result;
2089  }
2090  }


Generated: Thu Apr 25 01:01:05 2024 Cross-referenced by PHPXref 0.7.1