3 use dokuwiki\Extension\Event
;
4 use dokuwiki\Extension\SyntaxPlugin
;
5 use dokuwiki\Parsing\Handler\Block
;
6 use dokuwiki\Parsing\Handler\CallWriter
;
7 use dokuwiki\Parsing\Handler\CallWriterInterface
;
8 use dokuwiki\Parsing\Handler\Lists
;
9 use dokuwiki\Parsing\Handler\Nest
;
10 use dokuwiki\Parsing\Handler\Preformatted
;
11 use dokuwiki\Parsing\Handler\Quote
;
12 use dokuwiki\Parsing\Handler\Table
;
19 /** @var CallWriterInterface */
20 protected $callWriter;
22 /** @var array The current CallWriter will write directly to this list of calls, Parser reads it */
25 /** @var array internal status holders for some modes */
31 /** @var bool should blocks be rewritten? FIXME seems to always be true */
32 protected $rewriteBlocks = true;
35 * @var bool are we in a footnote already?
40 * Doku_Handler constructor.
42 public function __construct()
44 $this->callWriter
= new CallWriter($this);
48 * Add a new call by passing it to the current CallWriter
50 * @param string $handler handler method name (see mode handlers below)
51 * @param mixed $args arguments for this call
52 * @param int $pos byte position in the original source file
54 public function addCall($handler, $args, $pos)
56 $call = [$handler, $args, $pos];
57 $this->callWriter
->writeCall($call);
61 * Accessor for the current CallWriter
63 * @return CallWriterInterface
65 public function getCallWriter()
67 return $this->callWriter
;
71 * Set a new CallWriter
73 * @param CallWriterInterface $callWriter
75 public function setCallWriter($callWriter)
77 $this->callWriter
= $callWriter;
81 * Return the current internal status of the given name
83 * @param string $status
86 public function getStatus($status)
88 if (!isset($this->status
[$status])) return null;
89 return $this->status
[$status];
93 * Set a new internal status
95 * @param string $status
98 public function setStatus($status, $value)
100 $this->status
[$status] = $value;
103 /** @deprecated 2019-10-31 use addCall() instead */
104 public function _addCall($handler, $args, $pos)
106 dbg_deprecated('addCall');
107 $this->addCall($handler, $args, $pos);
111 * Similar to addCall, but adds a plugin call
113 * @param string $plugin name of the plugin
114 * @param mixed $args arguments for this call
115 * @param int $state a LEXER_STATE_* constant
116 * @param int $pos byte position in the original source file
117 * @param string $match matched syntax
119 public function addPluginCall($plugin, $args, $state, $pos, $match)
121 $call = ['plugin', [$plugin, $args, $state, $match], $pos];
122 $this->callWriter
->writeCall($call);
128 * Called from the parser. Calls finalise() on the call writer, closes open
129 * sections, rewrites blocks and adds document_start and document_end calls.
131 * @triggers PARSER_HANDLER_DONE
133 public function finalize()
135 $this->callWriter
->finalise();
137 if ($this->status
['section']) {
138 $last_call = end($this->calls
);
139 $this->calls
[] = ['section_close', [], $last_call[2]];
142 if ($this->rewriteBlocks
) {
144 $this->calls
= $B->process($this->calls
);
147 Event
::createAndTrigger('PARSER_HANDLER_DONE', $this);
149 array_unshift($this->calls
, ['document_start', [], 0]);
150 $last_call = end($this->calls
);
151 $this->calls
[] = ['document_end', [], $last_call[2]];
155 * fetch the current call and advance the pointer to the next one
157 * @fixme seems to be unused?
160 public function fetch()
162 $call = current($this->calls
);
163 if ($call !== false) {
164 next($this->calls
); //advance the pointer
172 * Internal function for parsing highlight options.
173 * $options is parsed for key value pairs separated by commas.
174 * A value might also be missing in which case the value will simple
175 * be set to true. Commas in strings are ignored, e.g. option="4,56"
176 * will work as expected and will only create one entry.
178 * @param string $options space separated list of key-value pairs,
179 * e.g. option1=123, option2="456"
180 * @return array|null Array of key-value pairs $array['key'] = 'value';
181 * or null if no entries found
183 protected function parse_highlight_options($options)
186 preg_match_all('/(\w+(?:="[^"]*"))|(\w+(?:=[^\s]*))|(\w+[^=\s\]])(?:\s*)/', $options, $matches, PREG_SET_ORDER
);
187 foreach ($matches as $match) {
188 $equal_sign = strpos($match [0], '=');
189 if ($equal_sign === false) {
190 $key = trim($match[0]);
193 $key = substr($match[0], 0, $equal_sign);
194 $value = substr($match[0], $equal_sign +
1);
195 $value = trim($value, '"');
196 if (strlen($value) > 0) {
197 $result [$key] = $value;
204 // Check for supported options
205 $result = array_intersect_key(
208 'enable_line_numbers',
209 'start_line_numbers_at',
210 'highlight_lines_extra',
211 'enable_keyword_links'
216 if (isset($result['enable_line_numbers'])) {
217 if ($result['enable_line_numbers'] === 'false') {
218 $result['enable_line_numbers'] = false;
220 $result['enable_line_numbers'] = (bool)$result['enable_line_numbers'];
222 if (isset($result['highlight_lines_extra'])) {
223 $result['highlight_lines_extra'] = array_map('intval', explode(',', $result['highlight_lines_extra']));
224 $result['highlight_lines_extra'] = array_filter($result['highlight_lines_extra']);
225 $result['highlight_lines_extra'] = array_unique($result['highlight_lines_extra']);
227 if (isset($result['start_line_numbers_at'])) {
228 $result['start_line_numbers_at'] = (int)$result['start_line_numbers_at'];
230 if (isset($result['enable_keyword_links'])) {
231 if ($result['enable_keyword_links'] === 'false') {
232 $result['enable_keyword_links'] = false;
234 $result['enable_keyword_links'] = (bool)$result['enable_keyword_links'];
236 if (count($result) == 0) {
244 * Simplifies handling for the formatting tags which all behave the same
246 * @param string $match matched syntax
247 * @param int $state a LEXER_STATE_* constant
248 * @param int $pos byte position in the original source file
249 * @param string $name actual mode name
251 protected function nestingTag($match, $state, $pos, $name)
254 case DOKU_LEXER_ENTER
:
255 $this->addCall($name . '_open', [], $pos);
257 case DOKU_LEXER_EXIT
:
258 $this->addCall($name . '_close', [], $pos);
260 case DOKU_LEXER_UNMATCHED
:
261 $this->addCall('cdata', [$match], $pos);
268 * The following methods define the handlers for the different Syntax modes
270 * The handlers are called from dokuwiki\Parsing\Lexer\Lexer\invokeParser()
272 * @todo it might make sense to move these into their own class or merge them with the
273 * ParserMode classes some time.
275 // region mode handlers
278 * Special plugin handler
280 * This handler is called for all modes starting with 'plugin_'.
281 * An additional parameter with the plugin name is passed. The plugin's handle()
282 * method is called here
284 * @param string $match matched syntax
285 * @param int $state a LEXER_STATE_* constant
286 * @param int $pos byte position in the original source file
287 * @param string $pluginname name of the plugin
288 * @return bool mode handled?
289 * @author Andreas Gohr <andi@splitbrain.org>
292 public function plugin($match, $state, $pos, $pluginname)
295 /** @var SyntaxPlugin $plugin */
296 $plugin = plugin_load('syntax', $pluginname);
297 if ($plugin != null) {
298 $data = $plugin->handle($match, $state, $pos, $this);
300 if ($data !== false) {
301 $this->addPluginCall($pluginname, $data, $state, $pos, $match);
307 * @param string $match matched syntax
308 * @param int $state a LEXER_STATE_* constant
309 * @param int $pos byte position in the original source file
310 * @return bool mode handled?
312 public function base($match, $state, $pos)
314 if ($state === DOKU_LEXER_UNMATCHED
) {
315 $this->addCall('cdata', [$match], $pos);
322 * @param string $match matched syntax
323 * @param int $state a LEXER_STATE_* constant
324 * @param int $pos byte position in the original source file
325 * @return bool mode handled?
327 public function header($match, $state, $pos)
329 // get level and title
330 $title = trim($match);
331 $level = 7 - strspn($title, '=');
332 if ($level < 1) $level = 1;
333 $title = trim($title, '=');
334 $title = trim($title);
336 if ($this->status
['section']) $this->addCall('section_close', [], $pos);
338 $this->addCall('header', [$title, $level, $pos], $pos);
340 $this->addCall('section_open', [$level], $pos);
341 $this->status
['section'] = true;
346 * @param string $match matched syntax
347 * @param int $state a LEXER_STATE_* constant
348 * @param int $pos byte position in the original source file
349 * @return bool mode handled?
351 public function notoc($match, $state, $pos)
353 $this->addCall('notoc', [], $pos);
358 * @param string $match matched syntax
359 * @param int $state a LEXER_STATE_* constant
360 * @param int $pos byte position in the original source file
361 * @return bool mode handled?
363 public function nocache($match, $state, $pos)
365 $this->addCall('nocache', [], $pos);
370 * @param string $match matched syntax
371 * @param int $state a LEXER_STATE_* constant
372 * @param int $pos byte position in the original source file
373 * @return bool mode handled?
375 public function linebreak($match, $state, $pos)
377 $this->addCall('linebreak', [], $pos);
382 * @param string $match matched syntax
383 * @param int $state a LEXER_STATE_* constant
384 * @param int $pos byte position in the original source file
385 * @return bool mode handled?
387 public function eol($match, $state, $pos)
389 $this->addCall('eol', [], $pos);
394 * @param string $match matched syntax
395 * @param int $state a LEXER_STATE_* constant
396 * @param int $pos byte position in the original source file
397 * @return bool mode handled?
399 public function hr($match, $state, $pos)
401 $this->addCall('hr', [], $pos);
406 * @param string $match matched syntax
407 * @param int $state a LEXER_STATE_* constant
408 * @param int $pos byte position in the original source file
409 * @return bool mode handled?
411 public function strong($match, $state, $pos)
413 $this->nestingTag($match, $state, $pos, 'strong');
418 * @param string $match matched syntax
419 * @param int $state a LEXER_STATE_* constant
420 * @param int $pos byte position in the original source file
421 * @return bool mode handled?
423 public function emphasis($match, $state, $pos)
425 $this->nestingTag($match, $state, $pos, 'emphasis');
430 * @param string $match matched syntax
431 * @param int $state a LEXER_STATE_* constant
432 * @param int $pos byte position in the original source file
433 * @return bool mode handled?
435 public function underline($match, $state, $pos)
437 $this->nestingTag($match, $state, $pos, 'underline');
442 * @param string $match matched syntax
443 * @param int $state a LEXER_STATE_* constant
444 * @param int $pos byte position in the original source file
445 * @return bool mode handled?
447 public function monospace($match, $state, $pos)
449 $this->nestingTag($match, $state, $pos, 'monospace');
454 * @param string $match matched syntax
455 * @param int $state a LEXER_STATE_* constant
456 * @param int $pos byte position in the original source file
457 * @return bool mode handled?
459 public function subscript($match, $state, $pos)
461 $this->nestingTag($match, $state, $pos, 'subscript');
466 * @param string $match matched syntax
467 * @param int $state a LEXER_STATE_* constant
468 * @param int $pos byte position in the original source file
469 * @return bool mode handled?
471 public function superscript($match, $state, $pos)
473 $this->nestingTag($match, $state, $pos, 'superscript');
478 * @param string $match matched syntax
479 * @param int $state a LEXER_STATE_* constant
480 * @param int $pos byte position in the original source file
481 * @return bool mode handled?
483 public function deleted($match, $state, $pos)
485 $this->nestingTag($match, $state, $pos, 'deleted');
490 * @param string $match matched syntax
491 * @param int $state a LEXER_STATE_* constant
492 * @param int $pos byte position in the original source file
493 * @return bool mode handled?
495 public function footnote($match, $state, $pos)
497 if (!isset($this->footnote
)) $this->footnote
= false;
500 case DOKU_LEXER_ENTER
:
501 // footnotes can not be nested - however due to limitations in lexer it can't be prevented
502 // we will still enter a new footnote mode, we just do nothing
503 if ($this->footnote
) {
504 $this->addCall('cdata', [$match], $pos);
507 $this->footnote
= true;
509 $this->callWriter
= new Nest($this->callWriter
, 'footnote_close');
510 $this->addCall('footnote_open', [], $pos);
512 case DOKU_LEXER_EXIT
:
513 // check whether we have already exitted the footnote mode, can happen if the modes were nested
514 if (!$this->footnote
) {
515 $this->addCall('cdata', [$match], $pos);
519 $this->footnote
= false;
520 $this->addCall('footnote_close', [], $pos);
522 /** @var Nest $reWriter */
523 $reWriter = $this->callWriter
;
524 $this->callWriter
= $reWriter->process();
526 case DOKU_LEXER_UNMATCHED
:
527 $this->addCall('cdata', [$match], $pos);
534 * @param string $match matched syntax
535 * @param int $state a LEXER_STATE_* constant
536 * @param int $pos byte position in the original source file
537 * @return bool mode handled?
539 public function listblock($match, $state, $pos)
542 case DOKU_LEXER_ENTER
:
543 $this->callWriter
= new Lists($this->callWriter
);
544 $this->addCall('list_open', [$match], $pos);
546 case DOKU_LEXER_EXIT
:
547 $this->addCall('list_close', [], $pos);
548 /** @var Lists $reWriter */
549 $reWriter = $this->callWriter
;
550 $this->callWriter
= $reWriter->process();
552 case DOKU_LEXER_MATCHED
:
553 $this->addCall('list_item', [$match], $pos);
555 case DOKU_LEXER_UNMATCHED
:
556 $this->addCall('cdata', [$match], $pos);
563 * @param string $match matched syntax
564 * @param int $state a LEXER_STATE_* constant
565 * @param int $pos byte position in the original source file
566 * @return bool mode handled?
568 public function unformatted($match, $state, $pos)
570 if ($state == DOKU_LEXER_UNMATCHED
) {
571 $this->addCall('unformatted', [$match], $pos);
577 * @param string $match matched syntax
578 * @param int $state a LEXER_STATE_* constant
579 * @param int $pos byte position in the original source file
580 * @return bool mode handled?
582 public function preformatted($match, $state, $pos)
585 case DOKU_LEXER_ENTER
:
586 $this->callWriter
= new Preformatted($this->callWriter
);
587 $this->addCall('preformatted_start', [], $pos);
589 case DOKU_LEXER_EXIT
:
590 $this->addCall('preformatted_end', [], $pos);
591 /** @var Preformatted $reWriter */
592 $reWriter = $this->callWriter
;
593 $this->callWriter
= $reWriter->process();
595 case DOKU_LEXER_MATCHED
:
596 $this->addCall('preformatted_newline', [], $pos);
598 case DOKU_LEXER_UNMATCHED
:
599 $this->addCall('preformatted_content', [$match], $pos);
607 * @param string $match matched syntax
608 * @param int $state a LEXER_STATE_* constant
609 * @param int $pos byte position in the original source file
610 * @return bool mode handled?
612 public function quote($match, $state, $pos)
617 case DOKU_LEXER_ENTER
:
618 $this->callWriter
= new Quote($this->callWriter
);
619 $this->addCall('quote_start', [$match], $pos);
622 case DOKU_LEXER_EXIT
:
623 $this->addCall('quote_end', [], $pos);
624 /** @var Lists $reWriter */
625 $reWriter = $this->callWriter
;
626 $this->callWriter
= $reWriter->process();
629 case DOKU_LEXER_MATCHED
:
630 $this->addCall('quote_newline', [$match], $pos);
633 case DOKU_LEXER_UNMATCHED
:
634 $this->addCall('cdata', [$match], $pos);
643 * @param string $match matched syntax
644 * @param int $state a LEXER_STATE_* constant
645 * @param int $pos byte position in the original source file
646 * @return bool mode handled?
648 public function file($match, $state, $pos)
650 return $this->code($match, $state, $pos, 'file');
654 * @param string $match matched syntax
655 * @param int $state a LEXER_STATE_* constant
656 * @param int $pos byte position in the original source file
657 * @param string $type either 'code' or 'file'
658 * @return bool mode handled?
660 public function code($match, $state, $pos, $type = 'code')
662 if ($state == DOKU_LEXER_UNMATCHED
) {
663 $matches = sexplode('>', $match, 2, '');
664 // Cut out variable options enclosed in []
665 preg_match('/\[.*\]/', $matches[0], $options);
666 if (!empty($options[0])) {
667 $matches[0] = str_replace($options[0], '', $matches[0]);
669 $param = preg_split('/\s+/', $matches[0], 2, PREG_SPLIT_NO_EMPTY
);
670 while (count($param) < 2) $param[] = null;
671 // We shortcut html here.
672 if ($param[0] == 'html') $param[0] = 'html4strict';
673 if ($param[0] == '-') $param[0] = null;
674 array_unshift($param, $matches[1]);
675 if (!empty($options[0])) {
676 $param [] = $this->parse_highlight_options($options[0]);
678 $this->addCall($type, $param, $pos);
684 * @param string $match matched syntax
685 * @param int $state a LEXER_STATE_* constant
686 * @param int $pos byte position in the original source file
687 * @return bool mode handled?
689 public function acronym($match, $state, $pos)
691 $this->addCall('acronym', [$match], $pos);
696 * @param string $match matched syntax
697 * @param int $state a LEXER_STATE_* constant
698 * @param int $pos byte position in the original source file
699 * @return bool mode handled?
701 public function smiley($match, $state, $pos)
703 $this->addCall('smiley', [$match], $pos);
708 * @param string $match matched syntax
709 * @param int $state a LEXER_STATE_* constant
710 * @param int $pos byte position in the original source file
711 * @return bool mode handled?
713 public function wordblock($match, $state, $pos)
715 $this->addCall('wordblock', [$match], $pos);
720 * @param string $match matched syntax
721 * @param int $state a LEXER_STATE_* constant
722 * @param int $pos byte position in the original source file
723 * @return bool mode handled?
725 public function entity($match, $state, $pos)
727 $this->addCall('entity', [$match], $pos);
732 * @param string $match matched syntax
733 * @param int $state a LEXER_STATE_* constant
734 * @param int $pos byte position in the original source file
735 * @return bool mode handled?
737 public function multiplyentity($match, $state, $pos)
739 preg_match_all('/\d+/', $match, $matches);
740 $this->addCall('multiplyentity', [$matches[0][0], $matches[0][1]], $pos);
745 * @param string $match matched syntax
746 * @param int $state a LEXER_STATE_* constant
747 * @param int $pos byte position in the original source file
748 * @return bool mode handled?
750 public function singlequoteopening($match, $state, $pos)
752 $this->addCall('singlequoteopening', [], $pos);
757 * @param string $match matched syntax
758 * @param int $state a LEXER_STATE_* constant
759 * @param int $pos byte position in the original source file
760 * @return bool mode handled?
762 public function singlequoteclosing($match, $state, $pos)
764 $this->addCall('singlequoteclosing', [], $pos);
769 * @param string $match matched syntax
770 * @param int $state a LEXER_STATE_* constant
771 * @param int $pos byte position in the original source file
772 * @return bool mode handled?
774 public function apostrophe($match, $state, $pos)
776 $this->addCall('apostrophe', [], $pos);
781 * @param string $match matched syntax
782 * @param int $state a LEXER_STATE_* constant
783 * @param int $pos byte position in the original source file
784 * @return bool mode handled?
786 public function doublequoteopening($match, $state, $pos)
788 $this->addCall('doublequoteopening', [], $pos);
789 $this->status
['doublequote']++
;
794 * @param string $match matched syntax
795 * @param int $state a LEXER_STATE_* constant
796 * @param int $pos byte position in the original source file
797 * @return bool mode handled?
799 public function doublequoteclosing($match, $state, $pos)
801 if ($this->status
['doublequote'] <= 0) {
802 $this->doublequoteopening($match, $state, $pos);
804 $this->addCall('doublequoteclosing', [], $pos);
805 $this->status
['doublequote'] = max(0, --$this->status
['doublequote']);
811 * @param string $match matched syntax
812 * @param int $state a LEXER_STATE_* constant
813 * @param int $pos byte position in the original source file
814 * @return bool mode handled?
816 public function camelcaselink($match, $state, $pos)
818 $this->addCall('camelcaselink', [$match], $pos);
823 * @param string $match matched syntax
824 * @param int $state a LEXER_STATE_* constant
825 * @param int $pos byte position in the original source file
826 * @return bool mode handled?
828 public function internallink($match, $state, $pos)
830 // Strip the opening and closing markup
831 $link = preg_replace(['/^\[\[/', '/\]\]$/u'], '', $match);
833 // Split title from URL
834 $link = sexplode('|', $link, 2);
835 if ($link[1] === null) {
837 } elseif (preg_match('/^\{\{[^\}]+\}\}$/', $link[1])) {
838 // If the title is an image, convert it to an array containing the image details
839 $link[1] = Doku_Handler_Parse_Media($link[1]);
841 $link[0] = trim($link[0]);
843 //decide which kind of link it is
845 if (link_isinterwiki($link[0])) {
847 $interwiki = sexplode('>', $link[0], 2, '');
850 [$link[0], $link[1], strtolower($interwiki[0]), $interwiki[1]],
853 } elseif (preg_match('/^\\\\\\\\[^\\\\]+?\\\\/u', $link[0])) {
857 [$link[0], $link[1]],
860 } elseif (preg_match('#^([a-z0-9\-\.+]+?)://#i', $link[0])) {
861 // external link (accepts all protocols)
864 [$link[0], $link[1]],
867 } elseif (preg_match('<' . PREG_PATTERN_VALID_EMAIL
. '>', $link[0])) {
868 // E-Mail (pattern above is defined in inc/mail.php)
871 [$link[0], $link[1]],
874 } elseif (preg_match('!^#.+!', $link[0])) {
878 [substr($link[0], 1), $link[1]],
885 [$link[0], $link[1]],
894 * @param string $match matched syntax
895 * @param int $state a LEXER_STATE_* constant
896 * @param int $pos byte position in the original source file
897 * @return bool mode handled?
899 public function filelink($match, $state, $pos)
901 $this->addCall('filelink', [$match, null], $pos);
906 * @param string $match matched syntax
907 * @param int $state a LEXER_STATE_* constant
908 * @param int $pos byte position in the original source file
909 * @return bool mode handled?
911 public function windowssharelink($match, $state, $pos)
913 $this->addCall('windowssharelink', [$match, null], $pos);
918 * @param string $match matched syntax
919 * @param int $state a LEXER_STATE_* constant
920 * @param int $pos byte position in the original source file
921 * @return bool mode handled?
923 public function media($match, $state, $pos)
925 $p = Doku_Handler_Parse_Media($match);
929 [$p['src'], $p['title'], $p['align'], $p['width'], $p['height'], $p['cache'], $p['linking']],
936 * @param string $match matched syntax
937 * @param int $state a LEXER_STATE_* constant
938 * @param int $pos byte position in the original source file
939 * @return bool mode handled?
941 public function rss($match, $state, $pos)
943 $link = preg_replace(['/^\{\{rss>/', '/\}\}$/'], '', $match);
946 [$link, $params] = sexplode(' ', $link, 2, '');
949 if (preg_match('/\b(\d+)\b/', $params, $match)) {
950 $p['max'] = $match[1];
954 $p['reverse'] = (preg_match('/rev/', $params));
955 $p['author'] = (preg_match('/\b(by|author)/', $params));
956 $p['date'] = (preg_match('/\b(date)/', $params));
957 $p['details'] = (preg_match('/\b(desc|detail)/', $params));
958 $p['nosort'] = (preg_match('/\b(nosort)\b/', $params));
960 if (preg_match('/\b(\d+)([dhm])\b/', $params, $match)) {
961 $period = ['d' => 86400, 'h' => 3600, 'm' => 60];
962 $p['refresh'] = max(600, $match[1] * $period[$match[2]]); // n * period in seconds, minimum 10 minutes
964 $p['refresh'] = 14400; // default to 4 hours
967 $this->addCall('rss', [$link, $p], $pos);
972 * @param string $match matched syntax
973 * @param int $state a LEXER_STATE_* constant
974 * @param int $pos byte position in the original source file
975 * @return bool mode handled?
977 public function externallink($match, $state, $pos)
982 // add protocol on simple short URLs
983 if (substr($url, 0, 3) == 'ftp' && (substr($url, 0, 6) != 'ftp://')) {
985 $url = 'ftp://' . $url;
987 if (substr($url, 0, 3) == 'www' && (substr($url, 0, 7) != 'http://')) {
989 $url = 'http://' . $url;
992 $this->addCall('externallink', [$url, $title], $pos);
997 * @param string $match matched syntax
998 * @param int $state a LEXER_STATE_* constant
999 * @param int $pos byte position in the original source file
1000 * @return bool mode handled?
1002 public function emaillink($match, $state, $pos)
1004 $email = preg_replace(['/^</', '/>$/'], '', $match);
1005 $this->addCall('emaillink', [$email, null], $pos);
1010 * @param string $match matched syntax
1011 * @param int $state a LEXER_STATE_* constant
1012 * @param int $pos byte position in the original source file
1013 * @return bool mode handled?
1015 public function table($match, $state, $pos)
1019 case DOKU_LEXER_ENTER
:
1021 $this->callWriter
= new Table($this->callWriter
);
1023 $this->addCall('table_start', [$pos +
1], $pos);
1024 if (trim($match) == '^') {
1025 $this->addCall('tableheader', [], $pos);
1027 $this->addCall('tablecell', [], $pos);
1031 case DOKU_LEXER_EXIT
:
1032 $this->addCall('table_end', [$pos], $pos);
1033 /** @var Table $reWriter */
1034 $reWriter = $this->callWriter
;
1035 $this->callWriter
= $reWriter->process();
1038 case DOKU_LEXER_UNMATCHED
:
1039 if (trim($match) != '') {
1040 $this->addCall('cdata', [$match], $pos);
1044 case DOKU_LEXER_MATCHED
:
1045 if ($match == ' ') {
1046 $this->addCall('cdata', [$match], $pos);
1047 } elseif (preg_match('/:::/', $match)) {
1048 $this->addCall('rowspan', [$match], $pos);
1049 } elseif (preg_match('/\t+/', $match)) {
1050 $this->addCall('table_align', [$match], $pos);
1051 } elseif (preg_match('/ {2,}/', $match)) {
1052 $this->addCall('table_align', [$match], $pos);
1053 } elseif ($match == "\n|") {
1054 $this->addCall('table_row', [], $pos);
1055 $this->addCall('tablecell', [], $pos);
1056 } elseif ($match == "\n^") {
1057 $this->addCall('table_row', [], $pos);
1058 $this->addCall('tableheader', [], $pos);
1059 } elseif ($match == '|') {
1060 $this->addCall('tablecell', [], $pos);
1061 } elseif ($match == '^') {
1062 $this->addCall('tableheader', [], $pos);
1072 //------------------------------------------------------------------------
1073 function Doku_Handler_Parse_Media($match)
1076 // Strip the opening and closing markup
1077 $link = preg_replace(['/^\{\{/', '/\}\}$/u'], '', $match);
1079 // Split title from URL
1080 $link = sexplode('|', $link, 2);
1083 $ralign = (bool)preg_match('/^ /', $link[0]);
1084 $lalign = (bool)preg_match('/ $/', $link[0]);
1086 // Logic = what's that ;)...
1087 if ($lalign & $ralign) {
1089 } elseif ($ralign) {
1091 } elseif ($lalign) {
1098 if (!isset($link[1])) {
1102 //remove aligning spaces
1103 $link[0] = trim($link[0]);
1105 //split into src and parameters (using the very last questionmark)
1106 $pos = strrpos($link[0], '?');
1107 if ($pos !== false) {
1108 $src = substr($link[0], 0, $pos);
1109 $param = substr($link[0], $pos +
1);
1115 //parse width and height
1116 if (preg_match('#(\d+)(x(\d+))?#i', $param, $size)) {
1117 $w = empty($size[1]) ?
null : $size[1];
1118 $h = empty($size[3]) ?
null : $size[3];
1124 //get linking command
1125 if (preg_match('/nolink/i', $param)) {
1126 $linking = 'nolink';
1127 } elseif (preg_match('/direct/i', $param)) {
1128 $linking = 'direct';
1129 } elseif (preg_match('/linkonly/i', $param)) {
1130 $linking = 'linkonly';
1132 $linking = 'details';
1135 //get caching command
1136 if (preg_match('/(nocache|recache)/i', $param, $cachemode)) {
1137 $cache = $cachemode[1];
1142 // Check whether this is a local or remote image or interwiki
1143 if (media_isexternal($src) ||
link_isinterwiki($src)) {
1144 $call = 'externalmedia';
1146 $call = 'internalmedia';
1152 'title' => $link[1],
1157 'linking' => $linking