10cecf9d5Sandi<?php 25c2aad12SAndreas Gohr 3cbb44eabSAndreas Gohruse dokuwiki\Extension\Event; 4e1d9dcc8SAndreas Gohruse dokuwiki\Extension\SyntaxPlugin; 5be906b56SAndreas Gohruse dokuwiki\Parsing\Handler\Block; 6be906b56SAndreas Gohruse dokuwiki\Parsing\Handler\CallWriter; 7be906b56SAndreas Gohruse dokuwiki\Parsing\Handler\CallWriterInterface; 8be906b56SAndreas Gohruse dokuwiki\Parsing\Handler\Lists; 9be906b56SAndreas Gohruse dokuwiki\Parsing\Handler\Nest; 10be906b56SAndreas Gohruse dokuwiki\Parsing\Handler\Preformatted; 11be906b56SAndreas Gohruse dokuwiki\Parsing\Handler\Quote; 12be906b56SAndreas Gohruse dokuwiki\Parsing\Handler\Table; 135c2aad12SAndreas Gohr 148b1b81beSAndreas Gohr/** 158b1b81beSAndreas Gohr * Class Doku_Handler 168b1b81beSAndreas Gohr */ 17*faf3f01bSAndreas Gohrclass Doku_Handler 18*faf3f01bSAndreas Gohr{ 198b1b81beSAndreas Gohr /** @var CallWriterInterface */ 20*faf3f01bSAndreas Gohr protected $callWriter; 210cecf9d5Sandi 228b1b81beSAndreas Gohr /** @var array The current CallWriter will write directly to this list of calls, Parser reads it */ 23*faf3f01bSAndreas Gohr public $calls = []; 240cecf9d5Sandi 258b1b81beSAndreas Gohr /** @var array internal status holders for some modes */ 26*faf3f01bSAndreas Gohr protected $status = [ 2744881bd0Shenning.noren 'section' => false, 28*faf3f01bSAndreas Gohr 'doublequote' => 0 29*faf3f01bSAndreas Gohr ]; 300cecf9d5Sandi 318b1b81beSAndreas Gohr /** @var bool should blocks be rewritten? FIXME seems to always be true */ 328b1b81beSAndreas Gohr protected $rewriteBlocks = true; 33b7c441b9SHarry Fuecks 348b1b81beSAndreas Gohr /** 35ac1d8211SAndreas Gohr * @var bool are we in a footnote already? 36ac1d8211SAndreas Gohr */ 37ac1d8211SAndreas Gohr protected $footnote; 38ac1d8211SAndreas Gohr 39ac1d8211SAndreas Gohr /** 408b1b81beSAndreas Gohr * Doku_Handler constructor. 418b1b81beSAndreas Gohr */ 42*faf3f01bSAndreas Gohr public function __construct() 43*faf3f01bSAndreas Gohr { 448b1b81beSAndreas Gohr $this->callWriter = new CallWriter($this); 450cecf9d5Sandi } 460cecf9d5Sandi 47276820f7SScrutinizer Auto-Fixer /** 488b1b81beSAndreas Gohr * Add a new call by passing it to the current CallWriter 498b1b81beSAndreas Gohr * 508b1b81beSAndreas Gohr * @param string $handler handler method name (see mode handlers below) 518b1b81beSAndreas Gohr * @param mixed $args arguments for this call 528b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 53276820f7SScrutinizer Auto-Fixer */ 54*faf3f01bSAndreas Gohr public function addCall($handler, $args, $pos) 55*faf3f01bSAndreas Gohr { 56*faf3f01bSAndreas Gohr $call = [$handler, $args, $pos]; 578b1b81beSAndreas Gohr $this->callWriter->writeCall($call); 580cecf9d5Sandi } 590cecf9d5Sandi 60533aca44SAndreas Gohr /** 61533aca44SAndreas Gohr * Accessor for the current CallWriter 62533aca44SAndreas Gohr * 63533aca44SAndreas Gohr * @return CallWriterInterface 64533aca44SAndreas Gohr */ 65*faf3f01bSAndreas Gohr public function getCallWriter() 66*faf3f01bSAndreas Gohr { 67533aca44SAndreas Gohr return $this->callWriter; 68533aca44SAndreas Gohr } 69533aca44SAndreas Gohr 70533aca44SAndreas Gohr /** 71533aca44SAndreas Gohr * Set a new CallWriter 72533aca44SAndreas Gohr * 73533aca44SAndreas Gohr * @param CallWriterInterface $callWriter 74533aca44SAndreas Gohr */ 75*faf3f01bSAndreas Gohr public function setCallWriter($callWriter) 76*faf3f01bSAndreas Gohr { 77533aca44SAndreas Gohr $this->callWriter = $callWriter; 78533aca44SAndreas Gohr } 79533aca44SAndreas Gohr 80eb33b670SAndreas Gohr /** 81eb33b670SAndreas Gohr * Return the current internal status of the given name 82eb33b670SAndreas Gohr * 83eb33b670SAndreas Gohr * @param string $status 84eb33b670SAndreas Gohr * @return mixed|null 85eb33b670SAndreas Gohr */ 86*faf3f01bSAndreas Gohr public function getStatus($status) 87*faf3f01bSAndreas Gohr { 88eb33b670SAndreas Gohr if (!isset($this->status[$status])) return null; 89eb33b670SAndreas Gohr return $this->status[$status]; 90eb33b670SAndreas Gohr } 91eb33b670SAndreas Gohr 92eb33b670SAndreas Gohr /** 93eb33b670SAndreas Gohr * Set a new internal status 94eb33b670SAndreas Gohr * 95eb33b670SAndreas Gohr * @param string $status 96eb33b670SAndreas Gohr * @param mixed $value 97eb33b670SAndreas Gohr */ 98*faf3f01bSAndreas Gohr public function setStatus($status, $value) 99*faf3f01bSAndreas Gohr { 100eb33b670SAndreas Gohr $this->status[$status] = $value; 101eb33b670SAndreas Gohr } 102eb33b670SAndreas Gohr 10331c0895aSAndreas Gohr /** @deprecated 2019-10-31 use addCall() instead */ 104*faf3f01bSAndreas Gohr public function _addCall($handler, $args, $pos) 105*faf3f01bSAndreas Gohr { 10631c0895aSAndreas Gohr dbg_deprecated('addCall'); 10731c0895aSAndreas Gohr $this->addCall($handler, $args, $pos); 10831c0895aSAndreas Gohr } 10931c0895aSAndreas Gohr 1108b1b81beSAndreas Gohr /** 1118b1b81beSAndreas Gohr * Similar to addCall, but adds a plugin call 1128b1b81beSAndreas Gohr * 1138b1b81beSAndreas Gohr * @param string $plugin name of the plugin 1148b1b81beSAndreas Gohr * @param mixed $args arguments for this call 1158b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 1168b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 1178b1b81beSAndreas Gohr * @param string $match matched syntax 1188b1b81beSAndreas Gohr */ 119*faf3f01bSAndreas Gohr public function addPluginCall($plugin, $args, $state, $pos, $match) 120*faf3f01bSAndreas Gohr { 121*faf3f01bSAndreas Gohr $call = ['plugin', [$plugin, $args, $state, $match], $pos]; 1228b1b81beSAndreas Gohr $this->callWriter->writeCall($call); 12304ebd214Schris } 12404ebd214Schris 1258b1b81beSAndreas Gohr /** 1268b1b81beSAndreas Gohr * Finishes handling 1278b1b81beSAndreas Gohr * 1288b1b81beSAndreas Gohr * Called from the parser. Calls finalise() on the call writer, closes open 1298b1b81beSAndreas Gohr * sections, rewrites blocks and adds document_start and document_end calls. 1308b1b81beSAndreas Gohr * 1318b1b81beSAndreas Gohr * @triggers PARSER_HANDLER_DONE 1328b1b81beSAndreas Gohr */ 133*faf3f01bSAndreas Gohr public function finalize() 134*faf3f01bSAndreas Gohr { 1358b1b81beSAndreas Gohr $this->callWriter->finalise(); 136f4f02a0fSchris 137e1c10e4dSchris if ($this->status['section']) { 138e1c10e4dSchris $last_call = end($this->calls); 139*faf3f01bSAndreas Gohr $this->calls[] = ['section_close', [], $last_call[2]]; 1400cecf9d5Sandi } 1410cecf9d5Sandi 142b7c441b9SHarry Fuecks if ($this->rewriteBlocks) { 1435c2aad12SAndreas Gohr $B = new Block(); 1440cecf9d5Sandi $this->calls = $B->process($this->calls); 145b7c441b9SHarry Fuecks } 146e0ad864eSchris 147cbb44eabSAndreas Gohr Event::createAndTrigger('PARSER_HANDLER_DONE', $this); 1480cecf9d5Sandi 149*faf3f01bSAndreas Gohr array_unshift($this->calls, ['document_start', [], 0]); 1500cecf9d5Sandi $last_call = end($this->calls); 151*faf3f01bSAndreas Gohr $this->calls[] = ['document_end', [], $last_call[2]]; 1520cecf9d5Sandi } 1530cecf9d5Sandi 1549e491c01SAndreas Gohr /** 1559e491c01SAndreas Gohr * fetch the current call and advance the pointer to the next one 1569e491c01SAndreas Gohr * 1578b1b81beSAndreas Gohr * @fixme seems to be unused? 1589e491c01SAndreas Gohr * @return bool|mixed 1599e491c01SAndreas Gohr */ 160*faf3f01bSAndreas Gohr public function fetch() 161*faf3f01bSAndreas Gohr { 1629e491c01SAndreas Gohr $call = current($this->calls); 1639e491c01SAndreas Gohr if ($call !== false) { 1649e491c01SAndreas Gohr next($this->calls); //advance the pointer 1659e491c01SAndreas Gohr return $call; 1660cecf9d5Sandi } 16744881bd0Shenning.noren return false; 1680cecf9d5Sandi } 169ee20e7d1Sandi 170ee20e7d1Sandi 171ee20e7d1Sandi /** 172e2d88156SLarsDW223 * Internal function for parsing highlight options. 173e2d88156SLarsDW223 * $options is parsed for key value pairs separated by commas. 174e2d88156SLarsDW223 * A value might also be missing in which case the value will simple 175e2d88156SLarsDW223 * be set to true. Commas in strings are ignored, e.g. option="4,56" 176e2d88156SLarsDW223 * will work as expected and will only create one entry. 177e2d88156SLarsDW223 * 17854f741e8SAndreas Gohr * @param string $options space separated list of key-value pairs, 179e2d88156SLarsDW223 * e.g. option1=123, option2="456" 180e2d88156SLarsDW223 * @return array|null Array of key-value pairs $array['key'] = 'value'; 181e2d88156SLarsDW223 * or null if no entries found 182e2d88156SLarsDW223 */ 183*faf3f01bSAndreas Gohr protected function parse_highlight_options($options) 184*faf3f01bSAndreas Gohr { 185*faf3f01bSAndreas Gohr $result = []; 18654f741e8SAndreas Gohr preg_match_all('/(\w+(?:="[^"]*"))|(\w+(?:=[^\s]*))|(\w+[^=\s\]])(?:\s*)/', $options, $matches, PREG_SET_ORDER); 187e2d88156SLarsDW223 foreach ($matches as $match) { 188e2d88156SLarsDW223 $equal_sign = strpos($match [0], '='); 189e2d88156SLarsDW223 if ($equal_sign === false) { 19054f741e8SAndreas Gohr $key = trim($match[0]); 191e2d88156SLarsDW223 $result [$key] = 1; 192e2d88156SLarsDW223 } else { 193e2d88156SLarsDW223 $key = substr($match[0], 0, $equal_sign); 194e2d88156SLarsDW223 $value = substr($match[0], $equal_sign + 1); 195e2d88156SLarsDW223 $value = trim($value, '"'); 196e2d88156SLarsDW223 if (strlen($value) > 0) { 197e2d88156SLarsDW223 $result [$key] = $value; 198e2d88156SLarsDW223 } else { 199e2d88156SLarsDW223 $result [$key] = 1; 200e2d88156SLarsDW223 } 201e2d88156SLarsDW223 } 202e2d88156SLarsDW223 } 203e2d88156SLarsDW223 204e2d88156SLarsDW223 // Check for supported options 205e2d88156SLarsDW223 $result = array_intersect_key( 206e2d88156SLarsDW223 $result, 207*faf3f01bSAndreas Gohr array_flip([ 208e2d88156SLarsDW223 'enable_line_numbers', 209e2d88156SLarsDW223 'start_line_numbers_at', 210e2d88156SLarsDW223 'highlight_lines_extra', 211*faf3f01bSAndreas Gohr 'enable_keyword_links' 212*faf3f01bSAndreas Gohr ]) 213e2d88156SLarsDW223 ); 214e2d88156SLarsDW223 215e2d88156SLarsDW223 // Sanitize values 216e2d88156SLarsDW223 if (isset($result['enable_line_numbers'])) { 21754f741e8SAndreas Gohr if ($result['enable_line_numbers'] === 'false') { 21854f741e8SAndreas Gohr $result['enable_line_numbers'] = false; 21954f741e8SAndreas Gohr } 220e2d88156SLarsDW223 $result['enable_line_numbers'] = (bool)$result['enable_line_numbers']; 221e2d88156SLarsDW223 } 222e2d88156SLarsDW223 if (isset($result['highlight_lines_extra'])) { 223e2d88156SLarsDW223 $result['highlight_lines_extra'] = array_map('intval', explode(',', $result['highlight_lines_extra'])); 224e2d88156SLarsDW223 $result['highlight_lines_extra'] = array_filter($result['highlight_lines_extra']); 225e2d88156SLarsDW223 $result['highlight_lines_extra'] = array_unique($result['highlight_lines_extra']); 226e2d88156SLarsDW223 } 227e2d88156SLarsDW223 if (isset($result['start_line_numbers_at'])) { 228e2d88156SLarsDW223 $result['start_line_numbers_at'] = (int)$result['start_line_numbers_at']; 229e2d88156SLarsDW223 } 230e2d88156SLarsDW223 if (isset($result['enable_keyword_links'])) { 23154f741e8SAndreas Gohr if ($result['enable_keyword_links'] === 'false') { 23254f741e8SAndreas Gohr $result['enable_keyword_links'] = false; 23354f741e8SAndreas Gohr } 23454f741e8SAndreas Gohr $result['enable_keyword_links'] = (bool)$result['enable_keyword_links']; 235e2d88156SLarsDW223 } 236e2d88156SLarsDW223 if (count($result) == 0) { 237e2d88156SLarsDW223 return null; 238e2d88156SLarsDW223 } 239e2d88156SLarsDW223 240e2d88156SLarsDW223 return $result; 241e2d88156SLarsDW223 } 242e2d88156SLarsDW223 2438b1b81beSAndreas Gohr /** 2448b1b81beSAndreas Gohr * Simplifies handling for the formatting tags which all behave the same 2458b1b81beSAndreas Gohr * 2468b1b81beSAndreas Gohr * @param string $match matched syntax 2478b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 2488b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 2498b1b81beSAndreas Gohr * @param string $name actual mode name 2508b1b81beSAndreas Gohr */ 251*faf3f01bSAndreas Gohr protected function nestingTag($match, $state, $pos, $name) 252*faf3f01bSAndreas Gohr { 2538b1b81beSAndreas Gohr switch ($state) { 2548b1b81beSAndreas Gohr case DOKU_LEXER_ENTER: 255*faf3f01bSAndreas Gohr $this->addCall($name . '_open', [], $pos); 2568b1b81beSAndreas Gohr break; 2578b1b81beSAndreas Gohr case DOKU_LEXER_EXIT: 258*faf3f01bSAndreas Gohr $this->addCall($name . '_close', [], $pos); 2598b1b81beSAndreas Gohr break; 2608b1b81beSAndreas Gohr case DOKU_LEXER_UNMATCHED: 261*faf3f01bSAndreas Gohr $this->addCall('cdata', [$match], $pos); 2628b1b81beSAndreas Gohr break; 2638b1b81beSAndreas Gohr } 2648b1b81beSAndreas Gohr } 2658b1b81beSAndreas Gohr 2668b1b81beSAndreas Gohr 2678b1b81beSAndreas Gohr /** 2688b1b81beSAndreas Gohr * The following methods define the handlers for the different Syntax modes 2698b1b81beSAndreas Gohr * 270be906b56SAndreas Gohr * The handlers are called from dokuwiki\Parsing\Lexer\Lexer\invokeParser() 2718b1b81beSAndreas Gohr * 2728b1b81beSAndreas Gohr * @todo it might make sense to move these into their own class or merge them with the 2738b1b81beSAndreas Gohr * ParserMode classes some time. 2748b1b81beSAndreas Gohr */ 2758b1b81beSAndreas Gohr // region mode handlers 2768b1b81beSAndreas Gohr 2778b1b81beSAndreas Gohr /** 2788b1b81beSAndreas Gohr * Special plugin handler 2798b1b81beSAndreas Gohr * 2808b1b81beSAndreas Gohr * This handler is called for all modes starting with 'plugin_'. 2818b1b81beSAndreas Gohr * An additional parameter with the plugin name is passed. The plugin's handle() 2828b1b81beSAndreas Gohr * method is called here 2838b1b81beSAndreas Gohr * 2848b1b81beSAndreas Gohr * @param string $match matched syntax 2858b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 2868b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 2878b1b81beSAndreas Gohr * @param string $pluginname name of the plugin 2888b1b81beSAndreas Gohr * @return bool mode handled? 289*faf3f01bSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 290*faf3f01bSAndreas Gohr * 2918b1b81beSAndreas Gohr */ 292*faf3f01bSAndreas Gohr public function plugin($match, $state, $pos, $pluginname) 293*faf3f01bSAndreas Gohr { 294*faf3f01bSAndreas Gohr $data = [$match]; 295e1d9dcc8SAndreas Gohr /** @var SyntaxPlugin $plugin */ 2968b1b81beSAndreas Gohr $plugin = plugin_load('syntax', $pluginname); 2978b1b81beSAndreas Gohr if ($plugin != null) { 2988b1b81beSAndreas Gohr $data = $plugin->handle($match, $state, $pos, $this); 2998b1b81beSAndreas Gohr } 3008b1b81beSAndreas Gohr if ($data !== false) { 3018b1b81beSAndreas Gohr $this->addPluginCall($pluginname, $data, $state, $pos, $match); 3028b1b81beSAndreas Gohr } 3038b1b81beSAndreas Gohr return true; 3048b1b81beSAndreas Gohr } 3058b1b81beSAndreas Gohr 3068b1b81beSAndreas Gohr /** 3078b1b81beSAndreas Gohr * @param string $match matched syntax 3088b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 3098b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 3108b1b81beSAndreas Gohr * @return bool mode handled? 3118b1b81beSAndreas Gohr */ 312*faf3f01bSAndreas Gohr public function base($match, $state, $pos) 313*faf3f01bSAndreas Gohr { 314*faf3f01bSAndreas Gohr if ($state === DOKU_LEXER_UNMATCHED) { 315*faf3f01bSAndreas Gohr $this->addCall('cdata', [$match], $pos); 3168b1b81beSAndreas Gohr return true; 3178b1b81beSAndreas Gohr } 3188b1b81beSAndreas Gohr return false; 3198b1b81beSAndreas Gohr } 3208b1b81beSAndreas Gohr 3218b1b81beSAndreas Gohr /** 3228b1b81beSAndreas Gohr * @param string $match matched syntax 3238b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 3248b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 3258b1b81beSAndreas Gohr * @return bool mode handled? 3268b1b81beSAndreas Gohr */ 327*faf3f01bSAndreas Gohr public function header($match, $state, $pos) 328*faf3f01bSAndreas Gohr { 3298b1b81beSAndreas Gohr // get level and title 3308b1b81beSAndreas Gohr $title = trim($match); 3318b1b81beSAndreas Gohr $level = 7 - strspn($title, '='); 3328b1b81beSAndreas Gohr if ($level < 1) $level = 1; 3338b1b81beSAndreas Gohr $title = trim($title, '='); 3348b1b81beSAndreas Gohr $title = trim($title); 3358b1b81beSAndreas Gohr 336*faf3f01bSAndreas Gohr if ($this->status['section']) $this->addCall('section_close', [], $pos); 3378b1b81beSAndreas Gohr 338*faf3f01bSAndreas Gohr $this->addCall('header', [$title, $level, $pos], $pos); 3398b1b81beSAndreas Gohr 340*faf3f01bSAndreas Gohr $this->addCall('section_open', [$level], $pos); 3418b1b81beSAndreas Gohr $this->status['section'] = true; 3428b1b81beSAndreas Gohr return true; 3438b1b81beSAndreas Gohr } 3448b1b81beSAndreas Gohr 3458b1b81beSAndreas Gohr /** 3468b1b81beSAndreas Gohr * @param string $match matched syntax 3478b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 3488b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 3498b1b81beSAndreas Gohr * @return bool mode handled? 3508b1b81beSAndreas Gohr */ 351*faf3f01bSAndreas Gohr public function notoc($match, $state, $pos) 352*faf3f01bSAndreas Gohr { 353*faf3f01bSAndreas Gohr $this->addCall('notoc', [], $pos); 3548b1b81beSAndreas Gohr return true; 3558b1b81beSAndreas Gohr } 3568b1b81beSAndreas Gohr 3578b1b81beSAndreas Gohr /** 3588b1b81beSAndreas Gohr * @param string $match matched syntax 3598b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 3608b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 3618b1b81beSAndreas Gohr * @return bool mode handled? 3628b1b81beSAndreas Gohr */ 363*faf3f01bSAndreas Gohr public function nocache($match, $state, $pos) 364*faf3f01bSAndreas Gohr { 365*faf3f01bSAndreas Gohr $this->addCall('nocache', [], $pos); 3668b1b81beSAndreas Gohr return true; 3678b1b81beSAndreas Gohr } 3688b1b81beSAndreas Gohr 3698b1b81beSAndreas Gohr /** 3708b1b81beSAndreas Gohr * @param string $match matched syntax 3718b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 3728b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 3738b1b81beSAndreas Gohr * @return bool mode handled? 3748b1b81beSAndreas Gohr */ 375*faf3f01bSAndreas Gohr public function linebreak($match, $state, $pos) 376*faf3f01bSAndreas Gohr { 377*faf3f01bSAndreas Gohr $this->addCall('linebreak', [], $pos); 3788b1b81beSAndreas Gohr return true; 3798b1b81beSAndreas Gohr } 3808b1b81beSAndreas Gohr 3818b1b81beSAndreas Gohr /** 3828b1b81beSAndreas Gohr * @param string $match matched syntax 3838b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 3848b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 3858b1b81beSAndreas Gohr * @return bool mode handled? 3868b1b81beSAndreas Gohr */ 387*faf3f01bSAndreas Gohr public function eol($match, $state, $pos) 388*faf3f01bSAndreas Gohr { 389*faf3f01bSAndreas Gohr $this->addCall('eol', [], $pos); 3908b1b81beSAndreas Gohr return true; 3918b1b81beSAndreas Gohr } 3928b1b81beSAndreas Gohr 3938b1b81beSAndreas Gohr /** 3948b1b81beSAndreas Gohr * @param string $match matched syntax 3958b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 3968b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 3978b1b81beSAndreas Gohr * @return bool mode handled? 3988b1b81beSAndreas Gohr */ 399*faf3f01bSAndreas Gohr public function hr($match, $state, $pos) 400*faf3f01bSAndreas Gohr { 401*faf3f01bSAndreas Gohr $this->addCall('hr', [], $pos); 4028b1b81beSAndreas Gohr return true; 4038b1b81beSAndreas Gohr } 4048b1b81beSAndreas Gohr 4058b1b81beSAndreas Gohr /** 4068b1b81beSAndreas Gohr * @param string $match matched syntax 4078b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 4088b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 4098b1b81beSAndreas Gohr * @return bool mode handled? 4108b1b81beSAndreas Gohr */ 411*faf3f01bSAndreas Gohr public function strong($match, $state, $pos) 412*faf3f01bSAndreas Gohr { 4138b1b81beSAndreas Gohr $this->nestingTag($match, $state, $pos, 'strong'); 4148b1b81beSAndreas Gohr return true; 4158b1b81beSAndreas Gohr } 4168b1b81beSAndreas Gohr 4178b1b81beSAndreas Gohr /** 4188b1b81beSAndreas Gohr * @param string $match matched syntax 4198b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 4208b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 4218b1b81beSAndreas Gohr * @return bool mode handled? 4228b1b81beSAndreas Gohr */ 423*faf3f01bSAndreas Gohr public function emphasis($match, $state, $pos) 424*faf3f01bSAndreas Gohr { 4258b1b81beSAndreas Gohr $this->nestingTag($match, $state, $pos, 'emphasis'); 4268b1b81beSAndreas Gohr return true; 4278b1b81beSAndreas Gohr } 4288b1b81beSAndreas Gohr 4298b1b81beSAndreas Gohr /** 4308b1b81beSAndreas Gohr * @param string $match matched syntax 4318b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 4328b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 4338b1b81beSAndreas Gohr * @return bool mode handled? 4348b1b81beSAndreas Gohr */ 435*faf3f01bSAndreas Gohr public function underline($match, $state, $pos) 436*faf3f01bSAndreas Gohr { 4378b1b81beSAndreas Gohr $this->nestingTag($match, $state, $pos, 'underline'); 4388b1b81beSAndreas Gohr return true; 4398b1b81beSAndreas Gohr } 4408b1b81beSAndreas Gohr 4418b1b81beSAndreas Gohr /** 4428b1b81beSAndreas Gohr * @param string $match matched syntax 4438b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 4448b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 4458b1b81beSAndreas Gohr * @return bool mode handled? 4468b1b81beSAndreas Gohr */ 447*faf3f01bSAndreas Gohr public function monospace($match, $state, $pos) 448*faf3f01bSAndreas Gohr { 4498b1b81beSAndreas Gohr $this->nestingTag($match, $state, $pos, 'monospace'); 4508b1b81beSAndreas Gohr return true; 4518b1b81beSAndreas Gohr } 4528b1b81beSAndreas Gohr 4538b1b81beSAndreas Gohr /** 4548b1b81beSAndreas Gohr * @param string $match matched syntax 4558b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 4568b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 4578b1b81beSAndreas Gohr * @return bool mode handled? 4588b1b81beSAndreas Gohr */ 459*faf3f01bSAndreas Gohr public function subscript($match, $state, $pos) 460*faf3f01bSAndreas Gohr { 4618b1b81beSAndreas Gohr $this->nestingTag($match, $state, $pos, 'subscript'); 4628b1b81beSAndreas Gohr return true; 4638b1b81beSAndreas Gohr } 4648b1b81beSAndreas Gohr 4658b1b81beSAndreas Gohr /** 4668b1b81beSAndreas Gohr * @param string $match matched syntax 4678b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 4688b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 4698b1b81beSAndreas Gohr * @return bool mode handled? 4708b1b81beSAndreas Gohr */ 471*faf3f01bSAndreas Gohr public function superscript($match, $state, $pos) 472*faf3f01bSAndreas Gohr { 4738b1b81beSAndreas Gohr $this->nestingTag($match, $state, $pos, 'superscript'); 4748b1b81beSAndreas Gohr return true; 4758b1b81beSAndreas Gohr } 4768b1b81beSAndreas Gohr 4778b1b81beSAndreas Gohr /** 4788b1b81beSAndreas Gohr * @param string $match matched syntax 4798b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 4808b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 4818b1b81beSAndreas Gohr * @return bool mode handled? 4828b1b81beSAndreas Gohr */ 483*faf3f01bSAndreas Gohr public function deleted($match, $state, $pos) 484*faf3f01bSAndreas Gohr { 4858b1b81beSAndreas Gohr $this->nestingTag($match, $state, $pos, 'deleted'); 4868b1b81beSAndreas Gohr return true; 4878b1b81beSAndreas Gohr } 4888b1b81beSAndreas Gohr 4898b1b81beSAndreas Gohr /** 4908b1b81beSAndreas Gohr * @param string $match matched syntax 4918b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 4928b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 4938b1b81beSAndreas Gohr * @return bool mode handled? 4948b1b81beSAndreas Gohr */ 495*faf3f01bSAndreas Gohr public function footnote($match, $state, $pos) 496*faf3f01bSAndreas Gohr { 497ac1d8211SAndreas Gohr if (!isset($this->footnote)) $this->footnote = false; 4988b1b81beSAndreas Gohr 4998b1b81beSAndreas Gohr switch ($state) { 5008b1b81beSAndreas Gohr case DOKU_LEXER_ENTER: 5018b1b81beSAndreas Gohr // footnotes can not be nested - however due to limitations in lexer it can't be prevented 5028b1b81beSAndreas Gohr // we will still enter a new footnote mode, we just do nothing 503ac1d8211SAndreas Gohr if ($this->footnote) { 504*faf3f01bSAndreas Gohr $this->addCall('cdata', [$match], $pos); 5058b1b81beSAndreas Gohr break; 5068b1b81beSAndreas Gohr } 507ac1d8211SAndreas Gohr $this->footnote = true; 5088b1b81beSAndreas Gohr 5098b1b81beSAndreas Gohr $this->callWriter = new Nest($this->callWriter, 'footnote_close'); 510*faf3f01bSAndreas Gohr $this->addCall('footnote_open', [], $pos); 5118b1b81beSAndreas Gohr break; 5128b1b81beSAndreas Gohr case DOKU_LEXER_EXIT: 5138b1b81beSAndreas Gohr // check whether we have already exitted the footnote mode, can happen if the modes were nested 514ac1d8211SAndreas Gohr if (!$this->footnote) { 515*faf3f01bSAndreas Gohr $this->addCall('cdata', [$match], $pos); 5168b1b81beSAndreas Gohr break; 5178b1b81beSAndreas Gohr } 5188b1b81beSAndreas Gohr 519ac1d8211SAndreas Gohr $this->footnote = false; 520*faf3f01bSAndreas Gohr $this->addCall('footnote_close', [], $pos); 5218b1b81beSAndreas Gohr 5228b1b81beSAndreas Gohr /** @var Nest $reWriter */ 5238b1b81beSAndreas Gohr $reWriter = $this->callWriter; 5248b1b81beSAndreas Gohr $this->callWriter = $reWriter->process(); 5258b1b81beSAndreas Gohr break; 5268b1b81beSAndreas Gohr case DOKU_LEXER_UNMATCHED: 527*faf3f01bSAndreas Gohr $this->addCall('cdata', [$match], $pos); 5288b1b81beSAndreas Gohr break; 5298b1b81beSAndreas Gohr } 5308b1b81beSAndreas Gohr return true; 5318b1b81beSAndreas Gohr } 5328b1b81beSAndreas Gohr 5338b1b81beSAndreas Gohr /** 5348b1b81beSAndreas Gohr * @param string $match matched syntax 5358b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 5368b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 5378b1b81beSAndreas Gohr * @return bool mode handled? 5388b1b81beSAndreas Gohr */ 539*faf3f01bSAndreas Gohr public function listblock($match, $state, $pos) 540*faf3f01bSAndreas Gohr { 5418b1b81beSAndreas Gohr switch ($state) { 5428b1b81beSAndreas Gohr case DOKU_LEXER_ENTER: 5438b1b81beSAndreas Gohr $this->callWriter = new Lists($this->callWriter); 544*faf3f01bSAndreas Gohr $this->addCall('list_open', [$match], $pos); 5458b1b81beSAndreas Gohr break; 5468b1b81beSAndreas Gohr case DOKU_LEXER_EXIT: 547*faf3f01bSAndreas Gohr $this->addCall('list_close', [], $pos); 5488b1b81beSAndreas Gohr /** @var Lists $reWriter */ 5498b1b81beSAndreas Gohr $reWriter = $this->callWriter; 5508b1b81beSAndreas Gohr $this->callWriter = $reWriter->process(); 5518b1b81beSAndreas Gohr break; 5528b1b81beSAndreas Gohr case DOKU_LEXER_MATCHED: 553*faf3f01bSAndreas Gohr $this->addCall('list_item', [$match], $pos); 5548b1b81beSAndreas Gohr break; 5558b1b81beSAndreas Gohr case DOKU_LEXER_UNMATCHED: 556*faf3f01bSAndreas Gohr $this->addCall('cdata', [$match], $pos); 5578b1b81beSAndreas Gohr break; 5588b1b81beSAndreas Gohr } 5598b1b81beSAndreas Gohr return true; 5608b1b81beSAndreas Gohr } 5618b1b81beSAndreas Gohr 5628b1b81beSAndreas Gohr /** 5638b1b81beSAndreas Gohr * @param string $match matched syntax 5648b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 5658b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 5668b1b81beSAndreas Gohr * @return bool mode handled? 5678b1b81beSAndreas Gohr */ 568*faf3f01bSAndreas Gohr public function unformatted($match, $state, $pos) 569*faf3f01bSAndreas Gohr { 5708b1b81beSAndreas Gohr if ($state == DOKU_LEXER_UNMATCHED) { 571*faf3f01bSAndreas Gohr $this->addCall('unformatted', [$match], $pos); 5728b1b81beSAndreas Gohr } 5738b1b81beSAndreas Gohr return true; 5748b1b81beSAndreas Gohr } 5758b1b81beSAndreas Gohr 5768b1b81beSAndreas Gohr /** 5778b1b81beSAndreas Gohr * @param string $match matched syntax 5788b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 5798b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 5808b1b81beSAndreas Gohr * @return bool mode handled? 5818b1b81beSAndreas Gohr */ 582*faf3f01bSAndreas Gohr public function preformatted($match, $state, $pos) 583*faf3f01bSAndreas Gohr { 5848b1b81beSAndreas Gohr switch ($state) { 5858b1b81beSAndreas Gohr case DOKU_LEXER_ENTER: 5868b1b81beSAndreas Gohr $this->callWriter = new Preformatted($this->callWriter); 587*faf3f01bSAndreas Gohr $this->addCall('preformatted_start', [], $pos); 5888b1b81beSAndreas Gohr break; 5898b1b81beSAndreas Gohr case DOKU_LEXER_EXIT: 590*faf3f01bSAndreas Gohr $this->addCall('preformatted_end', [], $pos); 5918b1b81beSAndreas Gohr /** @var Preformatted $reWriter */ 5928b1b81beSAndreas Gohr $reWriter = $this->callWriter; 5938b1b81beSAndreas Gohr $this->callWriter = $reWriter->process(); 5948b1b81beSAndreas Gohr break; 5958b1b81beSAndreas Gohr case DOKU_LEXER_MATCHED: 596*faf3f01bSAndreas Gohr $this->addCall('preformatted_newline', [], $pos); 5978b1b81beSAndreas Gohr break; 5988b1b81beSAndreas Gohr case DOKU_LEXER_UNMATCHED: 599*faf3f01bSAndreas Gohr $this->addCall('preformatted_content', [$match], $pos); 6008b1b81beSAndreas Gohr break; 6018b1b81beSAndreas Gohr } 6028b1b81beSAndreas Gohr 6038b1b81beSAndreas Gohr return true; 6048b1b81beSAndreas Gohr } 6058b1b81beSAndreas Gohr 6068b1b81beSAndreas Gohr /** 6078b1b81beSAndreas Gohr * @param string $match matched syntax 6088b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 6098b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 6108b1b81beSAndreas Gohr * @return bool mode handled? 6118b1b81beSAndreas Gohr */ 612*faf3f01bSAndreas Gohr public function quote($match, $state, $pos) 613*faf3f01bSAndreas Gohr { 6148b1b81beSAndreas Gohr 6158b1b81beSAndreas Gohr switch ($state) { 6168b1b81beSAndreas Gohr 6178b1b81beSAndreas Gohr case DOKU_LEXER_ENTER: 6188b1b81beSAndreas Gohr $this->callWriter = new Quote($this->callWriter); 619*faf3f01bSAndreas Gohr $this->addCall('quote_start', [$match], $pos); 6208b1b81beSAndreas Gohr break; 6218b1b81beSAndreas Gohr 6228b1b81beSAndreas Gohr case DOKU_LEXER_EXIT: 623*faf3f01bSAndreas Gohr $this->addCall('quote_end', [], $pos); 6248b1b81beSAndreas Gohr /** @var Lists $reWriter */ 6258b1b81beSAndreas Gohr $reWriter = $this->callWriter; 6268b1b81beSAndreas Gohr $this->callWriter = $reWriter->process(); 6278b1b81beSAndreas Gohr break; 6288b1b81beSAndreas Gohr 6298b1b81beSAndreas Gohr case DOKU_LEXER_MATCHED: 630*faf3f01bSAndreas Gohr $this->addCall('quote_newline', [$match], $pos); 6318b1b81beSAndreas Gohr break; 6328b1b81beSAndreas Gohr 6338b1b81beSAndreas Gohr case DOKU_LEXER_UNMATCHED: 634*faf3f01bSAndreas Gohr $this->addCall('cdata', [$match], $pos); 6358b1b81beSAndreas Gohr break; 6368b1b81beSAndreas Gohr 6378b1b81beSAndreas Gohr } 6388b1b81beSAndreas Gohr 6398b1b81beSAndreas Gohr return true; 6408b1b81beSAndreas Gohr } 6418b1b81beSAndreas Gohr 6428b1b81beSAndreas Gohr /** 6438b1b81beSAndreas Gohr * @param string $match matched syntax 6448b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 6458b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 6468b1b81beSAndreas Gohr * @return bool mode handled? 6478b1b81beSAndreas Gohr */ 648*faf3f01bSAndreas Gohr public function file($match, $state, $pos) 649*faf3f01bSAndreas Gohr { 6503d491f75SAndreas Gohr return $this->code($match, $state, $pos, 'file'); 6513d491f75SAndreas Gohr } 6523d491f75SAndreas Gohr 6538b1b81beSAndreas Gohr /** 6548b1b81beSAndreas Gohr * @param string $match matched syntax 6558b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 6568b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 6578b1b81beSAndreas Gohr * @param string $type either 'code' or 'file' 6588b1b81beSAndreas Gohr * @return bool mode handled? 6598b1b81beSAndreas Gohr */ 660*faf3f01bSAndreas Gohr public function code($match, $state, $pos, $type = 'code') 661*faf3f01bSAndreas Gohr { 6623d491f75SAndreas Gohr if ($state == DOKU_LEXER_UNMATCHED) { 663ec34bb30SAndreas Gohr $matches = sexplode('>', $match, 2, ''); 664e2d88156SLarsDW223 // Cut out variable options enclosed in [] 665e2d88156SLarsDW223 preg_match('/\[.*\]/', $matches[0], $options); 666e2d88156SLarsDW223 if (!empty($options[0])) { 667e2d88156SLarsDW223 $matches[0] = str_replace($options[0], '', $matches[0]); 668e2d88156SLarsDW223 } 6690139312bSAdrian Lang $param = preg_split('/\s+/', $matches[0], 2, PREG_SPLIT_NO_EMPTY); 670*faf3f01bSAndreas Gohr while (count($param) < 2) $param[] = null; 6710139312bSAdrian Lang // We shortcut html here. 6720139312bSAdrian Lang if ($param[0] == 'html') $param[0] = 'html4strict'; 6730139312bSAdrian Lang if ($param[0] == '-') $param[0] = null; 6740139312bSAdrian Lang array_unshift($param, $matches[1]); 675e2d88156SLarsDW223 if (!empty($options[0])) { 676e2d88156SLarsDW223 $param [] = $this->parse_highlight_options($options[0]); 677e2d88156SLarsDW223 } 6788b1b81beSAndreas Gohr $this->addCall($type, $param, $pos); 6790cecf9d5Sandi } 68044881bd0Shenning.noren return true; 6810cecf9d5Sandi } 6820cecf9d5Sandi 6838b1b81beSAndreas Gohr /** 6848b1b81beSAndreas Gohr * @param string $match matched syntax 6858b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 6868b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 6878b1b81beSAndreas Gohr * @return bool mode handled? 6888b1b81beSAndreas Gohr */ 689*faf3f01bSAndreas Gohr public function acronym($match, $state, $pos) 690*faf3f01bSAndreas Gohr { 691*faf3f01bSAndreas Gohr $this->addCall('acronym', [$match], $pos); 69244881bd0Shenning.noren return true; 6930cecf9d5Sandi } 6940cecf9d5Sandi 6958b1b81beSAndreas Gohr /** 6968b1b81beSAndreas Gohr * @param string $match matched syntax 6978b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 6988b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 6998b1b81beSAndreas Gohr * @return bool mode handled? 7008b1b81beSAndreas Gohr */ 701*faf3f01bSAndreas Gohr public function smiley($match, $state, $pos) 702*faf3f01bSAndreas Gohr { 703*faf3f01bSAndreas Gohr $this->addCall('smiley', [$match], $pos); 70444881bd0Shenning.noren return true; 7050cecf9d5Sandi } 7060cecf9d5Sandi 7078b1b81beSAndreas Gohr /** 7088b1b81beSAndreas Gohr * @param string $match matched syntax 7098b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 7108b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 7118b1b81beSAndreas Gohr * @return bool mode handled? 7128b1b81beSAndreas Gohr */ 713*faf3f01bSAndreas Gohr public function wordblock($match, $state, $pos) 714*faf3f01bSAndreas Gohr { 715*faf3f01bSAndreas Gohr $this->addCall('wordblock', [$match], $pos); 71644881bd0Shenning.noren return true; 7170cecf9d5Sandi } 7180cecf9d5Sandi 7198b1b81beSAndreas Gohr /** 7208b1b81beSAndreas Gohr * @param string $match matched syntax 7218b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 7228b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 7238b1b81beSAndreas Gohr * @return bool mode handled? 7248b1b81beSAndreas Gohr */ 725*faf3f01bSAndreas Gohr public function entity($match, $state, $pos) 726*faf3f01bSAndreas Gohr { 727*faf3f01bSAndreas Gohr $this->addCall('entity', [$match], $pos); 72844881bd0Shenning.noren return true; 7290cecf9d5Sandi } 7300cecf9d5Sandi 7318b1b81beSAndreas Gohr /** 7328b1b81beSAndreas Gohr * @param string $match matched syntax 7338b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 7348b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 7358b1b81beSAndreas Gohr * @return bool mode handled? 7368b1b81beSAndreas Gohr */ 737*faf3f01bSAndreas Gohr public function multiplyentity($match, $state, $pos) 738*faf3f01bSAndreas Gohr { 7390cecf9d5Sandi preg_match_all('/\d+/', $match, $matches); 740*faf3f01bSAndreas Gohr $this->addCall('multiplyentity', [$matches[0][0], $matches[0][1]], $pos); 74144881bd0Shenning.noren return true; 7420cecf9d5Sandi } 7430cecf9d5Sandi 7448b1b81beSAndreas Gohr /** 7458b1b81beSAndreas Gohr * @param string $match matched syntax 7468b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 7478b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 7488b1b81beSAndreas Gohr * @return bool mode handled? 7498b1b81beSAndreas Gohr */ 750*faf3f01bSAndreas Gohr public function singlequoteopening($match, $state, $pos) 751*faf3f01bSAndreas Gohr { 752*faf3f01bSAndreas Gohr $this->addCall('singlequoteopening', [], $pos); 75344881bd0Shenning.noren return true; 7540cecf9d5Sandi } 7550cecf9d5Sandi 7568b1b81beSAndreas Gohr /** 7578b1b81beSAndreas Gohr * @param string $match matched syntax 7588b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 7598b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 7608b1b81beSAndreas Gohr * @return bool mode handled? 7618b1b81beSAndreas Gohr */ 762*faf3f01bSAndreas Gohr public function singlequoteclosing($match, $state, $pos) 763*faf3f01bSAndreas Gohr { 764*faf3f01bSAndreas Gohr $this->addCall('singlequoteclosing', [], $pos); 76544881bd0Shenning.noren return true; 7660cecf9d5Sandi } 7670cecf9d5Sandi 7688b1b81beSAndreas Gohr /** 7698b1b81beSAndreas Gohr * @param string $match matched syntax 7708b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 7718b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 7728b1b81beSAndreas Gohr * @return bool mode handled? 7738b1b81beSAndreas Gohr */ 774*faf3f01bSAndreas Gohr public function apostrophe($match, $state, $pos) 775*faf3f01bSAndreas Gohr { 776*faf3f01bSAndreas Gohr $this->addCall('apostrophe', [], $pos); 77757d757d1SAndreas Gohr return true; 77857d757d1SAndreas Gohr } 77957d757d1SAndreas Gohr 7808b1b81beSAndreas Gohr /** 7818b1b81beSAndreas Gohr * @param string $match matched syntax 7828b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 7838b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 7848b1b81beSAndreas Gohr * @return bool mode handled? 7858b1b81beSAndreas Gohr */ 786*faf3f01bSAndreas Gohr public function doublequoteopening($match, $state, $pos) 787*faf3f01bSAndreas Gohr { 788*faf3f01bSAndreas Gohr $this->addCall('doublequoteopening', [], $pos); 789e950d12fSChristopher Smith $this->status['doublequote']++; 79044881bd0Shenning.noren return true; 7910cecf9d5Sandi } 7920cecf9d5Sandi 7938b1b81beSAndreas Gohr /** 7948b1b81beSAndreas Gohr * @param string $match matched syntax 7958b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 7968b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 7978b1b81beSAndreas Gohr * @return bool mode handled? 7988b1b81beSAndreas Gohr */ 799*faf3f01bSAndreas Gohr public function doublequoteclosing($match, $state, $pos) 800*faf3f01bSAndreas Gohr { 801e950d12fSChristopher Smith if ($this->status['doublequote'] <= 0) { 802e950d12fSChristopher Smith $this->doublequoteopening($match, $state, $pos); 803e950d12fSChristopher Smith } else { 804*faf3f01bSAndreas Gohr $this->addCall('doublequoteclosing', [], $pos); 805e950d12fSChristopher Smith $this->status['doublequote'] = max(0, --$this->status['doublequote']); 806e950d12fSChristopher Smith } 80744881bd0Shenning.noren return true; 8080cecf9d5Sandi } 8090cecf9d5Sandi 8108b1b81beSAndreas Gohr /** 8118b1b81beSAndreas Gohr * @param string $match matched syntax 8128b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 8138b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 8148b1b81beSAndreas Gohr * @return bool mode handled? 8158b1b81beSAndreas Gohr */ 816*faf3f01bSAndreas Gohr public function camelcaselink($match, $state, $pos) 817*faf3f01bSAndreas Gohr { 818*faf3f01bSAndreas Gohr $this->addCall('camelcaselink', [$match], $pos); 81944881bd0Shenning.noren return true; 8200cecf9d5Sandi } 8210cecf9d5Sandi 8228b1b81beSAndreas Gohr /** 8238b1b81beSAndreas Gohr * @param string $match matched syntax 8248b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 8258b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 8268b1b81beSAndreas Gohr * @return bool mode handled? 8270cecf9d5Sandi */ 828*faf3f01bSAndreas Gohr public function internallink($match, $state, $pos) 829*faf3f01bSAndreas Gohr { 8300cecf9d5Sandi // Strip the opening and closing markup 831*faf3f01bSAndreas Gohr $link = preg_replace(['/^\[\[/', '/\]\]$/u'], '', $match); 8320cecf9d5Sandi 8330cecf9d5Sandi // Split title from URL 834ec34bb30SAndreas Gohr $link = sexplode('|', $link, 2); 835ec34bb30SAndreas Gohr if ($link[1] === null) { 8360ea51e63SMatt Perry $link[1] = null; 8370cecf9d5Sandi } elseif (preg_match('/^\{\{[^\}]+\}\}$/', $link[1])) { 8385578eb8fSandi // If the title is an image, convert it to an array containing the image details 839b625487dSandi $link[1] = Doku_Handler_Parse_Media($link[1]); 8400cecf9d5Sandi } 8410b7c14c2Sandi $link[0] = trim($link[0]); 8420cecf9d5Sandi 8430e1c636eSandi //decide which kind of link it is 8440e1c636eSandi 8456efc45a2SDmitry Katsubo if (link_isinterwiki($link[0])) { 8460e1c636eSandi // Interwiki 847ec34bb30SAndreas Gohr $interwiki = sexplode('>', $link[0], 2, ''); 8488b1b81beSAndreas Gohr $this->addCall( 8490cecf9d5Sandi 'interwikilink', 850*faf3f01bSAndreas Gohr [$link[0], $link[1], strtolower($interwiki[0]), $interwiki[1]], 8510cecf9d5Sandi $pos 8520cecf9d5Sandi ); 85315f1b77cSAndreas Gohr } elseif (preg_match('/^\\\\\\\\[^\\\\]+?\\\\/u', $link[0])) { 8540e1c636eSandi // Windows Share 8558b1b81beSAndreas Gohr $this->addCall( 8560cecf9d5Sandi 'windowssharelink', 857*faf3f01bSAndreas Gohr [$link[0], $link[1]], 8580cecf9d5Sandi $pos 8590cecf9d5Sandi ); 8604468cb4cSAndreas Gohr } elseif (preg_match('#^([a-z0-9\-\.+]+?)://#i', $link[0])) { 8610e1c636eSandi // external link (accepts all protocols) 8628b1b81beSAndreas Gohr $this->addCall( 8630cecf9d5Sandi 'externallink', 864*faf3f01bSAndreas Gohr [$link[0], $link[1]], 8650cecf9d5Sandi $pos 8660cecf9d5Sandi ); 8670a1d30bfSchris } elseif (preg_match('<' . PREG_PATTERN_VALID_EMAIL . '>', $link[0])) { 8680a1d30bfSchris // E-Mail (pattern above is defined in inc/mail.php) 8698b1b81beSAndreas Gohr $this->addCall( 870a6755281Sandi 'emaillink', 871*faf3f01bSAndreas Gohr [$link[0], $link[1]], 872a6755281Sandi $pos 873a6755281Sandi ); 8740b7c14c2Sandi } elseif (preg_match('!^#.+!', $link[0])) { 8750b7c14c2Sandi // local link 8768b1b81beSAndreas Gohr $this->addCall( 8770b7c14c2Sandi 'locallink', 878*faf3f01bSAndreas Gohr [substr($link[0], 1), $link[1]], 8790b7c14c2Sandi $pos 8800b7c14c2Sandi ); 8810e1c636eSandi } else { 8820e1c636eSandi // internal link 8838b1b81beSAndreas Gohr $this->addCall( 8840e1c636eSandi 'internallink', 885*faf3f01bSAndreas Gohr [$link[0], $link[1]], 8860e1c636eSandi $pos 8870e1c636eSandi ); 8880cecf9d5Sandi } 8890e1c636eSandi 89044881bd0Shenning.noren return true; 8910cecf9d5Sandi } 8920cecf9d5Sandi 8938b1b81beSAndreas Gohr /** 8948b1b81beSAndreas Gohr * @param string $match matched syntax 8958b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 8968b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 8978b1b81beSAndreas Gohr * @return bool mode handled? 8988b1b81beSAndreas Gohr */ 899*faf3f01bSAndreas Gohr public function filelink($match, $state, $pos) 900*faf3f01bSAndreas Gohr { 901*faf3f01bSAndreas Gohr $this->addCall('filelink', [$match, null], $pos); 90244881bd0Shenning.noren return true; 9030cecf9d5Sandi } 9040cecf9d5Sandi 9058b1b81beSAndreas Gohr /** 9068b1b81beSAndreas Gohr * @param string $match matched syntax 9078b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 9088b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 9098b1b81beSAndreas Gohr * @return bool mode handled? 9108b1b81beSAndreas Gohr */ 911*faf3f01bSAndreas Gohr public function windowssharelink($match, $state, $pos) 912*faf3f01bSAndreas Gohr { 913*faf3f01bSAndreas Gohr $this->addCall('windowssharelink', [$match, null], $pos); 91444881bd0Shenning.noren return true; 9150cecf9d5Sandi } 9160cecf9d5Sandi 9178b1b81beSAndreas Gohr /** 9188b1b81beSAndreas Gohr * @param string $match matched syntax 9198b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 9208b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 9218b1b81beSAndreas Gohr * @return bool mode handled? 9228b1b81beSAndreas Gohr */ 923*faf3f01bSAndreas Gohr public function media($match, $state, $pos) 924*faf3f01bSAndreas Gohr { 9250cecf9d5Sandi $p = Doku_Handler_Parse_Media($match); 9260cecf9d5Sandi 9278b1b81beSAndreas Gohr $this->addCall( 9280cecf9d5Sandi $p['type'], 929*faf3f01bSAndreas Gohr [$p['src'], $p['title'], $p['align'], $p['width'], $p['height'], $p['cache'], $p['linking']], 9300cecf9d5Sandi $pos 9310cecf9d5Sandi ); 93244881bd0Shenning.noren return true; 9330cecf9d5Sandi } 9340cecf9d5Sandi 9358b1b81beSAndreas Gohr /** 9368b1b81beSAndreas Gohr * @param string $match matched syntax 9378b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 9388b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 9398b1b81beSAndreas Gohr * @return bool mode handled? 9408b1b81beSAndreas Gohr */ 941*faf3f01bSAndreas Gohr public function rss($match, $state, $pos) 942*faf3f01bSAndreas Gohr { 943*faf3f01bSAndreas Gohr $link = preg_replace(['/^\{\{rss>/', '/\}\}$/'], '', $match); 9443db95becSAndreas Gohr 9453db95becSAndreas Gohr // get params 946*faf3f01bSAndreas Gohr [$link, $params] = sexplode(' ', $link, 2, ''); 9473db95becSAndreas Gohr 948*faf3f01bSAndreas Gohr $p = []; 9493db95becSAndreas Gohr if (preg_match('/\b(\d+)\b/', $params, $match)) { 9503db95becSAndreas Gohr $p['max'] = $match[1]; 9513db95becSAndreas Gohr } else { 9523db95becSAndreas Gohr $p['max'] = 8; 9533db95becSAndreas Gohr } 9543db95becSAndreas Gohr $p['reverse'] = (preg_match('/rev/', $params)); 9553db95becSAndreas Gohr $p['author'] = (preg_match('/\b(by|author)/', $params)); 9563db95becSAndreas Gohr $p['date'] = (preg_match('/\b(date)/', $params)); 9573db95becSAndreas Gohr $p['details'] = (preg_match('/\b(desc|detail)/', $params)); 95838c6f603SRobin H. Johnson $p['nosort'] = (preg_match('/\b(nosort)\b/', $params)); 9593db95becSAndreas Gohr 9600a69dff7Schris if (preg_match('/\b(\d+)([dhm])\b/', $params, $match)) { 961*faf3f01bSAndreas Gohr $period = ['d' => 86400, 'h' => 3600, 'm' => 60]; 9620a69dff7Schris $p['refresh'] = max(600, $match[1] * $period[$match[2]]); // n * period in seconds, minimum 10 minutes 9630a69dff7Schris } else { 9640a69dff7Schris $p['refresh'] = 14400; // default to 4 hours 9650a69dff7Schris } 9660a69dff7Schris 967*faf3f01bSAndreas Gohr $this->addCall('rss', [$link, $p], $pos); 96844881bd0Shenning.noren return true; 969b625487dSandi } 970b625487dSandi 9718b1b81beSAndreas Gohr /** 9728b1b81beSAndreas Gohr * @param string $match matched syntax 9738b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 9748b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 9758b1b81beSAndreas Gohr * @return bool mode handled? 9768b1b81beSAndreas Gohr */ 977*faf3f01bSAndreas Gohr public function externallink($match, $state, $pos) 978*faf3f01bSAndreas Gohr { 979da9f31c5SAndreas Gohr $url = $match; 980da9f31c5SAndreas Gohr $title = null; 9810cecf9d5Sandi 982da9f31c5SAndreas Gohr // add protocol on simple short URLs 983da9f31c5SAndreas Gohr if (substr($url, 0, 3) == 'ftp' && (substr($url, 0, 6) != 'ftp://')) { 984da9f31c5SAndreas Gohr $title = $url; 985da9f31c5SAndreas Gohr $url = 'ftp://' . $url; 986da9f31c5SAndreas Gohr } 987da9f31c5SAndreas Gohr if (substr($url, 0, 3) == 'www' && (substr($url, 0, 7) != 'http://')) { 988da9f31c5SAndreas Gohr $title = $url; 989da9f31c5SAndreas Gohr $url = 'http://' . $url; 990da9f31c5SAndreas Gohr } 991da9f31c5SAndreas Gohr 992*faf3f01bSAndreas Gohr $this->addCall('externallink', [$url, $title], $pos); 99344881bd0Shenning.noren return true; 9940cecf9d5Sandi } 9950cecf9d5Sandi 9968b1b81beSAndreas Gohr /** 9978b1b81beSAndreas Gohr * @param string $match matched syntax 9988b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 9998b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 10008b1b81beSAndreas Gohr * @return bool mode handled? 10018b1b81beSAndreas Gohr */ 1002*faf3f01bSAndreas Gohr public function emaillink($match, $state, $pos) 1003*faf3f01bSAndreas Gohr { 1004*faf3f01bSAndreas Gohr $email = preg_replace(['/^</', '/>$/'], '', $match); 1005*faf3f01bSAndreas Gohr $this->addCall('emaillink', [$email, null], $pos); 100644881bd0Shenning.noren return true; 10070cecf9d5Sandi } 10080cecf9d5Sandi 10098b1b81beSAndreas Gohr /** 10108b1b81beSAndreas Gohr * @param string $match matched syntax 10118b1b81beSAndreas Gohr * @param int $state a LEXER_STATE_* constant 10128b1b81beSAndreas Gohr * @param int $pos byte position in the original source file 10138b1b81beSAndreas Gohr * @return bool mode handled? 10148b1b81beSAndreas Gohr */ 1015*faf3f01bSAndreas Gohr public function table($match, $state, $pos) 1016*faf3f01bSAndreas Gohr { 10170cecf9d5Sandi switch ($state) { 10180cecf9d5Sandi 10190cecf9d5Sandi case DOKU_LEXER_ENTER: 10200cecf9d5Sandi 10218b1b81beSAndreas Gohr $this->callWriter = new Table($this->callWriter); 10220cecf9d5Sandi 1023*faf3f01bSAndreas Gohr $this->addCall('table_start', [$pos + 1], $pos); 10240cecf9d5Sandi if (trim($match) == '^') { 1025*faf3f01bSAndreas Gohr $this->addCall('tableheader', [], $pos); 10260cecf9d5Sandi } else { 1027*faf3f01bSAndreas Gohr $this->addCall('tablecell', [], $pos); 10280cecf9d5Sandi } 10290cecf9d5Sandi break; 10300cecf9d5Sandi 10310cecf9d5Sandi case DOKU_LEXER_EXIT: 1032*faf3f01bSAndreas Gohr $this->addCall('table_end', [$pos], $pos); 10335c2aad12SAndreas Gohr /** @var Table $reWriter */ 10348b1b81beSAndreas Gohr $reWriter = $this->callWriter; 10358b1b81beSAndreas Gohr $this->callWriter = $reWriter->process(); 10360cecf9d5Sandi break; 10370cecf9d5Sandi 10380cecf9d5Sandi case DOKU_LEXER_UNMATCHED: 10390cecf9d5Sandi if (trim($match) != '') { 1040*faf3f01bSAndreas Gohr $this->addCall('cdata', [$match], $pos); 10410cecf9d5Sandi } 10420cecf9d5Sandi break; 10430cecf9d5Sandi 10440cecf9d5Sandi case DOKU_LEXER_MATCHED: 10459ab75d9eSAndreas Gohr if ($match == ' ') { 1046*faf3f01bSAndreas Gohr $this->addCall('cdata', [$match], $pos); 104725b97867Shakan.sandell } elseif (preg_match('/:::/', $match)) { 1048*faf3f01bSAndreas Gohr $this->addCall('rowspan', [$match], $pos); 1049e205b721SAndreas Gohr } elseif (preg_match('/\t+/', $match)) { 1050*faf3f01bSAndreas Gohr $this->addCall('table_align', [$match], $pos); 1051e205b721SAndreas Gohr } elseif (preg_match('/ {2,}/', $match)) { 1052*faf3f01bSAndreas Gohr $this->addCall('table_align', [$match], $pos); 10530cecf9d5Sandi } elseif ($match == "\n|") { 1054*faf3f01bSAndreas Gohr $this->addCall('table_row', [], $pos); 1055*faf3f01bSAndreas Gohr $this->addCall('tablecell', [], $pos); 10560cecf9d5Sandi } elseif ($match == "\n^") { 1057*faf3f01bSAndreas Gohr $this->addCall('table_row', [], $pos); 1058*faf3f01bSAndreas Gohr $this->addCall('tableheader', [], $pos); 10590cecf9d5Sandi } elseif ($match == '|') { 1060*faf3f01bSAndreas Gohr $this->addCall('tablecell', [], $pos); 10610cecf9d5Sandi } elseif ($match == '^') { 1062*faf3f01bSAndreas Gohr $this->addCall('tableheader', [], $pos); 10630cecf9d5Sandi } 10640cecf9d5Sandi break; 10650cecf9d5Sandi } 106644881bd0Shenning.noren return true; 10670cecf9d5Sandi } 10688b1b81beSAndreas Gohr 10698b1b81beSAndreas Gohr // endregion modes 10700cecf9d5Sandi} 10710cecf9d5Sandi 10720cecf9d5Sandi//------------------------------------------------------------------------ 1073*faf3f01bSAndreas Gohrfunction Doku_Handler_Parse_Media($match) 1074*faf3f01bSAndreas Gohr{ 1075d8ab8746SAndreas Gohr 10760cecf9d5Sandi // Strip the opening and closing markup 1077*faf3f01bSAndreas Gohr $link = preg_replace(['/^\{\{/', '/\}\}$/u'], '', $match); 10780cecf9d5Sandi 10790cecf9d5Sandi // Split title from URL 1080ec34bb30SAndreas Gohr $link = sexplode('|', $link, 2); 10810cecf9d5Sandi 10820cecf9d5Sandi // Check alignment 10830cecf9d5Sandi $ralign = (bool)preg_match('/^ /', $link[0]); 10840cecf9d5Sandi $lalign = (bool)preg_match('/ $/', $link[0]); 10850cecf9d5Sandi 10860cecf9d5Sandi // Logic = what's that ;)... 10870cecf9d5Sandi if ($lalign & $ralign) { 10880cecf9d5Sandi $align = 'center'; 10890cecf9d5Sandi } elseif ($ralign) { 10900cecf9d5Sandi $align = 'right'; 10910cecf9d5Sandi } elseif ($lalign) { 10920cecf9d5Sandi $align = 'left'; 10930cecf9d5Sandi } else { 10940ea51e63SMatt Perry $align = null; 10950cecf9d5Sandi } 10960cecf9d5Sandi 10970cecf9d5Sandi // The title... 10980cecf9d5Sandi if (!isset($link[1])) { 10990ea51e63SMatt Perry $link[1] = null; 11000cecf9d5Sandi } 11010cecf9d5Sandi 11024826ab45Sandi //remove aligning spaces 11034826ab45Sandi $link[0] = trim($link[0]); 11040cecf9d5Sandi 11054826ab45Sandi //split into src and parameters (using the very last questionmark) 11064826ab45Sandi $pos = strrpos($link[0], '?'); 11074826ab45Sandi if ($pos !== false) { 11084826ab45Sandi $src = substr($link[0], 0, $pos); 11094826ab45Sandi $param = substr($link[0], $pos + 1); 11100cecf9d5Sandi } else { 11114826ab45Sandi $src = $link[0]; 11124826ab45Sandi $param = ''; 11130cecf9d5Sandi } 11140cecf9d5Sandi 11154826ab45Sandi //parse width and height 11164826ab45Sandi if (preg_match('#(\d+)(x(\d+))?#i', $param, $size)) { 1117*faf3f01bSAndreas Gohr $w = empty($size[1]) ? null : $size[1]; 1118*faf3f01bSAndreas Gohr $h = empty($size[3]) ? null : $size[3]; 1119fc1c55b1Shfuecks } else { 11200ea51e63SMatt Perry $w = null; 11210ea51e63SMatt Perry $h = null; 11220cecf9d5Sandi } 11230cecf9d5Sandi 1124dc673a5bSjoe.lapp //get linking command 1125d35ab615Shenning.noren if (preg_match('/nolink/i', $param)) { 1126dc673a5bSjoe.lapp $linking = 'nolink'; 1127d35ab615Shenning.noren } elseif (preg_match('/direct/i', $param)) { 1128dc673a5bSjoe.lapp $linking = 'direct'; 11298acb3108SAndreas Gohr } elseif (preg_match('/linkonly/i', $param)) { 11308acb3108SAndreas Gohr $linking = 'linkonly'; 1131dc673a5bSjoe.lapp } else { 1132dc673a5bSjoe.lapp $linking = 'details'; 1133dc673a5bSjoe.lapp } 1134dc673a5bSjoe.lapp 11354826ab45Sandi //get caching command 11364826ab45Sandi if (preg_match('/(nocache|recache)/i', $param, $cachemode)) { 11374826ab45Sandi $cache = $cachemode[1]; 11380cecf9d5Sandi } else { 11394826ab45Sandi $cache = 'cache'; 11400cecf9d5Sandi } 1141d8ab8746SAndreas Gohr 11426efc45a2SDmitry Katsubo // Check whether this is a local or remote image or interwiki 11436efc45a2SDmitry Katsubo if (media_isexternal($src) || link_isinterwiki($src)) { 11444826ab45Sandi $call = 'externalmedia'; 11450cecf9d5Sandi } else { 11464826ab45Sandi $call = 'internalmedia'; 11470cecf9d5Sandi } 11480cecf9d5Sandi 1149*faf3f01bSAndreas Gohr $params = [ 11500cecf9d5Sandi 'type' => $call, 11514826ab45Sandi 'src' => $src, 11520cecf9d5Sandi 'title' => $link[1], 11530cecf9d5Sandi 'align' => $align, 11544826ab45Sandi 'width' => $w, 11554826ab45Sandi 'height' => $h, 11560cecf9d5Sandi 'cache' => $cache, 1157*faf3f01bSAndreas Gohr 'linking' => $linking 1158*faf3f01bSAndreas Gohr ]; 11590cecf9d5Sandi 11600cecf9d5Sandi return $params; 11610cecf9d5Sandi} 11620cecf9d5Sandi 1163