xref: /dokuwiki/inc/parser/handler.php (revision 5c2aad12d4f86628f0242d895e0b38d772a807f1)
10cecf9d5Sandi<?php
2*5c2aad12SAndreas Gohr
3*5c2aad12SAndreas Gohruse dokuwiki\Handler\Block;
4*5c2aad12SAndreas Gohruse dokuwiki\Handler\CallWriter;
5*5c2aad12SAndreas Gohruse dokuwiki\Handler\Lists;
6*5c2aad12SAndreas Gohruse dokuwiki\Handler\Nest;
7*5c2aad12SAndreas Gohruse dokuwiki\Handler\Preformatted;
8*5c2aad12SAndreas Gohruse dokuwiki\Handler\Quote;
9*5c2aad12SAndreas Gohruse dokuwiki\Handler\Table;
10*5c2aad12SAndreas Gohr
1152fe2bfbSChris Smithif (!defined('DOKU_PARSER_EOL')) define('DOKU_PARSER_EOL', "\n");   // add this to make handling test cases simpler
12a9c1d2d2SChris Smith
130cecf9d5Sandiclass Doku_Handler {
140cecf9d5Sandi
150ea51e63SMatt Perry    var $Renderer = null;
160cecf9d5Sandi
170ea51e63SMatt Perry    var $CallWriter = null;
180cecf9d5Sandi
190cecf9d5Sandi    var $calls = array();
200cecf9d5Sandi
21e1c10e4dSchris    var $status = array(
2244881bd0Shenning.noren        'section' => false,
23e950d12fSChristopher Smith        'doublequote' => 0,
240cecf9d5Sandi    );
250cecf9d5Sandi
2644881bd0Shenning.noren    var $rewriteBlocks = true;
27b7c441b9SHarry Fuecks
2826e22ab8SChristopher Smith    function __construct() {
29*5c2aad12SAndreas Gohr        $this->CallWriter = new CallWriter($this);
300cecf9d5Sandi    }
310cecf9d5Sandi
32276820f7SScrutinizer Auto-Fixer    /**
33276820f7SScrutinizer Auto-Fixer     * @param string $handler
34f50a239bSTakamura     * @param mixed $args
35f50a239bSTakamura     * @param integer|string $pos
36276820f7SScrutinizer Auto-Fixer     */
37433bef32Sandi    function _addCall($handler, $args, $pos) {
380cecf9d5Sandi        $call = array($handler,$args, $pos);
390cecf9d5Sandi        $this->CallWriter->writeCall($call);
400cecf9d5Sandi    }
410cecf9d5Sandi
4282d61635Spierre.spring    function addPluginCall($plugin, $args, $state, $pos, $match) {
4382d61635Spierre.spring        $call = array('plugin',array($plugin, $args, $state, $match), $pos);
4404ebd214Schris        $this->CallWriter->writeCall($call);
4504ebd214Schris    }
4604ebd214Schris
47433bef32Sandi    function _finalize(){
48e1c10e4dSchris
49f4f02a0fSchris        $this->CallWriter->finalise();
50f4f02a0fSchris
51e1c10e4dSchris        if ( $this->status['section'] ) {
52e1c10e4dSchris            $last_call = end($this->calls);
53e1c10e4dSchris            array_push($this->calls,array('section_close',array(), $last_call[2]));
540cecf9d5Sandi        }
550cecf9d5Sandi
56b7c441b9SHarry Fuecks        if ( $this->rewriteBlocks ) {
57*5c2aad12SAndreas Gohr            $B = new Block();
580cecf9d5Sandi            $this->calls = $B->process($this->calls);
59b7c441b9SHarry Fuecks        }
60e0ad864eSchris
6124bb549bSchris        trigger_event('PARSER_HANDLER_DONE',$this);
620cecf9d5Sandi
630cecf9d5Sandi        array_unshift($this->calls,array('document_start',array(),0));
640cecf9d5Sandi        $last_call = end($this->calls);
650cecf9d5Sandi        array_push($this->calls,array('document_end',array(),$last_call[2]));
660cecf9d5Sandi    }
670cecf9d5Sandi
689e491c01SAndreas Gohr    /**
699e491c01SAndreas Gohr     * fetch the current call and advance the pointer to the next one
709e491c01SAndreas Gohr     *
719e491c01SAndreas Gohr     * @return bool|mixed
729e491c01SAndreas Gohr     */
730cecf9d5Sandi    function fetch() {
749e491c01SAndreas Gohr        $call = current($this->calls);
759e491c01SAndreas Gohr        if($call !== false) {
769e491c01SAndreas Gohr            next($this->calls); //advance the pointer
779e491c01SAndreas Gohr            return $call;
780cecf9d5Sandi        }
7944881bd0Shenning.noren        return false;
800cecf9d5Sandi    }
81ee20e7d1Sandi
82ee20e7d1Sandi
83ee20e7d1Sandi    /**
84ee20e7d1Sandi     * Special plugin handler
85ee20e7d1Sandi     *
86ee20e7d1Sandi     * This handler is called for all modes starting with 'plugin_'.
87ee20e7d1Sandi     * An additional parameter with the plugin name is passed
88ee20e7d1Sandi     *
89ee20e7d1Sandi     * @author Andreas Gohr <andi@splitbrain.org>
90f50a239bSTakamura     *
91f50a239bSTakamura     * @param string|integer $match
92f50a239bSTakamura     * @param string|integer $state
93f50a239bSTakamura     * @param integer $pos
94f50a239bSTakamura     * @param $pluginname
95f50a239bSTakamura     *
96f50a239bSTakamura     * @return bool
97ee20e7d1Sandi     */
98ee20e7d1Sandi    function plugin($match, $state, $pos, $pluginname){
99ee20e7d1Sandi        $data = array($match);
10051a883edSGerrit Uitslag        /** @var DokuWiki_Syntax_Plugin $plugin */
10145d5ad75SGerrit Uitslag        $plugin = plugin_load('syntax',$pluginname);
102a46d0d65SAndreas Gohr        if($plugin != null){
103f02a7d06Schris            $data = $plugin->handle($match, $state, $pos, $this);
104ee20e7d1Sandi        }
10513ecfb18SChris Smith        if ($data !== false) {
10682d61635Spierre.spring            $this->addPluginCall($pluginname,$data,$state,$pos,$match);
10713ecfb18SChris Smith        }
10844881bd0Shenning.noren        return true;
109ee20e7d1Sandi    }
1100cecf9d5Sandi
1110cecf9d5Sandi    function base($match, $state, $pos) {
1120cecf9d5Sandi        switch ( $state ) {
1130cecf9d5Sandi            case DOKU_LEXER_UNMATCHED:
114433bef32Sandi                $this->_addCall('cdata',array($match), $pos);
11544881bd0Shenning.noren                return true;
1160cecf9d5Sandi            break;
1170cecf9d5Sandi        }
1180cecf9d5Sandi    }
1190cecf9d5Sandi
1200cecf9d5Sandi    function header($match, $state, $pos) {
121d7e8115fSAndreas Gohr        // get level and title
122a4a2d4cfSAndreas Gohr        $title = trim($match);
123a4a2d4cfSAndreas Gohr        $level = 7 - strspn($title,'=');
124d7e8115fSAndreas Gohr        if($level < 1) $level = 1;
125a4a2d4cfSAndreas Gohr        $title = trim($title,'=');
126a4a2d4cfSAndreas Gohr        $title = trim($title);
1270cecf9d5Sandi
128e1c10e4dSchris        if ($this->status['section']) $this->_addCall('section_close',array(),$pos);
129e1c10e4dSchris
130433bef32Sandi        $this->_addCall('header',array($title,$level,$pos), $pos);
131e1c10e4dSchris
132e1c10e4dSchris        $this->_addCall('section_open',array($level),$pos);
13344881bd0Shenning.noren        $this->status['section'] = true;
13444881bd0Shenning.noren        return true;
1350cecf9d5Sandi    }
1360cecf9d5Sandi
1370cecf9d5Sandi    function notoc($match, $state, $pos) {
138e41c4da9SAndreas Gohr        $this->_addCall('notoc',array(),$pos);
13944881bd0Shenning.noren        return true;
1400cecf9d5Sandi    }
1410cecf9d5Sandi
1429dc2c2afSandi    function nocache($match, $state, $pos) {
1439dc2c2afSandi        $this->_addCall('nocache',array(),$pos);
14444881bd0Shenning.noren        return true;
1459dc2c2afSandi    }
1469dc2c2afSandi
1470cecf9d5Sandi    function linebreak($match, $state, $pos) {
148433bef32Sandi        $this->_addCall('linebreak',array(),$pos);
14944881bd0Shenning.noren        return true;
1500cecf9d5Sandi    }
1510cecf9d5Sandi
1520cecf9d5Sandi    function eol($match, $state, $pos) {
153433bef32Sandi        $this->_addCall('eol',array(),$pos);
15444881bd0Shenning.noren        return true;
1550cecf9d5Sandi    }
1560cecf9d5Sandi
1570cecf9d5Sandi    function hr($match, $state, $pos) {
158433bef32Sandi        $this->_addCall('hr',array(),$pos);
15944881bd0Shenning.noren        return true;
1600cecf9d5Sandi    }
1610cecf9d5Sandi
162276820f7SScrutinizer Auto-Fixer    /**
163f50a239bSTakamura     * @param string|integer $match
164f50a239bSTakamura     * @param string|integer $state
165f50a239bSTakamura     * @param integer $pos
166276820f7SScrutinizer Auto-Fixer     * @param string $name
167276820f7SScrutinizer Auto-Fixer     */
168433bef32Sandi    function _nestingTag($match, $state, $pos, $name) {
1690cecf9d5Sandi        switch ( $state ) {
1700cecf9d5Sandi            case DOKU_LEXER_ENTER:
171433bef32Sandi                $this->_addCall($name.'_open', array(), $pos);
1720cecf9d5Sandi            break;
1730cecf9d5Sandi            case DOKU_LEXER_EXIT:
174433bef32Sandi                $this->_addCall($name.'_close', array(), $pos);
1750cecf9d5Sandi            break;
1760cecf9d5Sandi            case DOKU_LEXER_UNMATCHED:
177433bef32Sandi                $this->_addCall('cdata',array($match), $pos);
1780cecf9d5Sandi            break;
1790cecf9d5Sandi        }
1800cecf9d5Sandi    }
1810cecf9d5Sandi
1820cecf9d5Sandi    function strong($match, $state, $pos) {
183433bef32Sandi        $this->_nestingTag($match, $state, $pos, 'strong');
18444881bd0Shenning.noren        return true;
1850cecf9d5Sandi    }
1860cecf9d5Sandi
1870cecf9d5Sandi    function emphasis($match, $state, $pos) {
188433bef32Sandi        $this->_nestingTag($match, $state, $pos, 'emphasis');
18944881bd0Shenning.noren        return true;
1900cecf9d5Sandi    }
1910cecf9d5Sandi
1920cecf9d5Sandi    function underline($match, $state, $pos) {
193433bef32Sandi        $this->_nestingTag($match, $state, $pos, 'underline');
19444881bd0Shenning.noren        return true;
1950cecf9d5Sandi    }
1960cecf9d5Sandi
1970cecf9d5Sandi    function monospace($match, $state, $pos) {
198433bef32Sandi        $this->_nestingTag($match, $state, $pos, 'monospace');
19944881bd0Shenning.noren        return true;
2000cecf9d5Sandi    }
2010cecf9d5Sandi
2020cecf9d5Sandi    function subscript($match, $state, $pos) {
203433bef32Sandi        $this->_nestingTag($match, $state, $pos, 'subscript');
20444881bd0Shenning.noren        return true;
2050cecf9d5Sandi    }
2060cecf9d5Sandi
2070cecf9d5Sandi    function superscript($match, $state, $pos) {
208433bef32Sandi        $this->_nestingTag($match, $state, $pos, 'superscript');
20944881bd0Shenning.noren        return true;
2100cecf9d5Sandi    }
2110cecf9d5Sandi
2120cecf9d5Sandi    function deleted($match, $state, $pos) {
213433bef32Sandi        $this->_nestingTag($match, $state, $pos, 'deleted');
21444881bd0Shenning.noren        return true;
2150cecf9d5Sandi    }
2160cecf9d5Sandi
2170cecf9d5Sandi
2180cecf9d5Sandi    function footnote($match, $state, $pos) {
219742c66f8Schris        if (!isset($this->_footnote)) $this->_footnote = false;
2202fe7363dSchris
2215587e44cSchris        switch ( $state ) {
2225587e44cSchris            case DOKU_LEXER_ENTER:
2232fe7363dSchris                // footnotes can not be nested - however due to limitations in lexer it can't be prevented
2242fe7363dSchris                // we will still enter a new footnote mode, we just do nothing
225742c66f8Schris                if ($this->_footnote) {
2262fe7363dSchris                    $this->_addCall('cdata',array($match), $pos);
2272fe7363dSchris                    break;
2282fe7363dSchris                }
229742c66f8Schris                $this->_footnote = true;
2302fe7363dSchris
231*5c2aad12SAndreas Gohr                $this->CallWriter = new Nest($this->CallWriter,'footnote_close');
2324a26ad85Schris                $this->_addCall('footnote_open', array(), $pos);
2335587e44cSchris            break;
2345587e44cSchris            case DOKU_LEXER_EXIT:
2352fe7363dSchris                // check whether we have already exitted the footnote mode, can happen if the modes were nested
236742c66f8Schris                if (!$this->_footnote) {
2372fe7363dSchris                    $this->_addCall('cdata',array($match), $pos);
2382fe7363dSchris                    break;
2392fe7363dSchris                }
2402fe7363dSchris
241742c66f8Schris                $this->_footnote = false;
2425587e44cSchris                $this->_addCall('footnote_close', array(), $pos);
243*5c2aad12SAndreas Gohr
244*5c2aad12SAndreas Gohr                /** @var Nest $reWriter */
245*5c2aad12SAndreas Gohr                $reWriter = $this->CallWriter;
246*5c2aad12SAndreas Gohr                $this->CallWriter = $reWriter->process();
2475587e44cSchris            break;
2485587e44cSchris            case DOKU_LEXER_UNMATCHED:
2495587e44cSchris                $this->_addCall('cdata', array($match), $pos);
2505587e44cSchris            break;
2515587e44cSchris        }
25244881bd0Shenning.noren        return true;
2530cecf9d5Sandi    }
2540cecf9d5Sandi
2550cecf9d5Sandi    function listblock($match, $state, $pos) {
2560cecf9d5Sandi        switch ( $state ) {
2570cecf9d5Sandi            case DOKU_LEXER_ENTER:
258*5c2aad12SAndreas Gohr                $this->CallWriter = new Lists($this->CallWriter);
259433bef32Sandi                $this->_addCall('list_open', array($match), $pos);
2600cecf9d5Sandi            break;
2610cecf9d5Sandi            case DOKU_LEXER_EXIT:
262433bef32Sandi                $this->_addCall('list_close', array(), $pos);
263*5c2aad12SAndreas Gohr                /** @var Lists $reWriter */
264*5c2aad12SAndreas Gohr                $reWriter = $this->CallWriter;
265*5c2aad12SAndreas Gohr                $this->CallWriter = $reWriter->process();
2660cecf9d5Sandi            break;
2670cecf9d5Sandi            case DOKU_LEXER_MATCHED:
268433bef32Sandi                $this->_addCall('list_item', array($match), $pos);
2690cecf9d5Sandi            break;
2700cecf9d5Sandi            case DOKU_LEXER_UNMATCHED:
271433bef32Sandi                $this->_addCall('cdata', array($match), $pos);
2720cecf9d5Sandi            break;
2730cecf9d5Sandi        }
27444881bd0Shenning.noren        return true;
2750cecf9d5Sandi    }
2760cecf9d5Sandi
2770cecf9d5Sandi    function unformatted($match, $state, $pos) {
2780cecf9d5Sandi        if ( $state == DOKU_LEXER_UNMATCHED ) {
279433bef32Sandi            $this->_addCall('unformatted',array($match), $pos);
2800cecf9d5Sandi        }
28144881bd0Shenning.noren        return true;
2820cecf9d5Sandi    }
2830cecf9d5Sandi
2840cecf9d5Sandi    function php($match, $state, $pos) {
285df9add72Schris        global $conf;
2860cecf9d5Sandi        if ( $state == DOKU_LEXER_UNMATCHED ) {
287433bef32Sandi            $this->_addCall('php',array($match), $pos);
2880cecf9d5Sandi        }
28944881bd0Shenning.noren        return true;
2900cecf9d5Sandi    }
2910cecf9d5Sandi
29207f89c3cSAnika Henke    function phpblock($match, $state, $pos) {
29307f89c3cSAnika Henke        global $conf;
29407f89c3cSAnika Henke        if ( $state == DOKU_LEXER_UNMATCHED ) {
29507f89c3cSAnika Henke            $this->_addCall('phpblock',array($match), $pos);
29607f89c3cSAnika Henke        }
29707f89c3cSAnika Henke        return true;
29807f89c3cSAnika Henke    }
29907f89c3cSAnika Henke
3000cecf9d5Sandi    function html($match, $state, $pos) {
301df9add72Schris        global $conf;
3020cecf9d5Sandi        if ( $state == DOKU_LEXER_UNMATCHED ) {
303433bef32Sandi            $this->_addCall('html',array($match), $pos);
3040cecf9d5Sandi        }
30544881bd0Shenning.noren        return true;
3060cecf9d5Sandi    }
3070cecf9d5Sandi
30807f89c3cSAnika Henke    function htmlblock($match, $state, $pos) {
30907f89c3cSAnika Henke        global $conf;
31007f89c3cSAnika Henke        if ( $state == DOKU_LEXER_UNMATCHED ) {
31107f89c3cSAnika Henke            $this->_addCall('htmlblock',array($match), $pos);
31207f89c3cSAnika Henke        }
31307f89c3cSAnika Henke        return true;
31407f89c3cSAnika Henke    }
31507f89c3cSAnika Henke
3160cecf9d5Sandi    function preformatted($match, $state, $pos) {
3170cecf9d5Sandi        switch ( $state ) {
3180cecf9d5Sandi            case DOKU_LEXER_ENTER:
319*5c2aad12SAndreas Gohr                $this->CallWriter = new Preformatted($this->CallWriter);
320433bef32Sandi                $this->_addCall('preformatted_start',array(), $pos);
3210cecf9d5Sandi            break;
3220cecf9d5Sandi            case DOKU_LEXER_EXIT:
323433bef32Sandi                $this->_addCall('preformatted_end',array(), $pos);
324*5c2aad12SAndreas Gohr                /** @var Preformatted $reWriter */
325*5c2aad12SAndreas Gohr                $reWriter = $this->CallWriter;
326*5c2aad12SAndreas Gohr                $this->CallWriter = $reWriter->process();
3270cecf9d5Sandi            break;
3280cecf9d5Sandi            case DOKU_LEXER_MATCHED:
329433bef32Sandi                $this->_addCall('preformatted_newline',array(), $pos);
3300cecf9d5Sandi            break;
3310cecf9d5Sandi            case DOKU_LEXER_UNMATCHED:
332433bef32Sandi                $this->_addCall('preformatted_content',array($match), $pos);
3330cecf9d5Sandi            break;
3340cecf9d5Sandi        }
3350cecf9d5Sandi
33644881bd0Shenning.noren        return true;
3370cecf9d5Sandi    }
3380cecf9d5Sandi
3390cecf9d5Sandi    function quote($match, $state, $pos) {
3400cecf9d5Sandi
3410cecf9d5Sandi        switch ( $state ) {
3420cecf9d5Sandi
3430cecf9d5Sandi            case DOKU_LEXER_ENTER:
344*5c2aad12SAndreas Gohr                $this->CallWriter = new Quote($this->CallWriter);
345433bef32Sandi                $this->_addCall('quote_start',array($match), $pos);
3460cecf9d5Sandi            break;
3470cecf9d5Sandi
3480cecf9d5Sandi            case DOKU_LEXER_EXIT:
349433bef32Sandi                $this->_addCall('quote_end',array(), $pos);
350*5c2aad12SAndreas Gohr                /** @var Lists $reWriter */
351*5c2aad12SAndreas Gohr                $reWriter = $this->CallWriter;
352*5c2aad12SAndreas Gohr                $this->CallWriter = $reWriter->process();
3530cecf9d5Sandi            break;
3540cecf9d5Sandi
3550cecf9d5Sandi            case DOKU_LEXER_MATCHED:
356433bef32Sandi                $this->_addCall('quote_newline',array($match), $pos);
3570cecf9d5Sandi            break;
3580cecf9d5Sandi
3590cecf9d5Sandi            case DOKU_LEXER_UNMATCHED:
360433bef32Sandi                $this->_addCall('cdata',array($match), $pos);
3610cecf9d5Sandi            break;
3620cecf9d5Sandi
3630cecf9d5Sandi        }
3640cecf9d5Sandi
36544881bd0Shenning.noren        return true;
3660cecf9d5Sandi    }
3670cecf9d5Sandi
368e2d88156SLarsDW223    /**
369e2d88156SLarsDW223     * Internal function for parsing highlight options.
370e2d88156SLarsDW223     * $options is parsed for key value pairs separated by commas.
371e2d88156SLarsDW223     * A value might also be missing in which case the value will simple
372e2d88156SLarsDW223     * be set to true. Commas in strings are ignored, e.g. option="4,56"
373e2d88156SLarsDW223     * will work as expected and will only create one entry.
374e2d88156SLarsDW223     *
37554f741e8SAndreas Gohr     * @param string $options space separated list of key-value pairs,
376e2d88156SLarsDW223     *                        e.g. option1=123, option2="456"
377e2d88156SLarsDW223     * @return array|null     Array of key-value pairs $array['key'] = 'value';
378e2d88156SLarsDW223     *                        or null if no entries found
379e2d88156SLarsDW223     */
380e2d88156SLarsDW223    protected function parse_highlight_options ($options) {
381e2d88156SLarsDW223        $result = array();
38254f741e8SAndreas Gohr        preg_match_all('/(\w+(?:="[^"]*"))|(\w+(?:=[^\s]*))|(\w+[^=\s\]])(?:\s*)/', $options, $matches, PREG_SET_ORDER);
383e2d88156SLarsDW223        foreach ($matches as $match) {
384e2d88156SLarsDW223            $equal_sign = strpos($match [0], '=');
385e2d88156SLarsDW223            if ($equal_sign === false) {
38654f741e8SAndreas Gohr                $key = trim($match[0]);
387e2d88156SLarsDW223                $result [$key] = 1;
388e2d88156SLarsDW223            } else {
389e2d88156SLarsDW223                $key = substr($match[0], 0, $equal_sign);
390e2d88156SLarsDW223                $value = substr($match[0], $equal_sign+1);
391e2d88156SLarsDW223                $value = trim($value, '"');
392e2d88156SLarsDW223                if (strlen($value) > 0) {
393e2d88156SLarsDW223                    $result [$key] = $value;
394e2d88156SLarsDW223                } else {
395e2d88156SLarsDW223                    $result [$key] = 1;
396e2d88156SLarsDW223                }
397e2d88156SLarsDW223            }
398e2d88156SLarsDW223        }
399e2d88156SLarsDW223
400e2d88156SLarsDW223        // Check for supported options
401e2d88156SLarsDW223        $result = array_intersect_key(
402e2d88156SLarsDW223            $result,
403e2d88156SLarsDW223            array_flip(array(
404e2d88156SLarsDW223                'enable_line_numbers',
405e2d88156SLarsDW223                'start_line_numbers_at',
406e2d88156SLarsDW223                'highlight_lines_extra',
407e2d88156SLarsDW223                'enable_keyword_links')
408e2d88156SLarsDW223            )
409e2d88156SLarsDW223        );
410e2d88156SLarsDW223
411e2d88156SLarsDW223        // Sanitize values
412e2d88156SLarsDW223        if(isset($result['enable_line_numbers'])) {
41354f741e8SAndreas Gohr            if($result['enable_line_numbers'] === 'false') {
41454f741e8SAndreas Gohr                $result['enable_line_numbers'] = false;
41554f741e8SAndreas Gohr            }
416e2d88156SLarsDW223            $result['enable_line_numbers'] = (bool) $result['enable_line_numbers'];
417e2d88156SLarsDW223        }
418e2d88156SLarsDW223        if(isset($result['highlight_lines_extra'])) {
419e2d88156SLarsDW223            $result['highlight_lines_extra'] = array_map('intval', explode(',', $result['highlight_lines_extra']));
420e2d88156SLarsDW223            $result['highlight_lines_extra'] = array_filter($result['highlight_lines_extra']);
421e2d88156SLarsDW223            $result['highlight_lines_extra'] = array_unique($result['highlight_lines_extra']);
422e2d88156SLarsDW223        }
423e2d88156SLarsDW223        if(isset($result['start_line_numbers_at'])) {
424e2d88156SLarsDW223            $result['start_line_numbers_at'] = (int) $result['start_line_numbers_at'];
425e2d88156SLarsDW223        }
426e2d88156SLarsDW223        if(isset($result['enable_keyword_links'])) {
42754f741e8SAndreas Gohr            if($result['enable_keyword_links'] === 'false') {
42854f741e8SAndreas Gohr                $result['enable_keyword_links'] = false;
42954f741e8SAndreas Gohr            }
43054f741e8SAndreas Gohr            $result['enable_keyword_links'] = (bool) $result['enable_keyword_links'];
431e2d88156SLarsDW223        }
432e2d88156SLarsDW223        if (count($result) == 0) {
433e2d88156SLarsDW223            return null;
434e2d88156SLarsDW223        }
435e2d88156SLarsDW223
436e2d88156SLarsDW223        return $result;
437e2d88156SLarsDW223    }
438e2d88156SLarsDW223
4393d491f75SAndreas Gohr    function file($match, $state, $pos) {
4403d491f75SAndreas Gohr        return $this->code($match, $state, $pos, 'file');
4413d491f75SAndreas Gohr    }
4423d491f75SAndreas Gohr
4433d491f75SAndreas Gohr    function code($match, $state, $pos, $type='code') {
4443d491f75SAndreas Gohr        if ( $state == DOKU_LEXER_UNMATCHED ) {
4454b7f9e70STom N Harris            $matches = explode('>',$match,2);
446e2d88156SLarsDW223            // Cut out variable options enclosed in []
447e2d88156SLarsDW223            preg_match('/\[.*\]/', $matches[0], $options);
448e2d88156SLarsDW223            if (!empty($options[0])) {
449e2d88156SLarsDW223                $matches[0] = str_replace($options[0], '', $matches[0]);
450e2d88156SLarsDW223            }
4510139312bSAdrian Lang            $param = preg_split('/\s+/', $matches[0], 2, PREG_SPLIT_NO_EMPTY);
4520139312bSAdrian Lang            while(count($param) < 2) array_push($param, null);
4530139312bSAdrian Lang            // We shortcut html here.
4540139312bSAdrian Lang            if ($param[0] == 'html') $param[0] = 'html4strict';
4550139312bSAdrian Lang            if ($param[0] == '-') $param[0] = null;
4560139312bSAdrian Lang            array_unshift($param, $matches[1]);
457e2d88156SLarsDW223            if (!empty($options[0])) {
458e2d88156SLarsDW223                $param [] = $this->parse_highlight_options ($options[0]);
459e2d88156SLarsDW223            }
4600139312bSAdrian Lang            $this->_addCall($type, $param, $pos);
4610cecf9d5Sandi        }
46244881bd0Shenning.noren        return true;
4630cecf9d5Sandi    }
4640cecf9d5Sandi
4650cecf9d5Sandi    function acronym($match, $state, $pos) {
466433bef32Sandi        $this->_addCall('acronym',array($match), $pos);
46744881bd0Shenning.noren        return true;
4680cecf9d5Sandi    }
4690cecf9d5Sandi
4700cecf9d5Sandi    function smiley($match, $state, $pos) {
471433bef32Sandi        $this->_addCall('smiley',array($match), $pos);
47244881bd0Shenning.noren        return true;
4730cecf9d5Sandi    }
4740cecf9d5Sandi
4750cecf9d5Sandi    function wordblock($match, $state, $pos) {
476433bef32Sandi        $this->_addCall('wordblock',array($match), $pos);
47744881bd0Shenning.noren        return true;
4780cecf9d5Sandi    }
4790cecf9d5Sandi
4800cecf9d5Sandi    function entity($match, $state, $pos) {
481433bef32Sandi        $this->_addCall('entity',array($match), $pos);
48244881bd0Shenning.noren        return true;
4830cecf9d5Sandi    }
4840cecf9d5Sandi
4850cecf9d5Sandi    function multiplyentity($match, $state, $pos) {
4860cecf9d5Sandi        preg_match_all('/\d+/',$match,$matches);
487433bef32Sandi        $this->_addCall('multiplyentity',array($matches[0][0],$matches[0][1]), $pos);
48844881bd0Shenning.noren        return true;
4890cecf9d5Sandi    }
4900cecf9d5Sandi
4910cecf9d5Sandi    function singlequoteopening($match, $state, $pos) {
492433bef32Sandi        $this->_addCall('singlequoteopening',array(), $pos);
49344881bd0Shenning.noren        return true;
4940cecf9d5Sandi    }
4950cecf9d5Sandi
4960cecf9d5Sandi    function singlequoteclosing($match, $state, $pos) {
497433bef32Sandi        $this->_addCall('singlequoteclosing',array(), $pos);
49844881bd0Shenning.noren        return true;
4990cecf9d5Sandi    }
5000cecf9d5Sandi
50157d757d1SAndreas Gohr    function apostrophe($match, $state, $pos) {
50257d757d1SAndreas Gohr        $this->_addCall('apostrophe',array(), $pos);
50357d757d1SAndreas Gohr        return true;
50457d757d1SAndreas Gohr    }
50557d757d1SAndreas Gohr
5060cecf9d5Sandi    function doublequoteopening($match, $state, $pos) {
507433bef32Sandi        $this->_addCall('doublequoteopening',array(), $pos);
508e950d12fSChristopher Smith        $this->status['doublequote']++;
50944881bd0Shenning.noren        return true;
5100cecf9d5Sandi    }
5110cecf9d5Sandi
5120cecf9d5Sandi    function doublequoteclosing($match, $state, $pos) {
513e950d12fSChristopher Smith        if ($this->status['doublequote'] <= 0) {
514e950d12fSChristopher Smith            $this->doublequoteopening($match, $state, $pos);
515e950d12fSChristopher Smith        } else {
516433bef32Sandi            $this->_addCall('doublequoteclosing',array(), $pos);
517e950d12fSChristopher Smith            $this->status['doublequote'] = max(0, --$this->status['doublequote']);
518e950d12fSChristopher Smith        }
51944881bd0Shenning.noren        return true;
5200cecf9d5Sandi    }
5210cecf9d5Sandi
5220cecf9d5Sandi    function camelcaselink($match, $state, $pos) {
523433bef32Sandi        $this->_addCall('camelcaselink',array($match), $pos);
52444881bd0Shenning.noren        return true;
5250cecf9d5Sandi    }
5260cecf9d5Sandi
5270cecf9d5Sandi    /*
5280cecf9d5Sandi    */
5290cecf9d5Sandi    function internallink($match, $state, $pos) {
5300cecf9d5Sandi        // Strip the opening and closing markup
5310cecf9d5Sandi        $link = preg_replace(array('/^\[\[/','/\]\]$/u'),'',$match);
5320cecf9d5Sandi
5330cecf9d5Sandi        // Split title from URL
5344b7f9e70STom N Harris        $link = explode('|',$link,2);
5350cecf9d5Sandi        if ( !isset($link[1]) ) {
5360ea51e63SMatt Perry            $link[1] = null;
5370cecf9d5Sandi        } else if ( preg_match('/^\{\{[^\}]+\}\}$/',$link[1]) ) {
5385578eb8fSandi            // If the title is an image, convert it to an array containing the image details
539b625487dSandi            $link[1] = Doku_Handler_Parse_Media($link[1]);
5400cecf9d5Sandi        }
5410b7c14c2Sandi        $link[0] = trim($link[0]);
5420cecf9d5Sandi
5430e1c636eSandi        //decide which kind of link it is
5440e1c636eSandi
5456efc45a2SDmitry Katsubo        if ( link_isinterwiki($link[0]) ) {
5460e1c636eSandi            // Interwiki
5474b7f9e70STom N Harris            $interwiki = explode('>',$link[0],2);
548433bef32Sandi            $this->_addCall(
5490cecf9d5Sandi                'interwikilink',
5500cecf9d5Sandi                array($link[0],$link[1],strtolower($interwiki[0]),$interwiki[1]),
5510cecf9d5Sandi                $pos
5520cecf9d5Sandi                );
55315f1b77cSAndreas Gohr        }elseif ( preg_match('/^\\\\\\\\[^\\\\]+?\\\\/u',$link[0]) ) {
5540e1c636eSandi            // Windows Share
555433bef32Sandi            $this->_addCall(
5560cecf9d5Sandi                'windowssharelink',
5570cecf9d5Sandi                array($link[0],$link[1]),
5580cecf9d5Sandi                $pos
5590cecf9d5Sandi                );
5604468cb4cSAndreas Gohr        }elseif ( preg_match('#^([a-z0-9\-\.+]+?)://#i',$link[0]) ) {
5610e1c636eSandi            // external link (accepts all protocols)
562433bef32Sandi            $this->_addCall(
5630cecf9d5Sandi                    'externallink',
5640cecf9d5Sandi                    array($link[0],$link[1]),
5650cecf9d5Sandi                    $pos
5660cecf9d5Sandi                    );
5670a1d30bfSchris        }elseif ( preg_match('<'.PREG_PATTERN_VALID_EMAIL.'>',$link[0]) ) {
5680a1d30bfSchris            // E-Mail (pattern above is defined in inc/mail.php)
569a6755281Sandi            $this->_addCall(
570a6755281Sandi                'emaillink',
571a6755281Sandi                array($link[0],$link[1]),
572a6755281Sandi                $pos
573a6755281Sandi                );
5740b7c14c2Sandi        }elseif ( preg_match('!^#.+!',$link[0]) ){
5750b7c14c2Sandi            // local link
5760b7c14c2Sandi            $this->_addCall(
5770b7c14c2Sandi                'locallink',
5780b7c14c2Sandi                array(substr($link[0],1),$link[1]),
5790b7c14c2Sandi                $pos
5800b7c14c2Sandi                );
5810e1c636eSandi        }else{
5820e1c636eSandi            // internal link
583433bef32Sandi            $this->_addCall(
5840e1c636eSandi                'internallink',
5850e1c636eSandi                array($link[0],$link[1]),
5860e1c636eSandi                $pos
5870e1c636eSandi                );
5880cecf9d5Sandi        }
5890e1c636eSandi
59044881bd0Shenning.noren        return true;
5910cecf9d5Sandi    }
5920cecf9d5Sandi
5930cecf9d5Sandi    function filelink($match, $state, $pos) {
5940ea51e63SMatt Perry        $this->_addCall('filelink',array($match, null), $pos);
59544881bd0Shenning.noren        return true;
5960cecf9d5Sandi    }
5970cecf9d5Sandi
5980cecf9d5Sandi    function windowssharelink($match, $state, $pos) {
5990ea51e63SMatt Perry        $this->_addCall('windowssharelink',array($match, null), $pos);
60044881bd0Shenning.noren        return true;
6010cecf9d5Sandi    }
6020cecf9d5Sandi
6030cecf9d5Sandi    function media($match, $state, $pos) {
6040cecf9d5Sandi        $p = Doku_Handler_Parse_Media($match);
6050cecf9d5Sandi
606433bef32Sandi        $this->_addCall(
6070cecf9d5Sandi              $p['type'],
608dc673a5bSjoe.lapp              array($p['src'], $p['title'], $p['align'], $p['width'],
609dc673a5bSjoe.lapp                     $p['height'], $p['cache'], $p['linking']),
6100cecf9d5Sandi              $pos
6110cecf9d5Sandi             );
61244881bd0Shenning.noren        return true;
6130cecf9d5Sandi    }
6140cecf9d5Sandi
615b625487dSandi    function rss($match, $state, $pos) {
616b625487dSandi        $link = preg_replace(array('/^\{\{rss>/','/\}\}$/'),'',$match);
6173db95becSAndreas Gohr
6183db95becSAndreas Gohr        // get params
6193db95becSAndreas Gohr        list($link,$params) = explode(' ',$link,2);
6203db95becSAndreas Gohr
6213db95becSAndreas Gohr        $p = array();
6223db95becSAndreas Gohr        if(preg_match('/\b(\d+)\b/',$params,$match)){
6233db95becSAndreas Gohr            $p['max'] = $match[1];
6243db95becSAndreas Gohr        }else{
6253db95becSAndreas Gohr            $p['max'] = 8;
6263db95becSAndreas Gohr        }
6273db95becSAndreas Gohr        $p['reverse'] = (preg_match('/rev/',$params));
6283db95becSAndreas Gohr        $p['author']  = (preg_match('/\b(by|author)/',$params));
6293db95becSAndreas Gohr        $p['date']    = (preg_match('/\b(date)/',$params));
6303db95becSAndreas Gohr        $p['details'] = (preg_match('/\b(desc|detail)/',$params));
63138c6f603SRobin H. Johnson        $p['nosort']  = (preg_match('/\b(nosort)\b/',$params));
6323db95becSAndreas Gohr
6330a69dff7Schris        if (preg_match('/\b(\d+)([dhm])\b/',$params,$match)) {
6340a69dff7Schris            $period = array('d' => 86400, 'h' => 3600, 'm' => 60);
6350a69dff7Schris            $p['refresh'] = max(600,$match[1]*$period[$match[2]]);  // n * period in seconds, minimum 10 minutes
6360a69dff7Schris        } else {
6370a69dff7Schris            $p['refresh'] = 14400;   // default to 4 hours
6380a69dff7Schris        }
6390a69dff7Schris
6403db95becSAndreas Gohr        $this->_addCall('rss',array($link,$p),$pos);
64144881bd0Shenning.noren        return true;
642b625487dSandi    }
643b625487dSandi
6440cecf9d5Sandi    function externallink($match, $state, $pos) {
645da9f31c5SAndreas Gohr        $url   = $match;
646da9f31c5SAndreas Gohr        $title = null;
6470cecf9d5Sandi
648da9f31c5SAndreas Gohr        // add protocol on simple short URLs
649da9f31c5SAndreas Gohr        if(substr($url,0,3) == 'ftp' && (substr($url,0,6) != 'ftp://')){
650da9f31c5SAndreas Gohr            $title = $url;
651da9f31c5SAndreas Gohr            $url   = 'ftp://'.$url;
652da9f31c5SAndreas Gohr        }
653da9f31c5SAndreas Gohr        if(substr($url,0,3) == 'www' && (substr($url,0,7) != 'http://')){
654da9f31c5SAndreas Gohr            $title = $url;
655da9f31c5SAndreas Gohr            $url = 'http://'.$url;
656da9f31c5SAndreas Gohr        }
657da9f31c5SAndreas Gohr
658da9f31c5SAndreas Gohr        $this->_addCall('externallink',array($url, $title), $pos);
65944881bd0Shenning.noren        return true;
6600cecf9d5Sandi    }
6610cecf9d5Sandi
66271352defSandi    function emaillink($match, $state, $pos) {
6630cecf9d5Sandi        $email = preg_replace(array('/^</','/>$/'),'',$match);
6640ea51e63SMatt Perry        $this->_addCall('emaillink',array($email, null), $pos);
66544881bd0Shenning.noren        return true;
6660cecf9d5Sandi    }
6670cecf9d5Sandi
6680cecf9d5Sandi    function table($match, $state, $pos) {
6690cecf9d5Sandi        switch ( $state ) {
6700cecf9d5Sandi
6710cecf9d5Sandi            case DOKU_LEXER_ENTER:
6720cecf9d5Sandi
673*5c2aad12SAndreas Gohr                $this->CallWriter = new Table($this->CallWriter);
6740cecf9d5Sandi
67590df9a4dSAdrian Lang                $this->_addCall('table_start', array($pos + 1), $pos);
6760cecf9d5Sandi                if ( trim($match) == '^' ) {
677433bef32Sandi                    $this->_addCall('tableheader', array(), $pos);
6780cecf9d5Sandi                } else {
679433bef32Sandi                    $this->_addCall('tablecell', array(), $pos);
6800cecf9d5Sandi                }
6810cecf9d5Sandi            break;
6820cecf9d5Sandi
6830cecf9d5Sandi            case DOKU_LEXER_EXIT:
68490df9a4dSAdrian Lang                $this->_addCall('table_end', array($pos), $pos);
685*5c2aad12SAndreas Gohr                /** @var Table $reWriter */
686*5c2aad12SAndreas Gohr                $reWriter = $this->CallWriter;
687*5c2aad12SAndreas Gohr                $this->CallWriter = $reWriter->process();
6880cecf9d5Sandi            break;
6890cecf9d5Sandi
6900cecf9d5Sandi            case DOKU_LEXER_UNMATCHED:
6910cecf9d5Sandi                if ( trim($match) != '' ) {
692433bef32Sandi                    $this->_addCall('cdata',array($match), $pos);
6930cecf9d5Sandi                }
6940cecf9d5Sandi            break;
6950cecf9d5Sandi
6960cecf9d5Sandi            case DOKU_LEXER_MATCHED:
6979ab75d9eSAndreas Gohr                if ( $match == ' ' ){
6989ab75d9eSAndreas Gohr                    $this->_addCall('cdata', array($match), $pos);
69925b97867Shakan.sandell                } else if ( preg_match('/:::/',$match) ) {
70025b97867Shakan.sandell                    $this->_addCall('rowspan', array($match), $pos);
701e205b721SAndreas Gohr                } else if ( preg_match('/\t+/',$match) ) {
7029ab75d9eSAndreas Gohr                    $this->_addCall('table_align', array($match), $pos);
703e205b721SAndreas Gohr                } else if ( preg_match('/ {2,}/',$match) ) {
704433bef32Sandi                    $this->_addCall('table_align', array($match), $pos);
7050cecf9d5Sandi                } else if ( $match == "\n|" ) {
706433bef32Sandi                    $this->_addCall('table_row', array(), $pos);
707433bef32Sandi                    $this->_addCall('tablecell', array(), $pos);
7080cecf9d5Sandi                } else if ( $match == "\n^" ) {
709433bef32Sandi                    $this->_addCall('table_row', array(), $pos);
710433bef32Sandi                    $this->_addCall('tableheader', array(), $pos);
7110cecf9d5Sandi                } else if ( $match == '|' ) {
712433bef32Sandi                    $this->_addCall('tablecell', array(), $pos);
7130cecf9d5Sandi                } else if ( $match == '^' ) {
714433bef32Sandi                    $this->_addCall('tableheader', array(), $pos);
7150cecf9d5Sandi                }
7160cecf9d5Sandi            break;
7170cecf9d5Sandi        }
71844881bd0Shenning.noren        return true;
7190cecf9d5Sandi    }
7200cecf9d5Sandi}
7210cecf9d5Sandi
7220cecf9d5Sandi//------------------------------------------------------------------------
7230cecf9d5Sandifunction Doku_Handler_Parse_Media($match) {
7240cecf9d5Sandi
7250cecf9d5Sandi    // Strip the opening and closing markup
7260cecf9d5Sandi    $link = preg_replace(array('/^\{\{/','/\}\}$/u'),'',$match);
7270cecf9d5Sandi
7280cecf9d5Sandi    // Split title from URL
7294b7f9e70STom N Harris    $link = explode('|',$link,2);
7300cecf9d5Sandi
7310cecf9d5Sandi    // Check alignment
7320cecf9d5Sandi    $ralign = (bool)preg_match('/^ /',$link[0]);
7330cecf9d5Sandi    $lalign = (bool)preg_match('/ $/',$link[0]);
7340cecf9d5Sandi
7350cecf9d5Sandi    // Logic = what's that ;)...
7360cecf9d5Sandi    if ( $lalign & $ralign ) {
7370cecf9d5Sandi        $align = 'center';
7380cecf9d5Sandi    } else if ( $ralign ) {
7390cecf9d5Sandi        $align = 'right';
7400cecf9d5Sandi    } else if ( $lalign ) {
7410cecf9d5Sandi        $align = 'left';
7420cecf9d5Sandi    } else {
7430ea51e63SMatt Perry        $align = null;
7440cecf9d5Sandi    }
7450cecf9d5Sandi
7460cecf9d5Sandi    // The title...
7470cecf9d5Sandi    if ( !isset($link[1]) ) {
7480ea51e63SMatt Perry        $link[1] = null;
7490cecf9d5Sandi    }
7500cecf9d5Sandi
7514826ab45Sandi    //remove aligning spaces
7524826ab45Sandi    $link[0] = trim($link[0]);
7530cecf9d5Sandi
7544826ab45Sandi    //split into src and parameters (using the very last questionmark)
7554826ab45Sandi    $pos = strrpos($link[0], '?');
7564826ab45Sandi    if($pos !== false){
7574826ab45Sandi        $src   = substr($link[0],0,$pos);
7584826ab45Sandi        $param = substr($link[0],$pos+1);
7590cecf9d5Sandi    }else{
7604826ab45Sandi        $src   = $link[0];
7614826ab45Sandi        $param = '';
7620cecf9d5Sandi    }
7630cecf9d5Sandi
7644826ab45Sandi    //parse width and height
7654826ab45Sandi    if(preg_match('#(\d+)(x(\d+))?#i',$param,$size)){
766443e135dSChristopher Smith        !empty($size[1]) ? $w = $size[1] : $w = null;
767443e135dSChristopher Smith        !empty($size[3]) ? $h = $size[3] : $h = null;
768fc1c55b1Shfuecks    } else {
7690ea51e63SMatt Perry        $w = null;
7700ea51e63SMatt Perry        $h = null;
7710cecf9d5Sandi    }
7720cecf9d5Sandi
773dc673a5bSjoe.lapp    //get linking command
774d35ab615Shenning.noren    if(preg_match('/nolink/i',$param)){
775dc673a5bSjoe.lapp        $linking = 'nolink';
776d35ab615Shenning.noren    }else if(preg_match('/direct/i',$param)){
777dc673a5bSjoe.lapp        $linking = 'direct';
7788acb3108SAndreas Gohr    }else if(preg_match('/linkonly/i',$param)){
7798acb3108SAndreas Gohr        $linking = 'linkonly';
780dc673a5bSjoe.lapp    }else{
781dc673a5bSjoe.lapp        $linking = 'details';
782dc673a5bSjoe.lapp    }
783dc673a5bSjoe.lapp
7844826ab45Sandi    //get caching command
7854826ab45Sandi    if (preg_match('/(nocache|recache)/i',$param,$cachemode)){
7864826ab45Sandi        $cache = $cachemode[1];
7870cecf9d5Sandi    }else{
7884826ab45Sandi        $cache = 'cache';
7890cecf9d5Sandi    }
7900cecf9d5Sandi
7916efc45a2SDmitry Katsubo    // Check whether this is a local or remote image or interwiki
7926efc45a2SDmitry Katsubo    if (media_isexternal($src) || link_isinterwiki($src)){
7934826ab45Sandi        $call = 'externalmedia';
7940cecf9d5Sandi    } else {
7954826ab45Sandi        $call = 'internalmedia';
7960cecf9d5Sandi    }
7970cecf9d5Sandi
7980cecf9d5Sandi    $params = array(
7990cecf9d5Sandi        'type'=>$call,
8004826ab45Sandi        'src'=>$src,
8010cecf9d5Sandi        'title'=>$link[1],
8020cecf9d5Sandi        'align'=>$align,
8034826ab45Sandi        'width'=>$w,
8044826ab45Sandi        'height'=>$h,
8050cecf9d5Sandi        'cache'=>$cache,
806dc673a5bSjoe.lapp        'linking'=>$linking,
8070cecf9d5Sandi    );
8080cecf9d5Sandi
8090cecf9d5Sandi    return $params;
8100cecf9d5Sandi}
8110cecf9d5Sandi
8129569a107SDanny Lin
8132a27e99aSandi
814e3776c06SMichael Hamann//Setup VIM: ex: et ts=4 :
815