[ 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  @$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  }
1792  function Internal_ProcessIsolatedTag($tag_name, $tag_params, $tag_rule) {
1793  if (!$this->DoTag(BBCODE_CHECK, $tag_name, @$tag_params['_default'], $tag_params, "")) {
1794  $this->stack[] = Array(
1795  BBCODE_STACK_TOKEN => BBCODE_TEXT,
1796  BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text),
1797  BBCODE_STACK_TAG => false,
1798  BBCODE_STACK_CLASS => $this->current_class,
1799  );
1800  return;
1801  }
1802  $this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_tag'], $this->stack);
1803  $output = $this->DoTag(BBCODE_OUTPUT, $tag_name, @$tag_params['_default'], $tag_params, "");
1804  $this->Internal_CleanupWSByEatingInput(@$tag_rule['after_tag']);
1805  $this->stack[] = Array(
1806  BBCODE_STACK_TOKEN => BBCODE_TEXT,
1807  BBCODE_STACK_TEXT => $output,
1808  BBCODE_STACK_TAG => false,
1809  BBCODE_STACK_CLASS => $this->current_class,
1810  );
1811  }
1812  function Internal_ProcessVerbatimTag($tag_name, $tag_params, $tag_rule) {
1813  $state = $this->lexer->SaveState();
1814  $end_tag = $this->lexer->tagmarker . "/" . $tag_name . $this->lexer->end_tagmarker;
1815  $start = count($this->stack);
1816  $this->lexer->verbatim = true;
1817  while (($token_type = $this->lexer->NextToken()) != BBCODE_EOI) {
1818  if ($this->lexer->text == $end_tag) {
1819  $end_tag_params = $this->lexer->tag;
1820  break;
1821  }
1822  if ($this->output_limit > 0
1823  && $this->text_length + strlen($this->lexer->text) >= $this->output_limit) {
1824  $text = $this->Internal_LimitText($this->lexer->text,
1825  $this->output_limit - $this->text_length);
1826  if (strlen($text) > 0) {
1827  $this->text_length += strlen($text);
1828  $this->stack[] = Array(
1829  BBCODE_STACK_TOKEN => BBCODE_TEXT,
1830  BBCODE_STACK_TEXT => $this->FixupOutput($text),
1831  BBCODE_STACK_TAG => false,
1832  BBCODE_STACK_CLASS => $this->current_class,
1833  );
1834  }
1835  $this->Internal_DoLimit();
1836  break;
1837  }
1838  $this->text_length += strlen($this->lexer->text);
1839  $this->stack[] = Array(
1840  BBCODE_STACK_TOKEN => $token_type,
1841  BBCODE_STACK_TEXT => htmlspecialchars($this->lexer->text),
1842  BBCODE_STACK_TAG => $this->lexer->tag,
1843  BBCODE_STACK_CLASS => $this->current_class,
1844  );
1845  }
1846  $this->lexer->verbatim = false;
1847  if ($token_type == BBCODE_EOI) {
1848  $this->lexer->RestoreState($state);
1849  $this->stack[] = Array(
1850  BBCODE_STACK_TOKEN => BBCODE_TEXT,
1851  BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text),
1852  BBCODE_STACK_TAG => false,
1853  BBCODE_STACK_CLASS => $this->current_class,
1854  );
1855  return;
1856  }
1857  $newstart = $this->Internal_CleanupWSByIteratingPointer(@$tag_rule['after_tag'], $start, $this->stack);
1858  $this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_endtag'], $this->stack);
1859  $this->Internal_CleanupWSByEatingInput(@$tag_rule['after_endtag']);
1860  $content = $this->Internal_CollectText($this->stack, $newstart);
1861  array_splice($this->stack, $start);
1862  $this->Internal_ComputeCurrentClass();
1863  $this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_tag'], $this->stack);
1864  $tag_params['_endtag'] = $end_tag_params['_tag'];
1865  $tag_params['_hasend'] = true;
1866  $output = $this->DoTag(BBCODE_OUTPUT, $tag_name,
1867  @$tag_params['_default'], $tag_params, $content);
1868  $this->stack[] = Array(
1869  BBCODE_STACK_TOKEN => BBCODE_TEXT,
1870  BBCODE_STACK_TEXT => $output,
1871  BBCODE_STACK_TAG => false,
1872  BBCODE_STACK_CLASS => $this->current_class,
1873  );
1874  }
1875  function Internal_ParseStartTagToken() {
1876  $tag_params = $this->lexer->tag;
1877  $tag_name = @$tag_params['_name'];
1878  if (!isset($this->tag_rules[$tag_name])) {
1879  $this->stack[] = Array(
1880  BBCODE_STACK_TOKEN => BBCODE_TEXT,
1881  BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text),
1882  BBCODE_STACK_TAG => false,
1883  BBCODE_STACK_CLASS => $this->current_class,
1884  );
1885  return;
1886  }
1887  $tag_rule = $this->tag_rules[$tag_name];
1888  $allow_in = is_array($tag_rule['allow_in'])
1889  ? $tag_rule['allow_in'] : Array($this->root_class);
1890  if (!in_array($this->current_class, $allow_in)) {
1891  if (!$this->Internal_RewindToClass($allow_in)) {
1892  $this->stack[] = Array(
1893  BBCODE_STACK_TOKEN => BBCODE_TEXT,
1894  BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text),
1895  BBCODE_STACK_TAG => false,
1896  BBCODE_STACK_CLASS => $this->current_class,
1897  );
1898  return;
1899  }
1900  }
1901  $end_tag = isset($tag_rule['end_tag']) ? $tag_rule['end_tag'] : BBCODE_REQUIRED;
1902  if ($end_tag == BBCODE_PROHIBIT) {
1903  $this->Internal_ProcessIsolatedTag($tag_name, $tag_params, $tag_rule);
1904  return;
1905  }
1906  if (!$this->DoTag(BBCODE_CHECK, $tag_name, @$tag_params['_default'], $tag_params, "")) {
1907  $this->stack[] = Array(
1908  BBCODE_STACK_TOKEN => BBCODE_TEXT,
1909  BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text),
1910  BBCODE_STACK_TAG => false,
1911  BBCODE_STACK_CLASS => $this->current_class,
1912  );
1913  return;
1914  }
1915  if (@$tag_rule['content'] == BBCODE_VERBATIM) {
1916  $this->Internal_ProcessVerbatimTag($tag_name, $tag_params, $tag_rule);
1917  return;
1918  }
1919  if (isset($tag_rule['class']))
1920  $newclass = $tag_rule['class'];
1921  else $newclass = $this->root_class;
1922  $this->stack[] = Array(
1923  BBCODE_STACK_TOKEN => $this->lexer->token,
1924  BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text),
1925  BBCODE_STACK_TAG => $this->lexer->tag,
1926  BBCODE_STACK_CLASS => ($this->current_class = $newclass),
1927  );
1928  if (!isset($this->start_tags[$tag_name]))
1929  $this->start_tags[$tag_name] = Array(count($this->stack)-1);
1930  else $this->start_tags[$tag_name][] = count($this->stack)-1;
1931  }
1932  function Internal_ParseEndTagToken() {
1933  $tag_params = $this->lexer->tag;
1934  $tag_name = @$tag_params['_name'];
1935  $contents = $this->Internal_FinishTag($tag_name);
1936  if ($contents === false) {
1937  if (@$this->lost_start_tags[$tag_name] > 0) {
1938  $this->lost_start_tags[$tag_name]--;
1939  }
1940  else {
1941  $this->stack[] = Array(
1942  BBCODE_STACK_TOKEN => BBCODE_TEXT,
1943  BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text),
1944  BBCODE_STACK_TAG => false,
1945  BBCODE_STACK_CLASS => $this->current_class,
1946  );
1947  }
1948  return;
1949  }
1950  $start_tag_node = array_pop($this->stack);
1951  $start_tag_params = $start_tag_node[BBCODE_STACK_TAG];
1952  $this->Internal_ComputeCurrentClass();
1953  $this->Internal_CleanupWSByPoppingStack(@$this->tag_rules[$tag_name]['before_tag'], $this->stack);
1954  $start_tag_params['_endtag'] = $tag_params['_tag'];
1955  $start_tag_params['_hasend'] = true;
1956  $output = $this->DoTag(BBCODE_OUTPUT, $tag_name, @$start_tag_params['_default'],
1957  $start_tag_params, $contents);
1958  $this->Internal_CleanupWSByEatingInput(@$this->tag_rules[$tag_name]['after_endtag']);
1959  $this->stack[] = Array(
1960  BBCODE_STACK_TOKEN => BBCODE_TEXT,
1961  BBCODE_STACK_TEXT => $output,
1962  BBCODE_STACK_TAG => false,
1963  BBCODE_STACK_CLASS => $this->current_class,
1964  );
1965  }
1966  function Parse($string) {
1967  $this->lexer = new BBCodeLexer($string, $this->tag_marker);
1968  $this->lexer->debug = $this->debug;
1969  $old_output_limit = $this->output_limit;
1970  if ($this->output_limit > 0) {
1971  if (strlen($string) < $this->output_limit) {
1972  $this->output_limit = 0;
1973  }
1974  else if ($this->limit_precision > 0) {
1975  $guess_length = $this->lexer->GuessTextLength();
1976  if ($guess_length < $this->output_limit * ($this->limit_precision + 1.0)) {
1977  $this->output_limit = 0;
1978  }
1979  else {
1980  }
1981  }
1982  }
1983  $this->stack = Array();
1984  $this->start_tags = Array();
1985  $this->lost_start_tags = Array();
1986  $this->text_length = 0;
1987  $this->was_limited = false;
1988  if (strlen($this->pre_trim) > 0)
1989  $this->Internal_CleanupWSByEatingInput($this->pre_trim);
1990  $newline = $this->plain_mode ? "\n" : "<br />\n";
1991  while (true) {
1992  if (($token_type = $this->lexer->NextToken()) == BBCODE_EOI) {
1993  break;
1994  }
1995  switch ($token_type) {
1996  case BBCODE_TEXT:
1997  if ($this->output_limit > 0
1998  && $this->text_length + strlen($this->lexer->text) >= $this->output_limit) {
1999  $text = $this->Internal_LimitText($this->lexer->text,
2000  $this->output_limit - $this->text_length);
2001  if (strlen($text) > 0) {
2002  $this->text_length += strlen($text);
2003  $this->stack[] = Array(
2004  BBCODE_STACK_TOKEN => BBCODE_TEXT,
2005  BBCODE_STACK_TEXT => $this->FixupOutput($text),
2006  BBCODE_STACK_TAG => false,
2007  BBCODE_STACK_CLASS => $this->current_class,
2008  );
2009  }
2010  $this->Internal_DoLimit();
2011  break 2;
2012  }
2013  $this->text_length += strlen($this->lexer->text);
2014  $this->stack[] = Array(
2015  BBCODE_STACK_TOKEN => BBCODE_TEXT,
2016  BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text),
2017  BBCODE_STACK_TAG => false,
2018  BBCODE_STACK_CLASS => $this->current_class,
2019  );
2020  break;
2021  case BBCODE_WS:
2022  if ($this->output_limit > 0
2023  && $this->text_length + strlen($this->lexer->text) >= $this->output_limit) {
2024  $this->Internal_DoLimit();
2025  break 2;
2026  }
2027  $this->text_length += strlen($this->lexer->text);
2028  $this->stack[] = Array(
2029  BBCODE_STACK_TOKEN => BBCODE_WS,
2030  BBCODE_STACK_TEXT => $this->lexer->text,
2031  BBCODE_STACK_TAG => false,
2032  BBCODE_STACK_CLASS => $this->current_class,
2033  );
2034  break;
2035  case BBCODE_NL:
2036  if ($this->ignore_newlines) {
2037  if ($this->output_limit > 0
2038  && $this->text_length + 1 >= $this->output_limit) {
2039  $this->Internal_DoLimit();
2040  break 2;
2041  }
2042  $this->text_length += 1;
2043  $this->stack[] = Array(
2044  BBCODE_STACK_TOKEN => BBCODE_WS,
2045  BBCODE_STACK_TEXT => "\n",
2046  BBCODE_STACK_TAG => false,
2047  BBCODE_STACK_CLASS => $this->current_class,
2048  );
2049  }
2050  else {
2051  $this->Internal_CleanupWSByPoppingStack("s", $this->stack);
2052  if ($this->output_limit > 0
2053  && $this->text_length + 1 >= $this->output_limit) {
2054  $this->Internal_DoLimit();
2055  break 2;
2056  }
2057  $this->text_length += 1;
2058  $this->stack[] = Array(
2059  BBCODE_STACK_TOKEN => BBCODE_NL,
2060  BBCODE_STACK_TEXT => $newline,
2061  BBCODE_STACK_TAG => false,
2062  BBCODE_STACK_CLASS => $this->current_class,
2063  );
2064  $this->Internal_CleanupWSByEatingInput("s");
2065  }
2066  break;
2067  case BBCODE_TAG:
2068  $this->Internal_ParseStartTagToken();
2069  break;
2070  case BBCODE_ENDTAG:
2071  $this->Internal_ParseEndTagToken();
2072  break;
2073  default:
2074  break;
2075  }
2076  }
2077  if (strlen($this->post_trim) > 0)
2078  $this->Internal_CleanupWSByPoppingStack($this->post_trim, $this->stack);
2079  $result = $this->Internal_GenerateOutput(0);
2080  $result = $this->Internal_CollectTextReverse($result, count($result) - 1);
2081  $this->output_limit = $old_output_limit;
2082  if ($this->plain_mode) {
2083  $result = preg_replace("/[\\x00-\\x09\\x0B-\\x20]+/", " ", $result);
2084  $result = preg_replace("/(?:[\\x20]*\\n){2,}[\\x20]*/", "\n\n", $result);
2085  $result = trim($result);
2086  }
2087  return $result;
2088  }
2089  }


Generated: Tue Jul 16 01:01:32 2019 Cross-referenced by PHPXref 0.7.1