<?php /* Copyright (C) 2009 Winch Gate Property Limited * * This file is part of ryzom_api. * ryzom_api is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ryzom_api is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with ryzom_api. If not, see <http://www.gnu.org/licenses/>. */ // setup bbCode formatter bbCode::$ig = RYZOM_IG; /** * Image proxy */ if(!defined('IMG_PROXY')){ $url = 'http://'.$_SERVER['HTTP_HOST'].'/app_forum/tools/imageproxy.php'; define('IMG_PROXY', $url); } if (!function_exists('proxy_image_url')) { function proxy_image_url($href, $attrs=''){ return IMG_PROXY.'?'.($attrs != '' ? $attrs.'&' : '').'url='.urlencode($href); } } abstract class bbCodeParser { /** * @var bool */ private $_ig; /** * @var array */ private $tags_ignore; private $tags_block_open; private $tags_block_close; private $tags_ignore_depth; /** * @var array */ private $open_tags; /** * @var string */ private $last_closed_tag; /** * @var int */ private $current_tag; /** * @var array */ private $state; /** * @param bool $ig if true, use ingame markup */ function __construct($ig) { $this->_ig = $ig; // ignore bbcode between these tags $this->tags_ignore = array( 'noparse', 'code', 'url', 'img', 'mail', 'page', 'forum', 'topic', 'post', 'wiki', 'time', 'date' ); // these create block level html code, so '\n' or ' ' or '\t' around them needs to be cleared $this->tags_block_open = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'quote', 'list', 'p'); $this->tags_block_close = $this->tags_block_open; if ($this->_ig) { // ingame <p> is not block level when closing, so dont strip there $key = array_search('p', $this->tags_block_close, true); unset($this->tags_block_close[$key]); } $this->state = array(); // reset internals $this->reset(); } /** * Format bbcode tag * * @param string $tag tag name * @param string $open open markup * @param string $close close markup * @param string $attr tag attributes * @param string $text text between tags */ abstract function format($tag, $open, $close, $attr, $text); /** * Wrapper to call Child->format(...) * * @param array $tag assoc array with tag info * @return string */ function handle_tag($tag) { return $this->format($tag['tag'], $tag['open'], $tag['close'], $tag['attr'], $tag['text']); } /** * Reset internals */ function reset() { $this->current_tag = 0; $this->tags_ignore_depth = 0; // 0'th position is used as result $this->open_tags = array( 0 => array('tag' => '', 'open' => '', 'close' => '', 'attr' => '', 'text' => '') ); $this->last_closed_tag = false; } /** * Save working state */ private function _state_save() { $this->state[] = array($this->current_tag, $this->tags_ignore_depth, $this->open_tags, $this->last_closed_tag); $this->reset(); } /** * Restore working state */ private function _state_restore() { if (!empty($this->state)) { list($this->current_tag, $this->tags_ignore_depth, $this->open_tags, $this->last_closed_tag) = array_pop($this->state); } } /** * Main worker. Parse $text for bbCode tags * * NOTE: Text must already be safe for HTML, ie. treated with htmlspecialchars() * * @param string $text * @return string formatted string */ function bbcode($text) { $text = str_replace("\r\n", "\n", $text); $split = preg_split('/(\[[a-zA-Z0-9_\/]*?(?:[= ].*?)?\])/', $text, null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); foreach ($split as $chunk) { if (substr($chunk, 0, 1) == '[' && substr($chunk, -1, 1) == ']') { if (substr($chunk, 0, 2) == '[/') { $this->close($chunk); } else { $this->open($chunk); } } else { $this->text($chunk); } } return $this->result(); } /** * Push tag with args to stack * Do not strip whitespace because tag might be invalid * * @param string $chunk full tag string, eg. [tag=attr] */ function open($chunk) { list($tag, $attr) = $this->split_params($chunk); // test for [noparse] if ($this->tags_ignore_depth > 0) { $this->text($chunk); } else { $this->current_tag++; // remember tag, attributes and complete string that was used in markup $this->open_tags[$this->current_tag] = array('tag' => $tag, 'attr' => $attr, 'open' => $chunk, 'close' => '', 'text' => ''); } if (in_array($tag, $this->tags_ignore)) { $this->tags_ignore_depth++; } } /** * Close tag and call tag handler to format output * * @param $chunk full tag string, eg. [/tag] */ function close($chunk) { // extract tag name from [/name] $tag = strtolower(substr($chunk, 2, -1)); if ($this->tags_ignore_depth > 0 && in_array($tag, $this->tags_ignore)) { $this->tags_ignore_depth--; } // stack underrun if ($this->current_tag < 0) { $this->text($chunk); return; } // ignore block if ($this->tags_ignore_depth > 0) { $this->text($chunk); return; } // tag mismatch if ($this->open_tags[$this->current_tag]['tag'] !== $tag) { // try to find first open tag for this $key = false; for ($i = $this->current_tag - 1; $i > 0; $i--) { if (isset($this->open_tags[$i]['tag']) && $this->open_tags[$i]['tag'] === $tag) { $key = $i; break; } } if ($key === false) { $this->text($chunk); return; } // tag is open so we need to 'rewind' a bit for ($i = $this->current_tag; $i > $key; $i--) { $tmp_tag = $this->pop_stack(); $this->text($tmp_tag['open'] . $tmp_tag['text']); } } // close tag $open = $this->pop_stack(); // handle bbcode $open['close'] = $chunk; $block_level = false; if (in_array($tag, $this->tags_block_open)) { $block_level = true; // for block level element, trim whitespace from inside tag // [tag]<ws>...text...<ws>[/tag] $open['text'] = $this->trim_ws($open['text']); } $result = $this->handle_tag($open); // strip whitespace from text before tag 'text...<ws>[tag]' if ($block_level) { $ts = $this->rtrim_ws($this->open_tags[$this->current_tag]['text']); $this->open_tags[$this->current_tag]['text'] = $ts; } $this->text($result); $this->last_closed_tag = $open['tag']; } function text($text) { // strip whitespace after closing '[/tag]<ws>...text' if (in_array($this->last_closed_tag, $this->tags_block_close)) { $text = $this->ltrim_ws($text); } $this->open_tags[$this->current_tag]['text'] .= $text; $this->last_closed_tag = false; } function result() { // close tags that are still open while ($this->current_tag > 0) { $open = $this->pop_stack(); if ($this->tags_ignore_depth > 0) { $this->tags_ignore_depth--; // need to reparse text that's after ignore tag $this->_state_save(); $text = $open['open'] . $this->bbcode($open['text']); $this->_state_restore(); } else { // tag was not closed proprely, include start tag with result $text = $open['open'] . $open['text']; } $this->text($text); }; return $this->open_tags[0]['text']; } /** * Pop tag and text from stack and return them * * @return array [0] = tag, [1] = text */ function pop_stack() { // remove from stack $open = $this->open_tags[$this->current_tag]; unset($this->open_tags[$this->current_tag]); $this->current_tag--; return $open; } /** * Trim from end of string * 'text...\s{0,}\n{1}\s{0,}' * * @param string $ts * @return string */ function rtrim_ws($ts){ // we want to get rid of all spaces/tabs, but only single \n, so rtrim($ts, " \t\n\r") would not work $ts = rtrim($ts, " \t"); if (substr($ts, -1, 1) === "\n") { $ts = substr($ts, 0, -1); $ts = rtrim($ts, " \t"); } return $ts; } /** * Trim from start of string * '\s{0,}\n{1}...text' * * @param string $ts * @return string */ function ltrim_ws($ts){ // we want to get rid of all spaces/tabs, but only single \n, so ltrim($ts, " \t\n\r") would not work $ts = ltrim($ts, " \t"); if (substr($ts, 0, 1) === "\n") { $ts = substr($ts, 1); } return $ts; } /** * Trim from both sides * '\s{0,}\n{1}...text...\s{0,}\n{1}\s{0,} * * @param string $ts * @return string */ function trim_ws($ts){ $ts = $this->ltrim_ws($ts); $ts = $this->rtrim_ws($ts); return $ts; } /** * Extract tag parameters from [tag=params] or [tag key1=val1 key2=val2] * * @param type $tag * @return type */ function split_params($chunk) { if (substr($chunk, 0, 1) == '[') { $b = '\['; $e = '\]'; } else { $b = ''; $e = ''; } // [1] [2] [3] if (preg_match('/^' . $b . '([\*a-zA-Z0-9]*?)' . '(=| )' . '(.*?)' . $e . '$/', $chunk, $match)) { $tagName = strtolower($match[1]); if ($match[2] == '=') { // = means single parameter $tagParam = $match[3]; } else { // <space> means multiple parameters $tagParam = array(); $args = preg_split('/[ ]/', $match[3], null, PREG_SPLIT_NO_EMPTY); foreach ($args as $arg) { $pairs = explode('=', $arg); // preg_replace will remove possible quotes around value if (isset($pairs[1])) { $tagParam[strtolower($pairs[0])] = preg_replace('@("|\'|)(.*?)\\1@', '$2', $pairs[1]); } else { $tagParam[] = preg_replace('@("|\'|)(.*?)\\1@', '$2', $pairs[0]); } } } } else { if (substr($chunk, 0, 1) == '[' && substr($chunk, -1, 1) == ']') { $chunk = substr($chunk, 1, -1); } $tagName = strtolower($chunk); $tagParam = ''; } return array($tagName, $tagParam); } } class bbCode extends bbCodeParser { static $legacy_sync = 1348956841; static $legacy_shard = array( 'ani' => 2363920179, 'lea' => 2437578274, 'ari' => 2358620001, ); static $ig = false; static $timezone = 'UTC'; static $clock12h = false; static $shardid = false; static $lang = 'en'; static $disabledTags = array(); // const COLOR_P = '#d0d0d0'; // normal text // const COLOR_BBCODE_TAG = '#444444'; static function bbDisabled($tag) { return in_array(strtolower($tag), self::$disabledTags); } static function getFontSize($value) { $size = 16; switch (strtolower($value)) { case '1': case 'xx-small': $size = 9; break; case '2': case 'x-small' : $size = 10; break; case '3': case 'small' : $size = 13; break; case '4': case 'medium' : $size = 16; break; case '5': case 'large' : $size = 18; break; case '6': case 'x-large' : $size = 24; break; case '7': case 'xx-large': $size = 32; break; //case '8': case 'smaller' : break; //case '9': case 'larger' : break; } return $size; } static function bb_noparse($code) { return preg_replace(array('/\[/', '/\]/'), array('[', ']'), $code); } static function bb_code($code) { return '<pre>' . self::bb_noparse($code) . '</pre>'; } static function bb_list($list) { $result = ''; $list = str_replace("\n[", '[', $list); $result = '<ul>' . preg_replace('/\s*\[\*\]\s*/is', "</li><li>", $list) . '</li></ul>'; return preg_replace('#<ul>\s*</li>#is', '<ul>', $result); } static function bb_quote($author, $text) { if (self::$ig) { // prevents [color] tag to take over color $author = self::bb_color(self::COLOR_P, $author); $text = self::bb_color(self::COLOR_P, $text); // left/right border, top/bottom border height $l = '<td width="1" bgcolor="#888888" height="1"></td>'; $r = '<td width="1" bgcolor="#888888" height="1"></td>'; return // 98% gives bit padding on the right '<table width="98%" cellpadding="0" cellspacing="0" border="0">' . '<tr><td width="1" height="5"></td><td></td><td></td></tr>' . // top padding - no border '<tr>' . $l . '<td bgcolor="#888888"></td>' . $r . '</tr>' . // top border '<tr>' . $l . '<td bgcolor="#000000" l_margin="5" height="3"></td>' . $r . '</tr>' . // author top padding '<tr>' . $l . '<td bgcolor="#000000" l_margin="5">' . $author . '</td>' . $r . '</tr>' . // author '<tr>' . $l . '<td bgcolor="#000000" l_margin="5" height="2"></td>' . $r . '</tr>' . // author bottom padding '<tr>' . $l . '<td bgcolor="#555555" l_margin="10" height="3"></td>' . $r . '</tr>' . // quote top padding '<tr>' . $l . '<td bgcolor="#555555" l_margin="10">' . $text . '</td>' . $r . '</tr>' . // quote '<tr>' . $l . '<td bgcolor="#555555" l_margin="10" height="2"></td>' . $r . '</tr>' . // quote bottom padding '<tr>' . $l . '<td bgcolor="#888888"></td>' . $r . '</tr>' . // bottom border '<tr><td width="1" height="8"></td><td></td><td></td></tr>' . // bottom padding - no border '</table>'; } else { return '' . '<div class="post-quote">' . '<cite>' . $author . '</cite>' . '<blockquote>' . $text . '</blockquote>' . '</div>'; } } static function bb_h($nr, $color, $text) { $tag = 'h' . $nr; if (self::$ig) { if ($color != '') { $text = '<font color="' . $color . '">' . $text . '</font>'; } return '<' . $tag . '>' . $text . '</' . $tag . '>'; } else { if ($color != '') { $style = ' style="color: ' . $color . ';"'; } else { $style = ''; } return '<' . $tag . $style . '>' . $text . '</' . $tag . '>'; } } static function bb_url($href, $text) { // "http://..../" remove " if present if (substr($href, 0, 6) == '"') { if (substr($href, -6) == '"') { $href = substr($href, 6, -6); } else { $href = substr($href, 6); } } if ($href == '') $href = $text; if ($text == '') { $text = $href; $text = wordwrap($text, 65, ' ', true); } $disable = self::bbDisabled('url'); // if not disabled and in ryzom and is proper url (<scheme>://<host>/) if (!$disable && self::$ig) { $url = @parse_url(strtolower($href)); $disable = true; if (!empty($url['scheme']) && !empty($url['host'])) { if (in_array($url['scheme'], array('http', 'https'))) { if (in_array($url['host'], array('app.ryzom.com'))) { if (empty($url['query']) || stripos($url['query'], 'redirect') === false) { // http://atys.ryzom.com/ // and does not contain redirect // - allow url in game browser $disable = false; } } } } // !empty }// isRYZOM if ($disable) { // empty href will give proper link color without 'underline' - perfect for 'disabled' links if ($href == '') { $text = '<a href="">' . $text . '</a>'; } else { $href = wordwrap($href, 65, ' ', true); $text = wordwrap($text, 65, ' ', true); $text = '<a href="">' . $text . '</a> ' . self::bb_color(self::COLOR_BBCODE_TAG, '(' . $href . ')'); } return $text; } // make sure http:// (or ftp:// or mailto:// etc is present), if not, add it if (!preg_match('#://#', $href)) { $href = 'http://' . $href; } return sprintf('<a href="%s"' . (self::$ig ? '' : ' target="_blank"') . '>%s</a>', $href, $text); } static function bb_img($attr, $href) { if (self::bbDisabled('img')) { return self::bb_noparse('[img]' . $href . '[/img]'); } // $href is treated with htmlspecialchars() so any & in url is & $href = str_replace('&', '&', $href); // images from current server directly if ($attr=='src' || strstr($href, $_SERVER['HTTP_HOST']) !== false){ return '<img src="' . $href . '" />'; } $url = proxy_image_url($href); return '<a href="' . $url . '&no_proxy=1"><img src="' . $url . '" /></a>'; } static function bb_banner($lang, $ckey) { // $lang and $ckey should already be escaped for HTML, so urlencode() in here would double escape them // - channel it thru image proxy. proxy does caching better and uses '304 Not Modified' status $src = 'http://atys.ryzom.com/api/banner.php?ckey=' . $ckey . '&langid=' . $lang . '&size=500'; return self::bb_img('', $src); } static function bb_mail($user) { $url = 'http://' . $_SERVER['HTTP_HOST'] . '/app_mail/?page=compose/to/' . urlencode($user); return '<a href="' . $url . '">' . $user . '</a>'; } static function bb_profile($ptype, $pname) { // types from app_profile $types = array('user', 'player', 'npc', 'fauna', 'entity', 'source'); $ptype = array_search($ptype, $types, true); // if type not found, then fall back to player if ($ptype === false) $ptype = 1; $url = 'http://' . $_SERVER['HTTP_HOST'] . '/app_profile/?ptype=' . intval($ptype) . '&pname=' . urlencode($pname); return '<a href="' . $url . '">' . $pname . '</a>'; } static function bb_color($color, $str) { if ($color == '') { return $str; } if (self::$ig) { return '<font color="' . $color . '">' . $str . '</font>'; } else { return '<span style="color: ' . $color . ';">' . $str . '</span>'; } } static function bb_size($size, $str) { $size = self::getFontSize($size); if (self::$ig) { return '<font size="' . $size . 'px">' . $str . '</font>'; } else { return '<span style="font-size: ' . $size . 'px;">' . $str . '</span>'; } } static function bb_pre($str) { return '<pre>' . $str . '</pre>'; } static function bb_p($str) { return '<p>' . $str . '</p>'; } // Added by ulukyn. WebIg compatibility. static function bb_center($str) { if (self::$ig) { return '<table width="100%" cellpadding="0" cellspacing="0"><tr><td align="center" valign="middle">' . $str . '</td></tr></table>'; } else { return '<div style="text-align: center;">' . $str . '</div>'; } } /** Table format : (added by ulukyn) * A1| A2|A3 * B1| B2 |B3 * C1|C2 |C3 */ static function bb_table($attr, $content) { $width = isset($attr['width'])?$attr['width']:'100%'; $border = isset($attr['border'])?$attr['border']:'0'; $bgcolor = isset($attr['bgcolor'])?' bgcolor="'.$attr['bgcolor'].'" ':''; $ret = '<table width="'.$width.'" border="'.$border.'" cellpadding="0" cellspacing="0" '.$bgcolor.' >'; $lines = explode("\n", $content); foreach ($lines as $line) { if ($line) { $ret .= '<tr>'; $cols = explode('|', $line); foreach ($cols as $text) { if (!$text) continue; $params = array('valign' => 'middle'); if ($text[0] == '#') { $paramsdef = explode(' ', $text); $paramlist = substr(array_shift($paramsdef), 1); $paramlist = explode(',', $paramlist); foreach ($paramlist as $p) { list($name, $value) = explode('=', $p); $params[ _h(str_replace('"', '', $name))] = _h(str_replace('"', '', $value)); } if ($paramsdef) $text = implode(' ', $paramsdef); } $param_html = ''; foreach ($params as $name => $value) $param_html .= $name.'="'.$value.'" '; if ($text && $text[0] == ' ' && $text[strlen($text)-1] == ' ') $align = 'center'; else if ($text && $text[0] == ' ') $align = 'right'; else $align = 'left'; $ret .= '<td '.$param_html.' align="'.$align.'">'.$text.'</td>'; } $ret .= '</tr>'; } } $ret .= '</table>'; return $ret; } static function bb_page_link($page, $txt) { if ($page == '') { $page = $txt; } $tmp = explode('/', $page); foreach ($tmp as $k => $v) { $tmp[$k] = urlencode($v); } $url = 'http://' . $_SERVER['HTTP_HOST'] . '/app_forum/?page=' . join('/', $tmp); return '<a href="' . $url . '">' . $txt . '</a>'; } static function bb_forum_link($page, $id, $txt) { $page = $page . '/view/' . $id; if ($id == '') { $page.= $txt; } return self::bb_page_link($page, $txt); } // Added by Ulukyn static function bb_wiki_link($page, $txt) { $need_new_txt = false; if ($page == '') { $page = $txt; $need_new_txt = true; } if (substr($page, 0, 22) == 'http://atys.ryzom.com/') $url = 'http://atys.ryzom.com/start/app_wiki.php?page=' . substr($page, 21); else { $tmp = explode('/', $page); if (count($tmp) != 2) { return 'Syntax: [wiki]/[page], ex: en/Chronicles'; } else { $wiki = $tmp[0]; $page = $tmp[1]; } if (self::$ig) { $url = 'http://atys.ryzom.com/start/app_wiki.php?page=/projects/pub' . $wiki . '/wiki/' . $page; } else $url = 'http://atys.ryzom.com/projects/pub' . $wiki . '/wiki/' . $page; if ($need_new_txt) $txt = 'WIKI [' . $page . ']'; } return '<a href="' . $url . '"' . (self::$ig ? '' : ' target="_blank"') . '>' . $txt . '</a>'; } static function bb_biu($tag, $txt) { $tag = strtolower($tag); if (self::$ig) { switch ($tag) { // FIXME: darken/lighter or tint current color case 'b': $txt = self::bb_color('white', $txt); break; case 'i': $txt = self::bb_color('#ffffd0', $txt); break; case 'u': $txt = '<a href="ah:">' . self::bb_color(self::COLOR_P, $txt) . '</a>'; break; default : $txt = self::bb_color(self::COLOR_BBCODE_TAG, $txt); break; // fallback } return $txt; } switch ($tag) { case 'b': $tag = 'strong'; break; case 'i': $tag = 'em'; break; case 'u': $tag = 'u'; break; default: $tag = 'span'; // fallback } return '<' . $tag . '>' . $txt . '</' . $tag . '>'; } static function bb_date($attr, $txt) { $time = strtotime($txt); $shardid = isset($attr['shard']) ? $attr['shard'] : self::$shardid; if ($time === false || $shardid === false) return 'ERR:[' . $txt . ']'; if (isset(self::$legacy_shard[$shardid])) { $tick = self::$legacy_shard[$shardid]; if (self::$legacy_sync > $time) { // only modify game cycle when asked time is before sync $tick = ($time - self::$legacy_sync) * 10 + $tick; } } else { $tick = ryzom_time_tick($shardid); // tick is for NOW, adjust it to match time given $now = time(); $tick = ($time - $now) * 10 + $tick; } $rytime = ryzom_time_array($tick, $shardid); $txt = ryzom_time_txt($rytime, self::$lang); return $txt; } static function bb_lang($attr, $txt) { if (_user()->lang == $attr) return $txt; else return ''; } static function bb_time($options, $txt) { $time = strtotime($txt); if ($time == 0) { return $txt; } $timezone = self::$timezone; $show_time = ''; $show_date = ''; $show_timer = ''; if (is_array($options)) { foreach ($options as $key => $val) { switch ($key) { case 'timezone': // fix some timezones for php switch ($val) { case 'pst': // fall thru case 'pdt': $val = 'US/Pacific'; break; } $timezone = $val; break; case 'date' : $show_date = $val == 'off' ? false : $val; break; case 'time' : $show_time = $val == 'off' ? false : $val; break; case 'timer': $show_timer = $val == 'off' ? false : $val; break; }//switch }//foreach } $ret = array(); $old_timezone = date_default_timezone_get(); @date_default_timezone_set($timezone); if ($show_date !== false) { $date = ryzom_absolute_time($time); //ryzom_absolute_time does not have year, so we need to add it $current_y = date('Y', time()); $y = date('Y', $time); if ($y != $current_y) { $date.= ' ' . $y; } $ret[] = self::bb_color($show_date, $date); } if ($show_time !== false) { $fmtTime = self::$clock12h ? 'g:i:s a T' : 'H:i:s T'; $ret[] = self::bb_color($show_time, date($fmtTime, $time)); } date_default_timezone_set($old_timezone); if ($show_timer !== false) { if ($show_time === false && $show_date === false) { $f = '%s'; } else { $f = '(%s)'; } $ret[] = self::bb_color($show_timer, sprintf($f, ryzom_relative_time($time))); } return join(' ', $ret); } /** * This function is called by bbCodeParser class * * @see bbCodeParser::format */ public function format($tag, $open, $close, $attr, $text) { // silly functions all have different parameters switch ($tag) { case 'noparse' : $result = self::bb_noparse($text); break; case 'code' : $result = self::bb_code($text); break; case 'quote' : $result = self::bb_quote($attr, $text); break; case 'h1' : // fall thru case 'h2' : // fall thru case 'h3' : // fall thru case 'h4' : // fall thru case 'h5' : // fall thru case 'h6' : $nr = (int) substr($tag, -1); $color = isset($attr['color']) ? $attr['color'] : ''; $result = self::bb_h($nr, $color, $text); break; case 'color' : $result = self::bb_color($attr, $text); break; case 'size' : $result = self::bb_size($attr, $text); break; case 'list' : $result = self::bb_list($text); break; case 'img' : $result = self::bb_img($attr, $text); break; case 'banner' : $result = self::bb_banner($attr, $text); break; case 'pre' : $result = self::bb_pre($text); break; case 'p' : $result = self::bb_p($text); break; case 'table' : $result = self::bb_table($attr, $text); break; case 'center' : $result = self::bb_center($text); break; case 'url' : $result = self::bb_url($attr, $text); break; case 'mail' : $result = self::bb_mail($text); break; case 'profile' : $result = self::bb_profile($attr, $text); break; case 'page' : $result = self::bb_page_link($attr, $text); break; case 'forum' : // fall thru case 'topic' : // fall thru case 'post' : $result = self::bb_forum_link($tag, $attr, $text); break; case 'wiki' : $result = self::bb_wiki_link($attr, $text); break; case 'b' : // fall thru case 'i' : // fall thru case 'u' : $result = self::bb_biu($tag, $text); break; case 'time' : $result = self::bb_time($attr, $text); break; case 'date' : $result = self::bb_date($attr, $text); break; case 'lang' : $result = self::bb_lang($attr, $text); break; default : $result = $open . $text . $close; break; } return $result; } /** * Replaces some BBcode with HTML code * * NOTE: $text should be already escaped for HTML * * @param string $text html escaped input text * @param array $disabledTags */ static function parse($text, $disabledTags = array()) { static $parser = null; if ($parser === null) { $parser = new self(self::$ig); } $parser->reset(); self::$disabledTags = $disabledTags; return $parser->bbcode($text); } }