BBCode

The bbPress BBCode class.

Defined (1)

The class is defined in the following location(s).

/includes/admin/parser.php  
  1. class BBCode { 
  2. var $tag_rules; 
  3. var $defaults; 
  4. var $current_class; 
  5. var $root_class; 
  6. var $lost_start_tags; 
  7. var $start_tags; 
  8. var $allow_ampersand; 
  9. var $tag_marker; 
  10. var $ignore_newlines; 
  11. var $plain_mode; 
  12. var $detect_urls; 
  13. var $url_pattern; 
  14. var $output_limit; 
  15. var $text_length; 
  16. var $was_limited; 
  17. var $limit_tail; 
  18. var $limit_precision; 
  19. var $smiley_dir; 
  20. var $smiley_url; 
  21. var $smileys; 
  22. var $smiley_regex; 
  23. var $enable_smileys; 
  24. var $wiki_url; 
  25. var $local_img_dir; 
  26. var $local_img_url; 
  27. var $url_targetable; 
  28. var $url_target; 
  29. var $rule_html; 
  30. var $pre_trim; 
  31. var $post_trim; 
  32. var $debug; 
  33.  
  34.  
  35. /** ADDED */ 
  36. // singleton instance 
  37. private static $instance; 
  38.  
  39. // private constructor function 
  40. // to prevent external instantiation 
  41. private function __construct() 
  42. $this->defaults = new BBCodeLibrary; 
  43. $this->tag_rules = $this->defaults->default_tag_rules; 
  44. $this->smileys = $this->defaults->default_smileys; 
  45. $this->enable_smileys = true; 
  46. $this->smiley_regex = false; 
  47. $this->smiley_dir = $this->GetDefaultSmileyDir(); 
  48. $this->smiley_url = $this->GetDefaultSmileyURL(); 
  49. $this->wiki_url = $this->GetDefaultWikiURL(); 
  50. $this->local_img_dir = $this->GetDefaultLocalImgDir(); 
  51. $this->local_img_url = $this->GetDefaultLocalImgURL(); 
  52. $this->rule_html = $this->GetDefaultRuleHTML(); 
  53. $this->pre_trim = ""; 
  54. $this->post_trim = ""; 
  55. $this->root_class = 'block'; 
  56. $this->lost_start_tags = Array(); 
  57. $this->start_tags = Array(); 
  58. $this->tag_marker = '['; 
  59. $this->allow_ampsersand = false; 
  60. $this->current_class = $this->root_class; 
  61. $this->debug = false; 
  62. $this->ignore_newlines = false; 
  63. $this->output_limit = 0; 
  64. $this->plain_mode = false; 
  65. $this->was_limited = false; 
  66. $this->limit_tail = "..."; 
  67. $this->limit_precision = 0.15; 
  68. $this->detect_urls = false; 
  69. $this->url_pattern = '<a href="{$url/h}">{$text/h}</a>'; 
  70. $this->url_targetable = false; 
  71. $this->url_target = false; 
  72.  
  73. // getInstance method 
  74. public static function getInstance() 
  75. if(!self::$instance) 
  76. self::$instance = new self(); 
  77.  
  78. return self::$instance; 
  79. /** ADDED */ 
  80.  
  81.  
  82. function SetPreTrim($trim = "a") { $this->pre_trim = $trim; } 
  83. function GetPreTrim() { return $this->pre_trim; } 
  84. function SetPostTrim($trim = "a") { $this->post_trim = $trim; } 
  85. function GetPostTrim() { return $this->post_trim; } 
  86. function SetRoot($class = 'block') { $this->root_class = $class; } 
  87. function SetRootInline() { $this->root_class = 'inline'; } 
  88. function SetRootBlock() { $this->root_class = 'block'; } 
  89. function GetRoot() { return $this->root_class; } 
  90. function SetDebug($enable = true) { $this->debug = $enable; } 
  91. function GetDebug() { return $this->debug; } 
  92. function SetAllowAmpersand($enable = true) { $this->allow_ampersand = $enable; } 
  93. function GetAllowAmpersand() { return $this->allow_ampersand; } 
  94. function SetTagMarker($marker = '[') { $this->tag_marker = $marker; } 
  95. function GetTagMarker() { return $this->tag_marker; } 
  96. function SetIgnoreNewlines($ignore = true) { $this->ignore_newlines = $ignore; } 
  97. function GetIgnoreNewlines() { return $this->ignore_newlines; } 
  98. function SetLimit($limit = 0) { $this->output_limit = $limit; } 
  99. function GetLimit() { return $this->output_limit; } 
  100. function SetLimitTail($tail = "...") { $this->limit_tail = $tail; } 
  101. function GetLimitTail() { return $this->limit_tail; } 
  102. function SetLimitPrecision($prec = 0.15) { $this->limit_precision = $prec; } 
  103. function GetLimitPrecision() { return $this->limit_precision; } 
  104. function WasLimited() { return $this->was_limited; } 
  105. function SetPlainMode($enable = true) { $this->plain_mode = $enable; } 
  106. function GetPlainMode() { return $this->plain_mode; } 
  107. function SetDetectURLs($enable = true) { $this->detect_urls = $enable; } 
  108. function GetDetectURLs() { return $this->detect_urls; } 
  109. function SetURLPattern($pattern) { $this->url_pattern = $pattern; } 
  110. function GetURLPattern() { return $this->url_pattern; } 
  111. function SetURLTargetable($enable) { $this->url_targetable = $enable; } 
  112. function GetURLTargetable() { return $this->url_targetable; } 
  113. function SetURLTarget($target) { $this->url_target = $target; } 
  114. function GetURLTarget() { return $this->url_target; } 
  115. function AddRule($name, $rule) { $this->tag_rules[$name] = $rule; } 
  116. function RemoveRule($name) { unset($this->tag_rules[$name]); } 
  117. function GetRule($name) { return isset($this->tag_rules[$name]) 
  118. ? $this->tag_rules[$name] : false; } 
  119. function ClearRules() { $this->tag_rules = Array(); } 
  120. function GetDefaultRule($name) { return isset($this->defaults->default_tag_rules[$name]) 
  121. ? $this->defaults->default_tag_rules[$name] : false; } 
  122. function SetDefaultRule($name) { if (isset($this->defaults->default_tag_rules[$name])) 
  123. $this->AddRule($name, $this->defaults->default_tag_rules[$name]); 
  124. else $this->RemoveRule($name); } 
  125. function GetDefaultRules() { return $this->defaults->default_tag_rules; } 
  126. function SetDefaultRules() { $this->tag_rules = $this->defaults->default_tag_rules; } 
  127. function SetWikiURL($url) { $this->wiki_url = $url; } 
  128. function GetWikiURL($url) { return $this->wiki_url; } 
  129. function GetDefaultWikiURL() { return '/?page='; } 
  130. function SetLocalImgDir($path) { $this->local_img_dir = $path; } 
  131. function GetLocalImgDir() { return $this->local_img_dir; } 
  132. function GetDefaultLocalImgDir() { return "img"; } 
  133. function SetLocalImgURL($path) { $this->local_img_url = $path; } 
  134. function GetLocalImgURL() { return $this->local_img_url; } 
  135. function GetDefaultLocalImgURL() { return "img"; } 
  136. function SetRuleHTML($html) { $this->rule_html = $html; } 
  137. function GetRuleHTML() { return $this->rule_html; } 
  138. function GetDefaultRuleHTML() { return "\n<hr class=\"bbcode_rule\" />\n"; } 
  139. function AddSmiley($code, $image) { $this->smileys[$code] = $image; $this->smiley_regex = false; } 
  140. function RemoveSmiley($code) { unset($this->smileys[$code]); $this->smiley_regex = false; } 
  141. function GetSmiley($code) { return isset($this->smileys[$code]) 
  142. ? $this->smileys[$code] : false; } 
  143. function ClearSmileys() { $this->smileys = Array(); $this->smiley_regex = false; } 
  144. function GetDefaultSmiley($code) { return isset($this->defaults->default_smileys[$code]) 
  145. ? $this->defaults->default_smileys[$code] : false; } 
  146. function SetDefaultSmiley($code) { $this->smileys[$code] = @$this->defaults->default_smileys[$code]; 
  147. $this->smiley_regex = false; } 
  148. function GetDefaultSmileys() { return $this->defaults->default_smileys; } 
  149. function SetDefaultSmileys() { $this->smileys = $this->defaults->default_smileys; 
  150. $this->smiley_regex = false; } 
  151. function SetSmileyDir($path) { $this->smiley_dir = $path; } 
  152. function GetSmileyDir() { return $this->smiley_dir; } 
  153. function GetDefaultSmileyDir() { return "smileys"; } 
  154. function SetSmileyURL($path) { $this->smiley_url = $path; } 
  155. function GetSmileyURL() { return $this->smiley_url; } 
  156. function GetDefaultSmileyURL() { return "smileys"; } 
  157. function SetEnableSmileys($enable = true) { $this->enable_smileys = $enable; } 
  158. function GetEnableSmileys() { return $this->enable_smileys; } 
  159. function nl2br($string) { 
  160. return preg_replace("/\\x0A|\\x0D|\\x0A\\x0D|\\x0D\\x0A/", "<br />\n", $string); 
  161. function UnHTMLEncode($string) { 
  162. if (function_exists("html_entity_decode")) 
  163. return html_entity_decode($string); 
  164. $string = preg_replace('~&#x([0-9a-f]+);~ei', 'chr(hexdec("\\1"))', $string); 
  165. $string = preg_replace('~&#([0-9]+);~e', 'chr("\\1")', $string); 
  166. $trans_tbl = get_html_translation_table(HTML_ENTITIES); 
  167. $trans_tbl = array_flip($trans_tbl); 
  168. return strtr($string, $trans_tbl); 
  169. function Wikify($string) { 
  170. return rawurlencode(str_replace(" ", "_",  
  171. trim(preg_replace("/[!?;@#\$%\\^&*<>=+`~\\x00-\\x20_-]+/", " ", $string)))); 
  172. function IsValidURL($string, $email_too = true) { 
  173. if (preg_match("/^ 
  174. (?:https?|ftp):\\/\\/ 
  175. (?: 
  176. (?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+ 
  177. [a-zA-Z0-9] 
  178. (?:[a-zA-Z0-9-]*[a-zA-Z0-9])? 
  179. \\[ 
  180. (?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.) {3} 
  181. (?: 
  182. 25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-zA-Z0-9-]*[a-zA-Z0-9]: 
  183. (?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21-\\x5A\\x53-\\x7F] 
  184. |\\\\[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F])+ 
  185. \\] 
  186. (?::[0-9]{1, 5})? 
  187. (?:[\\/\\?\\#][^\\n\\r]*)? 
  188. $/Dx", $string)) return true; 
  189. if (preg_match("/^[^:]+([\\/\\\\?#][^\\r\\n]*)?$/D", $string)) 
  190. return true; 
  191. if ($email_too) 
  192. if (substr($string, 0, 7) == "mailto:") 
  193. return $this->IsValidEmail(substr($string, 7)); 
  194. return false; 
  195. function IsValidEmail($string) { 
  196. $validator = new BBCodeEmailAddressValidator; 
  197. return $validator->check_email_address($string); 
  198. /** 
  199. return preg_match("/^ 
  200. (?: 
  201. [a-z0-9\\!\\#\\\$\\%\\&\\'\\*\\+\\/=\\?\\^_`\\{\\|\\}~-]+ 
  202. (?:\.[a-z0-9\\!\\#\\\$\\%\\&\\'\\*\\+\\/=\\?\\^_`\\{\\|\\}~-]+)* 
  203. \"(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F] 
  204. |\\\\[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F])*\" 
  205. (?: 
  206. (?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+ 
  207. [a-z0-9] 
  208. (?:[a-z0-9-]*[a-z0-9])? 
  209. \\[ 
  210. (?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.) {3} 
  211. (?: 
  212. 25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]: 
  213. (?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21-\\x5A\\x53-\\x7F] 
  214. |\\\\[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F])+ 
  215. \\] 
  216. $/Dx", $string); 
  217. */ 
  218. function HTMLEncode($string) { 
  219. if (!$this->allow_ampersand) 
  220. return htmlspecialchars($string); 
  221. else return str_replace(Array('<', '>', '"'),  
  222. Array('<', '>', '"'), $string); 
  223. function FixupOutput($string) { 
  224. if (!$this->detect_urls) { 
  225. $output = $this->Internal_ProcessSmileys($string); 
  226. else { 
  227. $chunks = $this->Internal_AutoDetectURLs($string); 
  228. $output = Array(); 
  229. if (count($chunks)) { 
  230. $is_a_url = false; 
  231. foreach ($chunks as $index => $chunk) { 
  232. if (!$is_a_url) { 
  233. $chunk = $this->Internal_ProcessSmileys($chunk); 
  234. $output[] = $chunk; 
  235. $is_a_url = !$is_a_url; 
  236. $output = implode("", $output); 
  237. return $output; 
  238. function Internal_ProcessSmileys($string) { 
  239. if (!$this->enable_smileys || $this->plain_mode) { 
  240. $output = $this->HTMLEncode($string); 
  241. else { 
  242. if ($this->smiley_regex === false) { 
  243. $this->Internal_RebuildSmileys(); 
  244. $tokens = preg_split($this->smiley_regex, $string, -1, PREG_SPLIT_DELIM_CAPTURE); 
  245. if (count($tokens) <= 1) { 
  246. $output = $this->HTMLEncode($string); 
  247. else { 
  248. $output = ""; 
  249. $is_a_smiley = false; 
  250. foreach ($tokens as $token) { 
  251. if (!$is_a_smiley) { 
  252. $output .= $this->HTMLEncode($token); 
  253. else { 
  254. if (isset($this->smiley_info[$token])) { 
  255. $info = $this->smiley_info[$token]; 
  256. else { 
  257. $info = @getimagesize($this->smiley_dir . '/' . $this->smileys[$token]); 
  258. $this->smiley_info[$token] = $info; 
  259. $alt = htmlspecialchars($token); 
  260. $output .= "<img src=\"" . htmlspecialchars($this->smiley_url . '/' . $this->smileys[$token]) 
  261. . "\" width=\"{$info[0]}\" height=\"{$info[1]}\"" 
  262. . " alt=\"$alt\" title=\"$alt\" class=\"bbcode_smiley\" />"; 
  263. $is_a_smiley = !$is_a_smiley; 
  264. return $output; 
  265. function Internal_RebuildSmileys() { 
  266. $regex = Array("/(?<![\\w])("); 
  267. $first = true; 
  268. foreach ($this->smileys as $code => $filename) { 
  269. if (!$first) $regex[] = "|"; 
  270. $regex[] = preg_quote("$code", '/'); 
  271. $first = false; 
  272. $regex[] = ")(?![\\w])/"; 
  273. $this->smiley_regex = implode("", $regex); 
  274. function Internal_AutoDetectURLs($string) { 
  275. $output = preg_split("/( (?: 
  276. (?:https?|ftp) : \\/* 
  277. (?: 
  278. (?: (?: [a-zA-Z0-9-]{2, } \\. )+ 
  279. (?: arpa | com | org | net | edu | gov | mil | int | [a-z]{2} 
  280. | aero | biz | coop | info | museum | name | pro 
  281. | example | invalid | localhost | test | local | onion | swift ) ) 
  282. | (?: [0-9]{1, 3} \\. [0-9]{1, 3} \\. [0-9]{1, 3} \\. [0-9]{1, 3} ) 
  283. | (?: [0-9A-Fa-f:]+ : [0-9A-Fa-f]{1, 4} ) 
  284. (?: : [0-9]+ )? 
  285. (?! [a-zA-Z0-9.:-] ) 
  286. (?: 
  287. \\/ 
  288. [^&?#\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]* 
  289. )? 
  290. (?: 
  291. [?#] 
  292. [^\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]+ 
  293. )? 
  294. ) | (?: 
  295. (?: 
  296. (?: (?: [a-zA-Z0-9-]{2, } \\. )+ 
  297. (?: arpa | com | org | net | edu | gov | mil | int | [a-z]{2} 
  298. | aero | biz | coop | info | museum | name | pro 
  299. | example | invalid | localhost | test | local | onion | swift ) ) 
  300. | (?: [0-9]{1, 3} \\. [0-9]{1, 3} \\. [0-9]{1, 3} \\. [0-9]{1, 3} ) 
  301. (?: : [0-9]+ )? 
  302. (?! [a-zA-Z0-9.:-] ) 
  303. (?: 
  304. \\/ 
  305. [^&?#\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]* 
  306. )? 
  307. (?: 
  308. [?#] 
  309. [^\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]+ 
  310. )? 
  311. ) | (?: 
  312. [a-zA-Z0-9._-]{2, } @ 
  313. (?: 
  314. (?: (?: [a-zA-Z0-9-]{2, } \\. )+ 
  315. (?: arpa | com | org | net | edu | gov | mil | int | [a-z]{2} 
  316. | aero | biz | coop | info | museum | name | pro 
  317. | example | invalid | localhost | test | local | onion | swift ) ) 
  318. | (?: [0-9]{1, 3} \\. [0-9]{1, 3} \\. [0-9]{1, 3} \\. [0-9]{1, 3} ) 
  319. ) )/Dx", $string, -1, PREG_SPLIT_DELIM_CAPTURE); 
  320. if (count($output) > 1) { 
  321. $is_a_url = false; 
  322. foreach ($output as $index => $token) { 
  323. if ($is_a_url) { 
  324. if (preg_match("/^[a-zA-Z0-9._-]{2, }@/", $token)) { 
  325. $url = "mailto:" . $token; 
  326. else if (preg_match("/^(https?:|ftp:)\\/*([^\\/&?#]+)\\/*(.*)\$/", $token, $matches)) { 
  327. $url = $matches[1] . '/' . '/' . $matches[2] . "/" . $matches[3]; 
  328. else { 
  329. preg_match("/^([^\\/&?#]+)\\/*(.*)\$/", $token, $matches); 
  330. $url = "http:/" . "/" . $matches[1] . "/" . $matches[2]; 
  331. $params = @parse_url($url); 
  332. if (!is_array($params)) $params = Array(); 
  333. $params['url'] = $url; 
  334. $params['link'] = $url; 
  335. $params['text'] = $token; 
  336. $output[$index] = $this->FillTemplate($this->url_pattern, $params); 
  337. $is_a_url = !$is_a_url; 
  338. return $output; 
  339. function FillTemplate($template, $insert_array, $default_array = Array()) { 
  340. $pieces = preg_split('/(\{\$[a-zA-Z0-9_.:\/-]+\})/', $template,  
  341. -1, PREG_SPLIT_DELIM_CAPTURE); 
  342. if (count($pieces) <= 1) 
  343. return $template; 
  344. $result = Array(); 
  345. $is_an_insert = false; 
  346. foreach ($pieces as $piece) { 
  347. if (!$is_an_insert) { 
  348. $result[] = $piece; 
  349. else if (!preg_match('/\{\$([a-zA-Z0-9_:-]+)((?:\\.[a-zA-Z0-9_:-]+)*)(?:\/([a-zA-Z0-9_:-]+))?\}/', $piece, $matches)) { 
  350. $result[] = $piece; 
  351. else { 
  352. if (isset($insert_array[$matches[1]])) 
  353. $value = @$insert_array[$matches[1]]; 
  354. else $value = @$default_array[$matches[1]]; 
  355. if (strlen(@$matches[2])) { 
  356. foreach (split(".", substr($matches[2], 1)) as $index) { 
  357. if (is_array($value)) 
  358. $value = @$value[$index]; 
  359. else if (is_object($value)) { 
  360. $value = (array)$value; 
  361. $value = @$value[$index]; 
  362. else $value = ""; 
  363. switch (gettype($value)) { 
  364. case 'boolean': $value = $value ? "true" : "false"; break; 
  365. case 'integer': $value = (string)$value; break; 
  366. case 'double': $value = (string)$value; break; 
  367. case 'string': break; 
  368. default: $value = ""; break; 
  369. if (strlen(@$matches[3])) 
  370. $flags = array_flip(str_split($matches[3])); 
  371. else $flags = Array(); 
  372. if (!isset($flags['v'])) { 
  373. if (isset($flags['w'])) 
  374. $value = preg_replace("/[\\x00-\\x09\\x0B-\x0C\x0E-\\x20]+/", " ", $value); 
  375. if (isset($flags['t'])) $value = trim($value); 
  376. if (isset($flags['b'])) $value = basename($value); 
  377. if (isset($flags['e'])) $value = $this->HTMLEncode($value); 
  378. else if (isset($flags['k'])) $value = $this->Wikify($value); 
  379. else if (isset($flags['h'])) $value = htmlspecialchars($value); 
  380. else if (isset($flags['u'])) $value = urlencode($value); 
  381. if (isset($flags['n'])) $value = $this->nl2br($value); 
  382. $result[] = $value; 
  383. $is_an_insert = !$is_an_insert; 
  384. return implode("", $result); 
  385. function Internal_CollectText($array, $start = 0) { 
  386. ob_start(); 
  387. for ($start = intval($start), $end = count($array); $start < $end; $start++) 
  388. print $array[$start][BBCODE_STACK_TEXT]; 
  389. $output = ob_get_contents(); 
  390. ob_end_clean(); 
  391. return $output; 
  392. function Internal_CollectTextReverse($array, $start = 0, $end = 0) { 
  393. ob_start(); 
  394. for ($start = intval($start); $start >= $end; $start--) 
  395. print $array[$start][BBCODE_STACK_TEXT]; 
  396. $output = ob_get_contents(); 
  397. ob_end_clean(); 
  398. return $output; 
  399. function Internal_GenerateOutput($pos) { 
  400. $output = Array(); 
  401. while (count($this->stack) > $pos) { 
  402. $token = array_pop($this->stack); 
  403. if ($token[BBCODE_STACK_TOKEN] != BBCODE_TAG) { 
  404. $output[] = $token; 
  405. else { 
  406. $name = @$token[BBCODE_STACK_TAG]['_name']; 
  407. $rule = @$this->tag_rules[$name]; 
  408. $end_tag = @$rule['end_tag']; 
  409. if (!isset($rule['end_tag'])) $end_tag = BBCODE_REQUIRED; 
  410. else $end_tag = $rule['end_tag']; 
  411. array_pop($this->start_tags[$name]); 
  412. if ($end_tag == BBCODE_PROHIBIT) { 
  413. $output[] = Array( 
  414. BBCODE_STACK_TOKEN => BBCODE_TEXT,  
  415. BBCODE_STACK_TAG => false,  
  416. BBCODE_STACK_TEXT => $token[BBCODE_STACK_TEXT],  
  417. BBCODE_STACK_CLASS => $this->current_class,  
  418. ); 
  419. else { 
  420. if ($end_tag == BBCODE_REQUIRED) 
  421. @$this->lost_start_tags[$name] += 1; 
  422. $end = $this->Internal_CleanupWSByIteratingPointer(@$rule['before_endtag'], 0, $output); 
  423. $this->Internal_CleanupWSByPoppingStack(@$rule['after_tag'], $output); 
  424. $tag_body = $this->Internal_CollectTextReverse($output, count($output)-1, $end); 
  425. $this->Internal_CleanupWSByPoppingStack(@$rule['before_tag'], $this->stack); 
  426. $this->Internal_UpdateParamsForMissingEndTag(@$token[BBCODE_STACK_TAG]); 
  427. $tag_output = $this->DoTag(BBCODE_OUTPUT, $name,  
  428. @$token[BBCODE_STACK_TAG]['_default'], @$token[BBCODE_STACK_TAG], $tag_body); 
  429. $output = Array(Array( 
  430. BBCODE_STACK_TOKEN => BBCODE_TEXT,  
  431. BBCODE_STACK_TAG => false,  
  432. BBCODE_STACK_TEXT => $tag_output,  
  433. BBCODE_STACK_CLASS => $this->current_class 
  434. )); 
  435. $this->Internal_ComputeCurrentClass(); 
  436. return $output; 
  437. function Internal_RewindToClass($class_list) { 
  438. $pos = count($this->stack) - 1; 
  439. while ($pos >= 0 && !in_array($this->stack[$pos][BBCODE_STACK_CLASS], $class_list)) 
  440. $pos--; 
  441. if ($pos < 0) { 
  442. if (!in_array($this->root_class, $class_list)) 
  443. return false; 
  444. $output = $this->Internal_GenerateOutput($pos+1); 
  445. while (count($output)) { 
  446. $token = array_pop($output); 
  447. $token[BBCODE_STACK_CLASS] = $this->current_class; 
  448. $this->stack[] = $token; 
  449. return true; 
  450. function Internal_FinishTag($tag_name) { 
  451. if (strlen($tag_name) <= 0) 
  452. return false; 
  453. if (isset($this->start_tags[$tag_name]) 
  454. && count($this->start_tags[$tag_name])) 
  455. $pos = array_pop($this->start_tags[$tag_name]); 
  456. else $pos = -1; 
  457. if ($pos < 0) return false; 
  458. $newpos = $this->Internal_CleanupWSByIteratingPointer(@$this->tag_rules[$tag_name]['after_tag'],  
  459. $pos+1, $this->stack); 
  460. $delta = $newpos - ($pos+1); 
  461. $output = $this->Internal_GenerateOutput($newpos); 
  462. $newend = $this->Internal_CleanupWSByIteratingPointer(@$this->tag_rules[$tag_name]['before_endtag'],  
  463. 0, $output); 
  464. $output = $this->Internal_CollectTextReverse($output, count($output) - 1, $newend); 
  465. while ($delta-- > 0) 
  466. array_pop($this->stack); 
  467. $this->Internal_ComputeCurrentClass(); 
  468. return $output; 
  469. function Internal_ComputeCurrentClass() { 
  470. if (count($this->stack) > 0) 
  471. $this->current_class = $this->stack[count($this->stack)-1][BBCODE_STACK_CLASS]; 
  472. else $this->current_class = $this->root_class; 
  473. function Internal_DumpStack($array = false, $raw = false) { 
  474. if (!$raw) $string = "<span style='color: #00C;'>"; 
  475. else $string = ""; 
  476. if ($array === false) 
  477. $array = $this->stack; 
  478. foreach ($array as $item) { 
  479. switch (@$item[BBCODE_STACK_TOKEN]) { 
  480. case BBCODE_TEXT: 
  481. $string .= "\"" . htmlspecialchars(@$item[BBCODE_STACK_TEXT]) . "\" "; 
  482. break; 
  483. case BBCODE_WS: 
  484. $string .= "WS "; 
  485. break; 
  486. case BBCODE_NL: 
  487. $string .= "NL "; 
  488. break; 
  489. case BBCODE_TAG: 
  490. $string .= "[" . htmlspecialchars(@$item[BBCODE_STACK_TAG]['_name']) . "] "; 
  491. break; 
  492. default: 
  493. $string .= "unknown "; 
  494. break; 
  495. if (!$raw) $string .= "</span>"; 
  496. return $string; 
  497. function Internal_CleanupWSByPoppingStack($pattern, &$array) { 
  498. if (strlen($pattern) <= 0) return; 
  499. $oldlen = count($array); 
  500. foreach (str_split($pattern) as $char) { 
  501. switch ($char) { 
  502. case 's': 
  503. while (count($array) > 0 && $array[count($array)-1][BBCODE_STACK_TOKEN] == BBCODE_WS) 
  504. array_pop($array); 
  505. break; 
  506. case 'n': 
  507. if (count($array) > 0 && $array[count($array)-1][BBCODE_STACK_TOKEN] == BBCODE_NL) 
  508. array_pop($array); 
  509. break; 
  510. case 'a': 
  511. while (count($array) > 0 
  512. && (($token = $array[count($array)-1][BBCODE_STACK_TOKEN]) == BBCODE_WS 
  513. || $token == BBCODE_NL)) 
  514. array_pop($array); 
  515. break; 
  516. if (count($array) != $oldlen) { 
  517. $this->Internal_ComputeCurrentClass(); 
  518. function Internal_CleanupWSByEatingInput($pattern) { 
  519. if (strlen($pattern) <= 0) return; 
  520. foreach (str_split($pattern) as $char) { 
  521. switch ($char) { 
  522. case 's': 
  523. $token_type = $this->lexer->NextToken(); 
  524. while ($token_type == BBCODE_WS) { 
  525. $token_type = $this->lexer->NextToken(); 
  526. $this->lexer->UngetToken(); 
  527. break; 
  528. case 'n': 
  529. $token_type = $this->lexer->NextToken(); 
  530. if ($token_type != BBCODE_NL) 
  531. $this->lexer->UngetToken(); 
  532. break; 
  533. case 'a': 
  534. $token_type = $this->lexer->NextToken(); 
  535. while ($token_type == BBCODE_WS || $token_type == BBCODE_NL) { 
  536. $token_type = $this->lexer->NextToken(); 
  537. $this->lexer->UngetToken(); 
  538. break; 
  539. function Internal_CleanupWSByIteratingPointer($pattern, $pos, $array) { 
  540. if (strlen($pattern) <= 0) return $pos; 
  541. foreach (str_split($pattern) as $char) { 
  542. switch ($char) { 
  543. case 's': 
  544. while ($pos < count($array) && $array[$pos][BBCODE_STACK_TOKEN] == BBCODE_WS) 
  545. $pos++; 
  546. break; 
  547. case 'n': 
  548. if ($pos < count($array) && $array[$pos][BBCODE_STACK_TOKEN] == BBCODE_NL) 
  549. $pos++; 
  550. break; 
  551. case 'a': 
  552. while ($pos < count($array) 
  553. && (($token = $array[$pos][BBCODE_STACK_TOKEN]) == BBCODE_WS || $token == BBCODE_NL)) 
  554. $pos++; 
  555. break; 
  556. return $pos; 
  557. function Internal_LimitText($string, $limit) { 
  558. $chunks = preg_split("/([\\x00-\\x20]+)/", $string, -1, PREG_SPLIT_DELIM_CAPTURE); 
  559. $output = ""; 
  560. foreach ($chunks as $chunk) { 
  561. if (strlen($output) + strlen($chunk) > $limit) 
  562. break; 
  563. $output .= $chunk; 
  564. $output = rtrim($output); 
  565. return $output; 
  566. function Internal_DoLimit() { 
  567. $this->Internal_CleanupWSByPoppingStack("a", $this->stack); 
  568. if (strlen($this->limit_tail) > 0) { 
  569. $this->stack[] = Array( 
  570. BBCODE_STACK_TOKEN => BBCODE_TEXT,  
  571. BBCODE_STACK_TEXT => $this->limit_tail,  
  572. BBCODE_STACK_TAG => false,  
  573. BBCODE_STACK_CLASS => $this->current_class,  
  574. ); 
  575. $this->was_limited = true; 
  576. function DoTag($action, $tag_name, $default_value, $params, $contents) { 
  577. $tag_rule = @$this->tag_rules[$tag_name]; 
  578. switch ($action) { 
  579. case BBCODE_CHECK: 
  580. if (isset($tag_rule['allow'])) { 
  581. foreach ($tag_rule['allow'] as $param => $pattern) { 
  582. if ($param == '_content') $value = $contents; 
  583. else if ($param == '_defaultcontent') { 
  584. if (strlen($default_value)) 
  585. $value = $default_value; 
  586. else $value = $contents; 
  587. else { 
  588. if (isset($params[$param])) 
  589. $value = $params[$param]; 
  590. else $value = @$tag_rule['default'][$param]; 
  591. if (!preg_match($pattern, $value)) { 
  592. return false; 
  593. return true; 
  594. switch (@$tag_rule['mode']) { 
  595. default: 
  596. case BBCODE_MODE_SIMPLE: 
  597. $result = true; 
  598. break; 
  599. case BBCODE_MODE_ENHANCED: 
  600. $result = true; 
  601. break; 
  602. case BBCODE_MODE_INTERNAL: 
  603. $result = @call_user_func(Array($this, @$tag_rule['method']), BBCODE_CHECK,  
  604. $tag_name, $default_value, $params, $contents); 
  605. break; 
  606. case BBCODE_MODE_LIBRARY: 
  607. $result = @call_user_func(Array($this->defaults, @$tag_rule['method']), $this, BBCODE_CHECK,  
  608. $tag_name, $default_value, $params, $contents); 
  609. break; 
  610. case BBCODE_MODE_CALLBACK: 
  611. $result = @call_user_func(@$tag_rule['method'], $this, BBCODE_CHECK,  
  612. $tag_name, $default_value, $params, $contents); 
  613. break; 
  614. return $result; 
  615. case BBCODE_OUTPUT: 
  616. if ($this->plain_mode) { 
  617. if (!isset($tag_rule['plain_content'])) 
  618. $plain_content = Array('_content'); 
  619. else $plain_content = $tag_rule['plain_content']; 
  620. $result = $possible_content = ""; 
  621. foreach ($plain_content as $possible_content) { 
  622. if ($possible_content == '_content' 
  623. && strlen($contents) > 0) { 
  624. $result = $contents; 
  625. break; 
  626. if (isset($params[$possible_content]) 
  627. && strlen($params[$possible_content]) > 0) { 
  628. $result = htmlspecialchars($params[$possible_content]); 
  629. break; 
  630. $start = @$tag_rule['plain_start']; 
  631. $end = @$tag_rule['plain_end']; 
  632. if (isset($tag_rule['plain_link'])) { 
  633. $link = $possible_content = ""; 
  634. foreach ($tag_rule['plain_link'] as $possible_content) { 
  635. if ($possible_content == '_content' 
  636. && strlen($contents) > 0) { 
  637. $link = $this->UnHTMLEncode(strip_tags($contents)); 
  638. break; 
  639. if (isset($params[$possible_content]) 
  640. && strlen($params[$possible_content]) > 0) { 
  641. $link = $params[$possible_content]; 
  642. break; 
  643. $params = @parse_url($link); 
  644. if (!is_array($params)) $params = Array(); 
  645. $params['link'] = $link; 
  646. $params['url'] = $link; 
  647. $start = $this->FillTemplate($start, $params); 
  648. $end = $this->FillTemplate($end, $params); 
  649. return $start . $result . $end; 
  650. switch (@$tag_rule['mode']) { 
  651. default: 
  652. case BBCODE_MODE_SIMPLE: 
  653. $result = @$tag_rule['simple_start'] . $contents . @$tag_rule['simple_end']; 
  654. break; 
  655. case BBCODE_MODE_ENHANCED: 
  656. $result = $this->Internal_DoEnhancedTag($tag_rule, $params, $contents); 
  657. break; 
  658. case BBCODE_MODE_INTERNAL: 
  659. $result = @call_user_func(Array($this, @$tag_rule['method']), BBCODE_OUTPUT,  
  660. $tag_name, $default_value, $params, $contents); 
  661. break; 
  662. case BBCODE_MODE_LIBRARY: 
  663. $result = @call_user_func(Array($this->defaults, @$tag_rule['method']), $this, BBCODE_OUTPUT,  
  664. $tag_name, $default_value, $params, $contents); 
  665. break; 
  666. case BBCODE_MODE_CALLBACK: 
  667. $result = @call_user_func(@$tag_rule['method'], $this, BBCODE_OUTPUT,  
  668. $tag_name, $default_value, $params, $contents); 
  669. break; 
  670. return $result; 
  671. default: 
  672. return false; 
  673. function Internal_DoEnhancedTag($tag_rule, $params, $contents) { 
  674. $params['_content'] = $contents; 
  675. $params['_defaultcontent'] = strlen(@$params['_default']) ? $params['_default'] : $contents; 
  676. return $this->FillTemplate(@$tag_rule['template'], $params, @$tag_rule['default']); 
  677. function Internal_UpdateParamsForMissingEndTag(&$params) { 
  678. switch ($this->tag_marker) { 
  679. case '[': $tail_marker = ']'; break; 
  680. case '<': $tail_marker = '>'; break; 
  681. case '{': $tail_marker = '}'; break; 
  682. case '(': $tail_marker = ')'; break; 
  683. default: $tail_marker = $this->tag_marker; break; 
  684. $params['_endtag'] = $this->tag_marker . '/' . $params['_name'] . $tail_marker; 
  685. function Internal_ProcessIsolatedTag($tag_name, $tag_params, $tag_rule) { 
  686. if (!$this->DoTag(BBCODE_CHECK, $tag_name, @$tag_params['_default'], $tag_params, "")) { 
  687. $this->stack[] = Array( 
  688. BBCODE_STACK_TOKEN => BBCODE_TEXT,  
  689. BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text),  
  690. BBCODE_STACK_TAG => false,  
  691. BBCODE_STACK_CLASS => $this->current_class,  
  692. ); 
  693. return; 
  694. $this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_tag'], $this->stack); 
  695. $output = $this->DoTag(BBCODE_OUTPUT, $tag_name, @$tag_params['_default'], $tag_params, ""); 
  696. $this->Internal_CleanupWSByEatingInput(@$tag_rule['after_tag']); 
  697. $this->stack[] = Array( 
  698. BBCODE_STACK_TOKEN => BBCODE_TEXT,  
  699. BBCODE_STACK_TEXT => $output,  
  700. BBCODE_STACK_TAG => false,  
  701. BBCODE_STACK_CLASS => $this->current_class,  
  702. ); 
  703. function Internal_ProcessVerbatimTag($tag_name, $tag_params, $tag_rule) { 
  704. $state = $this->lexer->SaveState(); 
  705. $end_tag = $this->lexer->tagmarker . "/" . $tag_name . $this->lexer->end_tagmarker; 
  706. $start = count($this->stack); 
  707. $this->lexer->verbatim = true; 
  708. while (($token_type = $this->lexer->NextToken()) != BBCODE_EOI) { 
  709. if ($this->lexer->text == $end_tag) { 
  710. $end_tag_params = $this->lexer->tag; 
  711. break; 
  712. if ($this->output_limit > 0 
  713. && $this->text_length + strlen($this->lexer->text) >= $this->output_limit) { 
  714. $text = $this->Internal_LimitText($this->lexer->text,  
  715. $this->output_limit - $this->text_length); 
  716. if (strlen($text) > 0) { 
  717. $this->text_length += strlen($text); 
  718. $this->stack[] = Array( 
  719. BBCODE_STACK_TOKEN => BBCODE_TEXT,  
  720. BBCODE_STACK_TEXT => $this->FixupOutput($text),  
  721. BBCODE_STACK_TAG => false,  
  722. BBCODE_STACK_CLASS => $this->current_class,  
  723. ); 
  724. $this->Internal_DoLimit(); 
  725. break; 
  726. $this->text_length += strlen($this->lexer->text); 
  727. $this->stack[] = Array( 
  728. BBCODE_STACK_TOKEN => $token_type,  
  729. BBCODE_STACK_TEXT => htmlspecialchars($this->lexer->text),  
  730. BBCODE_STACK_TAG => $this->lexer->tag,  
  731. BBCODE_STACK_CLASS => $this->current_class,  
  732. ); 
  733. $this->lexer->verbatim = false; 
  734. if ($token_type == BBCODE_EOI) { 
  735. $this->lexer->RestoreState($state); 
  736. $this->stack[] = Array( 
  737. BBCODE_STACK_TOKEN => BBCODE_TEXT,  
  738. BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text),  
  739. BBCODE_STACK_TAG => false,  
  740. BBCODE_STACK_CLASS => $this->current_class,  
  741. ); 
  742. return; 
  743. $newstart = $this->Internal_CleanupWSByIteratingPointer(@$tag_rule['after_tag'], $start, $this->stack); 
  744. $this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_endtag'], $this->stack); 
  745. $this->Internal_CleanupWSByEatingInput(@$tag_rule['after_endtag']); 
  746. $content = $this->Internal_CollectText($this->stack, $newstart); 
  747. array_splice($this->stack, $start); 
  748. $this->Internal_ComputeCurrentClass(); 
  749. $this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_tag'], $this->stack); 
  750. $tag_params['_endtag'] = $end_tag_params['_tag']; 
  751. $tag_params['_hasend'] = true; 
  752. $output = $this->DoTag(BBCODE_OUTPUT, $tag_name,  
  753. @$tag_params['_default'], $tag_params, $content); 
  754. $this->stack[] = Array( 
  755. BBCODE_STACK_TOKEN => BBCODE_TEXT,  
  756. BBCODE_STACK_TEXT => $output,  
  757. BBCODE_STACK_TAG => false,  
  758. BBCODE_STACK_CLASS => $this->current_class,  
  759. ); 
  760. function Internal_ParseStartTagToken() { 
  761. $tag_params = $this->lexer->tag; 
  762. $tag_name = @$tag_params['_name']; 
  763. if (!isset($this->tag_rules[$tag_name])) { 
  764. $this->stack[] = Array( 
  765. BBCODE_STACK_TOKEN => BBCODE_TEXT,  
  766. BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text),  
  767. BBCODE_STACK_TAG => false,  
  768. BBCODE_STACK_CLASS => $this->current_class,  
  769. ); 
  770. return; 
  771. $tag_rule = $this->tag_rules[$tag_name]; 
  772. $allow_in = is_array($tag_rule['allow_in']) 
  773. ? $tag_rule['allow_in'] : Array($this->root_class); 
  774. if (!in_array($this->current_class, $allow_in)) { 
  775. if (!$this->Internal_RewindToClass($allow_in)) { 
  776. $this->stack[] = Array( 
  777. BBCODE_STACK_TOKEN => BBCODE_TEXT,  
  778. BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text),  
  779. BBCODE_STACK_TAG => false,  
  780. BBCODE_STACK_CLASS => $this->current_class,  
  781. ); 
  782. return; 
  783. $end_tag = isset($tag_rule['end_tag']) ? $tag_rule['end_tag'] : BBCODE_REQUIRED; 
  784. if ($end_tag == BBCODE_PROHIBIT) { 
  785. $this->Internal_ProcessIsolatedTag($tag_name, $tag_params, $tag_rule); 
  786. return; 
  787. if (!$this->DoTag(BBCODE_CHECK, $tag_name, @$tag_params['_default'], $tag_params, "")) { 
  788. $this->stack[] = Array( 
  789. BBCODE_STACK_TOKEN => BBCODE_TEXT,  
  790. BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text),  
  791. BBCODE_STACK_TAG => false,  
  792. BBCODE_STACK_CLASS => $this->current_class,  
  793. ); 
  794. return; 
  795. if (@$tag_rule['content'] == BBCODE_VERBATIM) { 
  796. $this->Internal_ProcessVerbatimTag($tag_name, $tag_params, $tag_rule); 
  797. return; 
  798. if (isset($tag_rule['class'])) 
  799. $newclass = $tag_rule['class']; 
  800. else $newclass = $this->root_class; 
  801. $this->stack[] = Array( 
  802. BBCODE_STACK_TOKEN => $this->lexer->token,  
  803. BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text),  
  804. BBCODE_STACK_TAG => $this->lexer->tag,  
  805. BBCODE_STACK_CLASS => ($this->current_class = $newclass),  
  806. ); 
  807. if (!isset($this->start_tags[$tag_name])) 
  808. $this->start_tags[$tag_name] = Array(count($this->stack)-1); 
  809. else $this->start_tags[$tag_name][] = count($this->stack)-1; 
  810. function Internal_ParseEndTagToken() { 
  811. $tag_params = $this->lexer->tag; 
  812. $tag_name = @$tag_params['_name']; 
  813. $contents = $this->Internal_FinishTag($tag_name); 
  814. if ($contents === false) { 
  815. if (@$this->lost_start_tags[$tag_name] > 0) { 
  816. $this->lost_start_tags[$tag_name]--; 
  817. else { 
  818. $this->stack[] = Array( 
  819. BBCODE_STACK_TOKEN => BBCODE_TEXT,  
  820. BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text),  
  821. BBCODE_STACK_TAG => false,  
  822. BBCODE_STACK_CLASS => $this->current_class,  
  823. ); 
  824. return; 
  825. $start_tag_node = array_pop($this->stack); 
  826. $start_tag_params = $start_tag_node[BBCODE_STACK_TAG]; 
  827. $this->Internal_ComputeCurrentClass(); 
  828. $this->Internal_CleanupWSByPoppingStack(@$this->tag_rules[$tag_name]['before_tag'], $this->stack); 
  829. $start_tag_params['_endtag'] = $tag_params['_tag']; 
  830. $start_tag_params['_hasend'] = true; 
  831. $output = $this->DoTag(BBCODE_OUTPUT, $tag_name, @$start_tag_params['_default'],  
  832. $start_tag_params, $contents); 
  833. $this->Internal_CleanupWSByEatingInput(@$this->tag_rules[$tag_name]['after_endtag']); 
  834. $this->stack[] = Array( 
  835. BBCODE_STACK_TOKEN => BBCODE_TEXT,  
  836. BBCODE_STACK_TEXT => $output,  
  837. BBCODE_STACK_TAG => false,  
  838. BBCODE_STACK_CLASS => $this->current_class,  
  839. ); 
  840. function Parse($string) { 
  841. $this->lexer = new BBCodeLexer($string, $this->tag_marker); 
  842. $this->lexer->debug = $this->debug; 
  843. $old_output_limit = $this->output_limit; 
  844. if ($this->output_limit > 0) { 
  845. if (strlen($string) < $this->output_limit) { 
  846. $this->output_limit = 0; 
  847. else if ($this->limit_precision > 0) { 
  848. $guess_length = $this->lexer->GuessTextLength(); 
  849. if ($guess_length < $this->output_limit * ($this->limit_precision + 1.0)) { 
  850. $this->output_limit = 0; 
  851. else { 
  852. $this->stack = Array(); 
  853. $this->start_tags = Array(); 
  854. $this->lost_start_tags = Array(); 
  855. $this->text_length = 0; 
  856. $this->was_limited = false; 
  857. if (strlen($this->pre_trim) > 0) 
  858. $this->Internal_CleanupWSByEatingInput($this->pre_trim); 
  859. $newline = $this->plain_mode ? "\n" : "<br />\n"; 
  860. while (true) { 
  861. if (($token_type = $this->lexer->NextToken()) == BBCODE_EOI) { 
  862. break; 
  863. switch ($token_type) { 
  864. case BBCODE_TEXT: 
  865. if ($this->output_limit > 0 
  866. && $this->text_length + strlen($this->lexer->text) >= $this->output_limit) { 
  867. $text = $this->Internal_LimitText($this->lexer->text,  
  868. $this->output_limit - $this->text_length); 
  869. if (strlen($text) > 0) { 
  870. $this->text_length += strlen($text); 
  871. $this->stack[] = Array( 
  872. BBCODE_STACK_TOKEN => BBCODE_TEXT,  
  873. BBCODE_STACK_TEXT => $this->FixupOutput($text),  
  874. BBCODE_STACK_TAG => false,  
  875. BBCODE_STACK_CLASS => $this->current_class,  
  876. ); 
  877. $this->Internal_DoLimit(); 
  878. break 2; 
  879. $this->text_length += strlen($this->lexer->text); 
  880. $this->stack[] = Array( 
  881. BBCODE_STACK_TOKEN => BBCODE_TEXT,  
  882. BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text),  
  883. BBCODE_STACK_TAG => false,  
  884. BBCODE_STACK_CLASS => $this->current_class,  
  885. ); 
  886. break; 
  887. case BBCODE_WS: 
  888. if ($this->output_limit > 0 
  889. && $this->text_length + strlen($this->lexer->text) >= $this->output_limit) { 
  890. $this->Internal_DoLimit(); 
  891. break 2; 
  892. $this->text_length += strlen($this->lexer->text); 
  893. $this->stack[] = Array( 
  894. BBCODE_STACK_TOKEN => BBCODE_WS,  
  895. BBCODE_STACK_TEXT => $this->lexer->text,  
  896. BBCODE_STACK_TAG => false,  
  897. BBCODE_STACK_CLASS => $this->current_class,  
  898. ); 
  899. break; 
  900. case BBCODE_NL: 
  901. if ($this->ignore_newlines) { 
  902. if ($this->output_limit > 0 
  903. && $this->text_length + 1 >= $this->output_limit) { 
  904. $this->Internal_DoLimit(); 
  905. break 2; 
  906. $this->text_length += 1; 
  907. $this->stack[] = Array( 
  908. BBCODE_STACK_TOKEN => BBCODE_WS,  
  909. BBCODE_STACK_TEXT => "\n",  
  910. BBCODE_STACK_TAG => false,  
  911. BBCODE_STACK_CLASS => $this->current_class,  
  912. ); 
  913. else { 
  914. $this->Internal_CleanupWSByPoppingStack("s", $this->stack); 
  915. if ($this->output_limit > 0 
  916. && $this->text_length + 1 >= $this->output_limit) { 
  917. $this->Internal_DoLimit(); 
  918. break 2; 
  919. $this->text_length += 1; 
  920. $this->stack[] = Array( 
  921. BBCODE_STACK_TOKEN => BBCODE_NL,  
  922. BBCODE_STACK_TEXT => $newline,  
  923. BBCODE_STACK_TAG => false,  
  924. BBCODE_STACK_CLASS => $this->current_class,  
  925. ); 
  926. $this->Internal_CleanupWSByEatingInput("s"); 
  927. break; 
  928. case BBCODE_TAG: 
  929. $this->Internal_ParseStartTagToken(); 
  930. break; 
  931. case BBCODE_ENDTAG: 
  932. $this->Internal_ParseEndTagToken(); 
  933. break; 
  934. default: 
  935. break; 
  936. if (strlen($this->post_trim) > 0) 
  937. $this->Internal_CleanupWSByPoppingStack($this->post_trim, $this->stack); 
  938. $result = $this->Internal_GenerateOutput(0); 
  939. $result = $this->Internal_CollectTextReverse($result, count($result) - 1); 
  940. $this->output_limit = $old_output_limit; 
  941. if ($this->plain_mode) { 
  942. $result = preg_replace("/[\\x00-\\x09\\x0B-\\x20]+/", " ", $result); 
  943. $result = preg_replace("/(?:[\\x20]*\\n) {2, }[\\x20]*/", "\n\n", $result); 
  944. $result = trim($result); 
  945. return $result;