array( 'expression' => "/^\{\{\{(.*?)\}\}\}/ims", 'tags' => array(), 'token' => array('{{{', '}}}') ), 'header' => array( 'expression' => "/^\ *(={1,6})\ *(.+?)=*\ *$/ims", 'tags' => array(), //none 'token' => '=' ), 'table' => array( 'expression' => "/^(?:\|.*?\|\ *\n)+/ims" ), 'line_break' => array( 'expression' => "/^----\s*$/im", 'token' => '----', 'tags' => array() ), 'list' => array( 'expression' => "/((?:^\ *[\*#][^\*#]\ *.+?)(?:^\ *[\*#]{1,5}\ *.+?)*)(\n\s*(?:\n|<(?:h\d|pre|table|tbody|thead|tr|th|td|ul|li|ol|hr)))/ims", 'tags' => array(), 'token' => array('*', '#') ), 'paragraph' => array( 'expression' => "/^\ *((?:<(?!\ *\/?(?:h\d|pre|table|tbody|thead|tr|th|td|ul|li|ol|hr)\ *\/?>)|[^<\s]).+?)\n\s*\n/ims", //not specified -> all tags (null or unset) 'tag' => 'p' ) ); protected $tagrules = array( 'nowiki' => array( 'expression' => "/\{\{\{(.*?)\}\}\}/is", 'token' => array('{{{', '}}}') ), 'image' => array( 'expression' => "/\~?\{\{(.+)\|(.+)\}\}/i", 'tags' => array(), 'token' => array('{{', '|Alt}}') ), 'link' => array( 'expression' => "/\~?\[\[(.+?)\]\]/is", 'tag' => 'a', 'token' => array('[[', ']]') ), 'url' => array( 'expression' => "/\~?(? 'a', 'token' => "http://" ), 'line_break' => array( 'expression' => "/\\\\\\\\/", 'tag' => 'br', 'simple' => true, 'token' => '----' ), 'bold' => array( 'expression' => "/\*\*(.*?)(?:\*\*|$)/is", 'tag' => 'strong', 'token' => array('**', '**') ), 'italic' => array( 'expression' => "#(? 'em', 'token' => array('//', '//') ) ); /** * Block hooks */ protected function before_parsing() { $this->string = htmlspecialchars($this->string); parent::before_parsing(); } public function get_section($header, $text, $clean = false) { // The requested header is likely to have been passed to htmlspecialchars in // self::before_parsing(), therefore we should decode it when looking for it. return parent::get_section(htmlspecialchars_decode($header), $text, $clean); } protected function header_block_rule($match) { $num = strlen($match[1]); $text = trim($match[2]); $text = preg_replace("/\s*={1,$num}$/im", "", $text); return $this->generate_header($text, $num); } /** * Table generation */ protected function table_block_rule($match) { $rows = explode("\n", $match[0]); $table = array(); foreach($rows as $r) { if(empty($r)) { continue; } $rawcells = explode("|", $r); $cells = array(); array_shift($rawcells); array_pop($rawcells); foreach($rawcells as $c) { if(!empty($c)) { if($c[0] == "=") { $type = 'header'; $c = substr($c, 1); } else { $type = 'normal'; } $this->rules($c); $cells[] = array($type, $c); } } $table[] = $cells; } return $this->generate_table($table); } protected function paragraph_block_rule($match) { $text = $match[1]; foreach($this->tagrules as $tr) { if(isset($tr['token'])) { if(is_array($tr['token'])) { $this->escape_token_string($text, $tr['token'][0]); $this->escape_token_string($text, $tr['token'][1]); } else { $this->escape_token_string($text, $tr['token']); } } } $this->escape_token_string($text, "~"); return $text; } /** * Escape token when it is "negated" */ private function escape_token_string(&$text, $token) { $text = str_replace("~".$token, $this->protect($token), $text); } /** * Tag functions */ protected function url_tag_rule($match) { if(strpos($match[0], "~") === 0) { return substr($match[0], 1); } else { $text = trim($match[0]); $options = array('href' => $text); return array($text, $options); } } protected function link_tag_rule($match) { $text = trim($match[1]); if(strpos($match[0], "~") === 0) { return substr($match[0], 1); } else { return $this->format_link($text); } } /** * Special treatment of // ** // ** // */ protected function bold_tag_rule($match) { $text = $match[1]; $this->rules($text, array('only' => array('italic'))); if(strpos($text, "//") !== false) { $text = str_replace("//", $this->protect("//"), $text); } return array($text, array()); } protected function image_tag_rule($match) { if(strpos($match[0], "~") === 0) { return substr($match[0], 1); } return $this->format_image($match[1], $match[2]); } }