PageRenderTime 61ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/wp-content/plugins/bbpress/includes/admin/parser.php

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