xref: /dokuwiki/inc/parser/handler.php (revision 8b1b81be965547272e7d20de20c2783fa59178cd)
10cecf9d5Sandi<?php
25c2aad12SAndreas Gohr
35c2aad12SAndreas Gohruse dokuwiki\Handler\Block;
45c2aad12SAndreas Gohruse dokuwiki\Handler\CallWriter;
5*8b1b81beSAndreas Gohruse dokuwiki\Handler\CallWriterInterface;
65c2aad12SAndreas Gohruse dokuwiki\Handler\Lists;
75c2aad12SAndreas Gohruse dokuwiki\Handler\Nest;
85c2aad12SAndreas Gohruse dokuwiki\Handler\Preformatted;
95c2aad12SAndreas Gohruse dokuwiki\Handler\Quote;
105c2aad12SAndreas Gohruse dokuwiki\Handler\Table;
115c2aad12SAndreas Gohr
12*8b1b81beSAndreas Gohr/**
13*8b1b81beSAndreas Gohr * Class Doku_Handler
14*8b1b81beSAndreas Gohr */
150cecf9d5Sandiclass Doku_Handler {
16*8b1b81beSAndreas Gohr    /** @var CallWriterInterface */
17*8b1b81beSAndreas Gohr    protected $callWriter = null;
180cecf9d5Sandi
19*8b1b81beSAndreas Gohr    /** @var array The current CallWriter will write directly to this list of calls, Parser reads it */
20*8b1b81beSAndreas Gohr    public $calls = array();
210cecf9d5Sandi
22*8b1b81beSAndreas Gohr    /** @var array internal status holders for some modes */
23*8b1b81beSAndreas Gohr    protected $status = array(
2444881bd0Shenning.noren        'section' => false,
25e950d12fSChristopher Smith        'doublequote' => 0,
260cecf9d5Sandi    );
270cecf9d5Sandi
28*8b1b81beSAndreas Gohr    /** @var bool should blocks be rewritten? FIXME seems to always be true */
29*8b1b81beSAndreas Gohr    protected $rewriteBlocks = true;
30b7c441b9SHarry Fuecks
31*8b1b81beSAndreas Gohr    /**
32*8b1b81beSAndreas Gohr     * Doku_Handler constructor.
33*8b1b81beSAndreas Gohr     */
3426e22ab8SChristopher Smith    function __construct() {
35*8b1b81beSAndreas Gohr        $this->callWriter = new CallWriter($this);
360cecf9d5Sandi    }
370cecf9d5Sandi
38276820f7SScrutinizer Auto-Fixer    /**
39*8b1b81beSAndreas Gohr     * Add a new call by passing it to the current CallWriter
40*8b1b81beSAndreas Gohr     *
41*8b1b81beSAndreas Gohr     * @param string $handler handler method name (see mode handlers below)
42*8b1b81beSAndreas Gohr     * @param mixed $args arguments for this call
43*8b1b81beSAndreas Gohr     * @param int $pos  byte position in the original source file
44276820f7SScrutinizer Auto-Fixer     */
45*8b1b81beSAndreas Gohr    protected function addCall($handler, $args, $pos) {
460cecf9d5Sandi        $call = array($handler,$args, $pos);
47*8b1b81beSAndreas Gohr        $this->callWriter->writeCall($call);
480cecf9d5Sandi    }
490cecf9d5Sandi
50*8b1b81beSAndreas Gohr    /**
51*8b1b81beSAndreas Gohr     * Similar to addCall, but adds a plugin call
52*8b1b81beSAndreas Gohr     *
53*8b1b81beSAndreas Gohr     * @param string $plugin name of the plugin
54*8b1b81beSAndreas Gohr     * @param mixed $args arguments for this call
55*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
56*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
57*8b1b81beSAndreas Gohr     * @param string $match matched syntax
58*8b1b81beSAndreas Gohr     */
59*8b1b81beSAndreas Gohr    protected function addPluginCall($plugin, $args, $state, $pos, $match) {
6082d61635Spierre.spring        $call = array('plugin',array($plugin, $args, $state, $match), $pos);
61*8b1b81beSAndreas Gohr        $this->callWriter->writeCall($call);
6204ebd214Schris    }
6304ebd214Schris
64*8b1b81beSAndreas Gohr    /**
65*8b1b81beSAndreas Gohr     * Finishes handling
66*8b1b81beSAndreas Gohr     *
67*8b1b81beSAndreas Gohr     * Called from the parser. Calls finalise() on the call writer, closes open
68*8b1b81beSAndreas Gohr     * sections, rewrites blocks and adds document_start and document_end calls.
69*8b1b81beSAndreas Gohr     *
70*8b1b81beSAndreas Gohr     * @triggers PARSER_HANDLER_DONE
71*8b1b81beSAndreas Gohr     */
72*8b1b81beSAndreas Gohr    public function finalize(){
73*8b1b81beSAndreas Gohr        $this->callWriter->finalise();
74f4f02a0fSchris
75e1c10e4dSchris        if ( $this->status['section'] ) {
76e1c10e4dSchris            $last_call = end($this->calls);
77e1c10e4dSchris            array_push($this->calls,array('section_close',array(), $last_call[2]));
780cecf9d5Sandi        }
790cecf9d5Sandi
80b7c441b9SHarry Fuecks        if ( $this->rewriteBlocks ) {
815c2aad12SAndreas Gohr            $B = new Block();
820cecf9d5Sandi            $this->calls = $B->process($this->calls);
83b7c441b9SHarry Fuecks        }
84e0ad864eSchris
8524bb549bSchris        trigger_event('PARSER_HANDLER_DONE',$this);
860cecf9d5Sandi
870cecf9d5Sandi        array_unshift($this->calls,array('document_start',array(),0));
880cecf9d5Sandi        $last_call = end($this->calls);
890cecf9d5Sandi        array_push($this->calls,array('document_end',array(),$last_call[2]));
900cecf9d5Sandi    }
910cecf9d5Sandi
929e491c01SAndreas Gohr    /**
939e491c01SAndreas Gohr     * fetch the current call and advance the pointer to the next one
949e491c01SAndreas Gohr     *
95*8b1b81beSAndreas Gohr     * @fixme seems to be unused?
969e491c01SAndreas Gohr     * @return bool|mixed
979e491c01SAndreas Gohr     */
98*8b1b81beSAndreas Gohr    public function fetch() {
999e491c01SAndreas Gohr        $call = current($this->calls);
1009e491c01SAndreas Gohr        if($call !== false) {
1019e491c01SAndreas Gohr            next($this->calls); //advance the pointer
1029e491c01SAndreas Gohr            return $call;
1030cecf9d5Sandi        }
10444881bd0Shenning.noren        return false;
1050cecf9d5Sandi    }
106ee20e7d1Sandi
107ee20e7d1Sandi
108ee20e7d1Sandi    /**
109e2d88156SLarsDW223     * Internal function for parsing highlight options.
110e2d88156SLarsDW223     * $options is parsed for key value pairs separated by commas.
111e2d88156SLarsDW223     * A value might also be missing in which case the value will simple
112e2d88156SLarsDW223     * be set to true. Commas in strings are ignored, e.g. option="4,56"
113e2d88156SLarsDW223     * will work as expected and will only create one entry.
114e2d88156SLarsDW223     *
11554f741e8SAndreas Gohr     * @param string $options space separated list of key-value pairs,
116e2d88156SLarsDW223     *                        e.g. option1=123, option2="456"
117e2d88156SLarsDW223     * @return array|null     Array of key-value pairs $array['key'] = 'value';
118e2d88156SLarsDW223     *                        or null if no entries found
119e2d88156SLarsDW223     */
120e2d88156SLarsDW223    protected function parse_highlight_options ($options) {
121e2d88156SLarsDW223        $result = array();
12254f741e8SAndreas Gohr        preg_match_all('/(\w+(?:="[^"]*"))|(\w+(?:=[^\s]*))|(\w+[^=\s\]])(?:\s*)/', $options, $matches, PREG_SET_ORDER);
123e2d88156SLarsDW223        foreach ($matches as $match) {
124e2d88156SLarsDW223            $equal_sign = strpos($match [0], '=');
125e2d88156SLarsDW223            if ($equal_sign === false) {
12654f741e8SAndreas Gohr                $key = trim($match[0]);
127e2d88156SLarsDW223                $result [$key] = 1;
128e2d88156SLarsDW223            } else {
129e2d88156SLarsDW223                $key = substr($match[0], 0, $equal_sign);
130e2d88156SLarsDW223                $value = substr($match[0], $equal_sign+1);
131e2d88156SLarsDW223                $value = trim($value, '"');
132e2d88156SLarsDW223                if (strlen($value) > 0) {
133e2d88156SLarsDW223                    $result [$key] = $value;
134e2d88156SLarsDW223                } else {
135e2d88156SLarsDW223                    $result [$key] = 1;
136e2d88156SLarsDW223                }
137e2d88156SLarsDW223            }
138e2d88156SLarsDW223        }
139e2d88156SLarsDW223
140e2d88156SLarsDW223        // Check for supported options
141e2d88156SLarsDW223        $result = array_intersect_key(
142e2d88156SLarsDW223            $result,
143e2d88156SLarsDW223            array_flip(array(
144e2d88156SLarsDW223                           'enable_line_numbers',
145e2d88156SLarsDW223                           'start_line_numbers_at',
146e2d88156SLarsDW223                           'highlight_lines_extra',
147e2d88156SLarsDW223                           'enable_keyword_links')
148e2d88156SLarsDW223            )
149e2d88156SLarsDW223        );
150e2d88156SLarsDW223
151e2d88156SLarsDW223        // Sanitize values
152e2d88156SLarsDW223        if(isset($result['enable_line_numbers'])) {
15354f741e8SAndreas Gohr            if($result['enable_line_numbers'] === 'false') {
15454f741e8SAndreas Gohr                $result['enable_line_numbers'] = false;
15554f741e8SAndreas Gohr            }
156e2d88156SLarsDW223            $result['enable_line_numbers'] = (bool) $result['enable_line_numbers'];
157e2d88156SLarsDW223        }
158e2d88156SLarsDW223        if(isset($result['highlight_lines_extra'])) {
159e2d88156SLarsDW223            $result['highlight_lines_extra'] = array_map('intval', explode(',', $result['highlight_lines_extra']));
160e2d88156SLarsDW223            $result['highlight_lines_extra'] = array_filter($result['highlight_lines_extra']);
161e2d88156SLarsDW223            $result['highlight_lines_extra'] = array_unique($result['highlight_lines_extra']);
162e2d88156SLarsDW223        }
163e2d88156SLarsDW223        if(isset($result['start_line_numbers_at'])) {
164e2d88156SLarsDW223            $result['start_line_numbers_at'] = (int) $result['start_line_numbers_at'];
165e2d88156SLarsDW223        }
166e2d88156SLarsDW223        if(isset($result['enable_keyword_links'])) {
16754f741e8SAndreas Gohr            if($result['enable_keyword_links'] === 'false') {
16854f741e8SAndreas Gohr                $result['enable_keyword_links'] = false;
16954f741e8SAndreas Gohr            }
17054f741e8SAndreas Gohr            $result['enable_keyword_links'] = (bool) $result['enable_keyword_links'];
171e2d88156SLarsDW223        }
172e2d88156SLarsDW223        if (count($result) == 0) {
173e2d88156SLarsDW223            return null;
174e2d88156SLarsDW223        }
175e2d88156SLarsDW223
176e2d88156SLarsDW223        return $result;
177e2d88156SLarsDW223    }
178e2d88156SLarsDW223
179*8b1b81beSAndreas Gohr    /**
180*8b1b81beSAndreas Gohr     * Simplifies handling for the formatting tags which all behave the same
181*8b1b81beSAndreas Gohr     *
182*8b1b81beSAndreas Gohr     * @param string $match matched syntax
183*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
184*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
185*8b1b81beSAndreas Gohr     * @param string $name actual mode name
186*8b1b81beSAndreas Gohr     */
187*8b1b81beSAndreas Gohr    protected function nestingTag($match, $state, $pos, $name) {
188*8b1b81beSAndreas Gohr        switch ( $state ) {
189*8b1b81beSAndreas Gohr            case DOKU_LEXER_ENTER:
190*8b1b81beSAndreas Gohr                $this->addCall($name.'_open', array(), $pos);
191*8b1b81beSAndreas Gohr                break;
192*8b1b81beSAndreas Gohr            case DOKU_LEXER_EXIT:
193*8b1b81beSAndreas Gohr                $this->addCall($name.'_close', array(), $pos);
194*8b1b81beSAndreas Gohr                break;
195*8b1b81beSAndreas Gohr            case DOKU_LEXER_UNMATCHED:
196*8b1b81beSAndreas Gohr                $this->addCall('cdata', array($match), $pos);
197*8b1b81beSAndreas Gohr                break;
198*8b1b81beSAndreas Gohr        }
199*8b1b81beSAndreas Gohr    }
200*8b1b81beSAndreas Gohr
201*8b1b81beSAndreas Gohr
202*8b1b81beSAndreas Gohr    /**
203*8b1b81beSAndreas Gohr     * The following methods define the handlers for the different Syntax modes
204*8b1b81beSAndreas Gohr     *
205*8b1b81beSAndreas Gohr     * The handlers are called from dokuwiki\Lexer\Lexer\invokeParser()
206*8b1b81beSAndreas Gohr     *
207*8b1b81beSAndreas Gohr     * @todo it might make sense to move these into their own class or merge them with the
208*8b1b81beSAndreas Gohr     *       ParserMode classes some time.
209*8b1b81beSAndreas Gohr     */
210*8b1b81beSAndreas Gohr    // region mode handlers
211*8b1b81beSAndreas Gohr
212*8b1b81beSAndreas Gohr    /**
213*8b1b81beSAndreas Gohr     * Special plugin handler
214*8b1b81beSAndreas Gohr     *
215*8b1b81beSAndreas Gohr     * This handler is called for all modes starting with 'plugin_'.
216*8b1b81beSAndreas Gohr     * An additional parameter with the plugin name is passed. The plugin's handle()
217*8b1b81beSAndreas Gohr     * method is called here
218*8b1b81beSAndreas Gohr     *
219*8b1b81beSAndreas Gohr     * @author Andreas Gohr <andi@splitbrain.org>
220*8b1b81beSAndreas Gohr     *
221*8b1b81beSAndreas Gohr     * @param string $match matched syntax
222*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
223*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
224*8b1b81beSAndreas Gohr     * @param string $pluginname name of the plugin
225*8b1b81beSAndreas Gohr     * @return bool mode handled?
226*8b1b81beSAndreas Gohr     */
227*8b1b81beSAndreas Gohr    public function plugin($match, $state, $pos, $pluginname){
228*8b1b81beSAndreas Gohr        $data = array($match);
229*8b1b81beSAndreas Gohr        /** @var DokuWiki_Syntax_Plugin $plugin */
230*8b1b81beSAndreas Gohr        $plugin = plugin_load('syntax',$pluginname);
231*8b1b81beSAndreas Gohr        if($plugin != null){
232*8b1b81beSAndreas Gohr            $data = $plugin->handle($match, $state, $pos, $this);
233*8b1b81beSAndreas Gohr        }
234*8b1b81beSAndreas Gohr        if ($data !== false) {
235*8b1b81beSAndreas Gohr            $this->addPluginCall($pluginname,$data,$state,$pos,$match);
236*8b1b81beSAndreas Gohr        }
237*8b1b81beSAndreas Gohr        return true;
238*8b1b81beSAndreas Gohr    }
239*8b1b81beSAndreas Gohr
240*8b1b81beSAndreas Gohr    /**
241*8b1b81beSAndreas Gohr     * @param string $match matched syntax
242*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
243*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
244*8b1b81beSAndreas Gohr     * @return bool mode handled?
245*8b1b81beSAndreas Gohr     */
246*8b1b81beSAndreas Gohr    public function base($match, $state, $pos) {
247*8b1b81beSAndreas Gohr        switch ( $state ) {
248*8b1b81beSAndreas Gohr            case DOKU_LEXER_UNMATCHED:
249*8b1b81beSAndreas Gohr                $this->addCall('cdata', array($match), $pos);
250*8b1b81beSAndreas Gohr                return true;
251*8b1b81beSAndreas Gohr            break;
252*8b1b81beSAndreas Gohr        }
253*8b1b81beSAndreas Gohr        return false;
254*8b1b81beSAndreas Gohr    }
255*8b1b81beSAndreas Gohr
256*8b1b81beSAndreas Gohr    /**
257*8b1b81beSAndreas Gohr     * @param string $match matched syntax
258*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
259*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
260*8b1b81beSAndreas Gohr     * @return bool mode handled?
261*8b1b81beSAndreas Gohr     */
262*8b1b81beSAndreas Gohr    public function header($match, $state, $pos) {
263*8b1b81beSAndreas Gohr        // get level and title
264*8b1b81beSAndreas Gohr        $title = trim($match);
265*8b1b81beSAndreas Gohr        $level = 7 - strspn($title,'=');
266*8b1b81beSAndreas Gohr        if($level < 1) $level = 1;
267*8b1b81beSAndreas Gohr        $title = trim($title,'=');
268*8b1b81beSAndreas Gohr        $title = trim($title);
269*8b1b81beSAndreas Gohr
270*8b1b81beSAndreas Gohr        if ($this->status['section']) $this->addCall('section_close', array(), $pos);
271*8b1b81beSAndreas Gohr
272*8b1b81beSAndreas Gohr        $this->addCall('header', array($title, $level, $pos), $pos);
273*8b1b81beSAndreas Gohr
274*8b1b81beSAndreas Gohr        $this->addCall('section_open', array($level), $pos);
275*8b1b81beSAndreas Gohr        $this->status['section'] = true;
276*8b1b81beSAndreas Gohr        return true;
277*8b1b81beSAndreas Gohr    }
278*8b1b81beSAndreas Gohr
279*8b1b81beSAndreas Gohr    /**
280*8b1b81beSAndreas Gohr     * @param string $match matched syntax
281*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
282*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
283*8b1b81beSAndreas Gohr     * @return bool mode handled?
284*8b1b81beSAndreas Gohr     */
285*8b1b81beSAndreas Gohr    public function notoc($match, $state, $pos) {
286*8b1b81beSAndreas Gohr        $this->addCall('notoc', array(), $pos);
287*8b1b81beSAndreas Gohr        return true;
288*8b1b81beSAndreas Gohr    }
289*8b1b81beSAndreas Gohr
290*8b1b81beSAndreas Gohr    /**
291*8b1b81beSAndreas Gohr     * @param string $match matched syntax
292*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
293*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
294*8b1b81beSAndreas Gohr     * @return bool mode handled?
295*8b1b81beSAndreas Gohr     */
296*8b1b81beSAndreas Gohr    public function nocache($match, $state, $pos) {
297*8b1b81beSAndreas Gohr        $this->addCall('nocache', array(), $pos);
298*8b1b81beSAndreas Gohr        return true;
299*8b1b81beSAndreas Gohr    }
300*8b1b81beSAndreas Gohr
301*8b1b81beSAndreas Gohr    /**
302*8b1b81beSAndreas Gohr     * @param string $match matched syntax
303*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
304*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
305*8b1b81beSAndreas Gohr     * @return bool mode handled?
306*8b1b81beSAndreas Gohr     */
307*8b1b81beSAndreas Gohr    public function linebreak($match, $state, $pos) {
308*8b1b81beSAndreas Gohr        $this->addCall('linebreak', array(), $pos);
309*8b1b81beSAndreas Gohr        return true;
310*8b1b81beSAndreas Gohr    }
311*8b1b81beSAndreas Gohr
312*8b1b81beSAndreas Gohr    /**
313*8b1b81beSAndreas Gohr     * @param string $match matched syntax
314*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
315*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
316*8b1b81beSAndreas Gohr     * @return bool mode handled?
317*8b1b81beSAndreas Gohr     */
318*8b1b81beSAndreas Gohr    public function eol($match, $state, $pos) {
319*8b1b81beSAndreas Gohr        $this->addCall('eol', array(), $pos);
320*8b1b81beSAndreas Gohr        return true;
321*8b1b81beSAndreas Gohr    }
322*8b1b81beSAndreas Gohr
323*8b1b81beSAndreas Gohr    /**
324*8b1b81beSAndreas Gohr     * @param string $match matched syntax
325*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
326*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
327*8b1b81beSAndreas Gohr     * @return bool mode handled?
328*8b1b81beSAndreas Gohr     */
329*8b1b81beSAndreas Gohr    public function hr($match, $state, $pos) {
330*8b1b81beSAndreas Gohr        $this->addCall('hr', array(), $pos);
331*8b1b81beSAndreas Gohr        return true;
332*8b1b81beSAndreas Gohr    }
333*8b1b81beSAndreas Gohr
334*8b1b81beSAndreas Gohr    /**
335*8b1b81beSAndreas Gohr     * @param string $match matched syntax
336*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
337*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
338*8b1b81beSAndreas Gohr     * @return bool mode handled?
339*8b1b81beSAndreas Gohr     */
340*8b1b81beSAndreas Gohr    public function strong($match, $state, $pos) {
341*8b1b81beSAndreas Gohr        $this->nestingTag($match, $state, $pos, 'strong');
342*8b1b81beSAndreas Gohr        return true;
343*8b1b81beSAndreas Gohr    }
344*8b1b81beSAndreas Gohr
345*8b1b81beSAndreas Gohr    /**
346*8b1b81beSAndreas Gohr     * @param string $match matched syntax
347*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
348*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
349*8b1b81beSAndreas Gohr     * @return bool mode handled?
350*8b1b81beSAndreas Gohr     */
351*8b1b81beSAndreas Gohr    public function emphasis($match, $state, $pos) {
352*8b1b81beSAndreas Gohr        $this->nestingTag($match, $state, $pos, 'emphasis');
353*8b1b81beSAndreas Gohr        return true;
354*8b1b81beSAndreas Gohr    }
355*8b1b81beSAndreas Gohr
356*8b1b81beSAndreas Gohr    /**
357*8b1b81beSAndreas Gohr     * @param string $match matched syntax
358*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
359*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
360*8b1b81beSAndreas Gohr     * @return bool mode handled?
361*8b1b81beSAndreas Gohr     */
362*8b1b81beSAndreas Gohr    public function underline($match, $state, $pos) {
363*8b1b81beSAndreas Gohr        $this->nestingTag($match, $state, $pos, 'underline');
364*8b1b81beSAndreas Gohr        return true;
365*8b1b81beSAndreas Gohr    }
366*8b1b81beSAndreas Gohr
367*8b1b81beSAndreas Gohr    /**
368*8b1b81beSAndreas Gohr     * @param string $match matched syntax
369*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
370*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
371*8b1b81beSAndreas Gohr     * @return bool mode handled?
372*8b1b81beSAndreas Gohr     */
373*8b1b81beSAndreas Gohr    public function monospace($match, $state, $pos) {
374*8b1b81beSAndreas Gohr        $this->nestingTag($match, $state, $pos, 'monospace');
375*8b1b81beSAndreas Gohr        return true;
376*8b1b81beSAndreas Gohr    }
377*8b1b81beSAndreas Gohr
378*8b1b81beSAndreas Gohr    /**
379*8b1b81beSAndreas Gohr     * @param string $match matched syntax
380*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
381*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
382*8b1b81beSAndreas Gohr     * @return bool mode handled?
383*8b1b81beSAndreas Gohr     */
384*8b1b81beSAndreas Gohr    public function subscript($match, $state, $pos) {
385*8b1b81beSAndreas Gohr        $this->nestingTag($match, $state, $pos, 'subscript');
386*8b1b81beSAndreas Gohr        return true;
387*8b1b81beSAndreas Gohr    }
388*8b1b81beSAndreas Gohr
389*8b1b81beSAndreas Gohr    /**
390*8b1b81beSAndreas Gohr     * @param string $match matched syntax
391*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
392*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
393*8b1b81beSAndreas Gohr     * @return bool mode handled?
394*8b1b81beSAndreas Gohr     */
395*8b1b81beSAndreas Gohr    public function superscript($match, $state, $pos) {
396*8b1b81beSAndreas Gohr        $this->nestingTag($match, $state, $pos, 'superscript');
397*8b1b81beSAndreas Gohr        return true;
398*8b1b81beSAndreas Gohr    }
399*8b1b81beSAndreas Gohr
400*8b1b81beSAndreas Gohr    /**
401*8b1b81beSAndreas Gohr     * @param string $match matched syntax
402*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
403*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
404*8b1b81beSAndreas Gohr     * @return bool mode handled?
405*8b1b81beSAndreas Gohr     */
406*8b1b81beSAndreas Gohr    public function deleted($match, $state, $pos) {
407*8b1b81beSAndreas Gohr        $this->nestingTag($match, $state, $pos, 'deleted');
408*8b1b81beSAndreas Gohr        return true;
409*8b1b81beSAndreas Gohr    }
410*8b1b81beSAndreas Gohr
411*8b1b81beSAndreas Gohr    /**
412*8b1b81beSAndreas Gohr     * @param string $match matched syntax
413*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
414*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
415*8b1b81beSAndreas Gohr     * @return bool mode handled?
416*8b1b81beSAndreas Gohr     */
417*8b1b81beSAndreas Gohr    public function footnote($match, $state, $pos) {
418*8b1b81beSAndreas Gohr        if (!isset($this->_footnote)) $this->_footnote = false;
419*8b1b81beSAndreas Gohr
420*8b1b81beSAndreas Gohr        switch ( $state ) {
421*8b1b81beSAndreas Gohr            case DOKU_LEXER_ENTER:
422*8b1b81beSAndreas Gohr                // footnotes can not be nested - however due to limitations in lexer it can't be prevented
423*8b1b81beSAndreas Gohr                // we will still enter a new footnote mode, we just do nothing
424*8b1b81beSAndreas Gohr                if ($this->_footnote) {
425*8b1b81beSAndreas Gohr                    $this->addCall('cdata', array($match), $pos);
426*8b1b81beSAndreas Gohr                    break;
427*8b1b81beSAndreas Gohr                }
428*8b1b81beSAndreas Gohr                $this->_footnote = true;
429*8b1b81beSAndreas Gohr
430*8b1b81beSAndreas Gohr                $this->callWriter = new Nest($this->callWriter, 'footnote_close');
431*8b1b81beSAndreas Gohr                $this->addCall('footnote_open', array(), $pos);
432*8b1b81beSAndreas Gohr            break;
433*8b1b81beSAndreas Gohr            case DOKU_LEXER_EXIT:
434*8b1b81beSAndreas Gohr                // check whether we have already exitted the footnote mode, can happen if the modes were nested
435*8b1b81beSAndreas Gohr                if (!$this->_footnote) {
436*8b1b81beSAndreas Gohr                    $this->addCall('cdata', array($match), $pos);
437*8b1b81beSAndreas Gohr                    break;
438*8b1b81beSAndreas Gohr                }
439*8b1b81beSAndreas Gohr
440*8b1b81beSAndreas Gohr                $this->_footnote = false;
441*8b1b81beSAndreas Gohr                $this->addCall('footnote_close', array(), $pos);
442*8b1b81beSAndreas Gohr
443*8b1b81beSAndreas Gohr                /** @var Nest $reWriter */
444*8b1b81beSAndreas Gohr                $reWriter = $this->callWriter;
445*8b1b81beSAndreas Gohr                $this->callWriter = $reWriter->process();
446*8b1b81beSAndreas Gohr            break;
447*8b1b81beSAndreas Gohr            case DOKU_LEXER_UNMATCHED:
448*8b1b81beSAndreas Gohr                $this->addCall('cdata', array($match), $pos);
449*8b1b81beSAndreas Gohr            break;
450*8b1b81beSAndreas Gohr        }
451*8b1b81beSAndreas Gohr        return true;
452*8b1b81beSAndreas Gohr    }
453*8b1b81beSAndreas Gohr
454*8b1b81beSAndreas Gohr    /**
455*8b1b81beSAndreas Gohr     * @param string $match matched syntax
456*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
457*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
458*8b1b81beSAndreas Gohr     * @return bool mode handled?
459*8b1b81beSAndreas Gohr     */
460*8b1b81beSAndreas Gohr    public function listblock($match, $state, $pos) {
461*8b1b81beSAndreas Gohr        switch ( $state ) {
462*8b1b81beSAndreas Gohr            case DOKU_LEXER_ENTER:
463*8b1b81beSAndreas Gohr                $this->callWriter = new Lists($this->callWriter);
464*8b1b81beSAndreas Gohr                $this->addCall('list_open', array($match), $pos);
465*8b1b81beSAndreas Gohr            break;
466*8b1b81beSAndreas Gohr            case DOKU_LEXER_EXIT:
467*8b1b81beSAndreas Gohr                $this->addCall('list_close', array(), $pos);
468*8b1b81beSAndreas Gohr                /** @var Lists $reWriter */
469*8b1b81beSAndreas Gohr                $reWriter = $this->callWriter;
470*8b1b81beSAndreas Gohr                $this->callWriter = $reWriter->process();
471*8b1b81beSAndreas Gohr            break;
472*8b1b81beSAndreas Gohr            case DOKU_LEXER_MATCHED:
473*8b1b81beSAndreas Gohr                $this->addCall('list_item', array($match), $pos);
474*8b1b81beSAndreas Gohr            break;
475*8b1b81beSAndreas Gohr            case DOKU_LEXER_UNMATCHED:
476*8b1b81beSAndreas Gohr                $this->addCall('cdata', array($match), $pos);
477*8b1b81beSAndreas Gohr            break;
478*8b1b81beSAndreas Gohr        }
479*8b1b81beSAndreas Gohr        return true;
480*8b1b81beSAndreas Gohr    }
481*8b1b81beSAndreas Gohr
482*8b1b81beSAndreas Gohr    /**
483*8b1b81beSAndreas Gohr     * @param string $match matched syntax
484*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
485*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
486*8b1b81beSAndreas Gohr     * @return bool mode handled?
487*8b1b81beSAndreas Gohr     */
488*8b1b81beSAndreas Gohr    public function unformatted($match, $state, $pos) {
489*8b1b81beSAndreas Gohr        if ( $state == DOKU_LEXER_UNMATCHED ) {
490*8b1b81beSAndreas Gohr            $this->addCall('unformatted', array($match), $pos);
491*8b1b81beSAndreas Gohr        }
492*8b1b81beSAndreas Gohr        return true;
493*8b1b81beSAndreas Gohr    }
494*8b1b81beSAndreas Gohr
495*8b1b81beSAndreas Gohr    /**
496*8b1b81beSAndreas Gohr     * @param string $match matched syntax
497*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
498*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
499*8b1b81beSAndreas Gohr     * @return bool mode handled?
500*8b1b81beSAndreas Gohr     */
501*8b1b81beSAndreas Gohr    public function php($match, $state, $pos) {
502*8b1b81beSAndreas Gohr        if ( $state == DOKU_LEXER_UNMATCHED ) {
503*8b1b81beSAndreas Gohr            $this->addCall('php', array($match), $pos);
504*8b1b81beSAndreas Gohr        }
505*8b1b81beSAndreas Gohr        return true;
506*8b1b81beSAndreas Gohr    }
507*8b1b81beSAndreas Gohr
508*8b1b81beSAndreas Gohr    /**
509*8b1b81beSAndreas Gohr     * @param string $match matched syntax
510*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
511*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
512*8b1b81beSAndreas Gohr     * @return bool mode handled?
513*8b1b81beSAndreas Gohr     */
514*8b1b81beSAndreas Gohr    public function phpblock($match, $state, $pos) {
515*8b1b81beSAndreas Gohr        if ( $state == DOKU_LEXER_UNMATCHED ) {
516*8b1b81beSAndreas Gohr            $this->addCall('phpblock', array($match), $pos);
517*8b1b81beSAndreas Gohr        }
518*8b1b81beSAndreas Gohr        return true;
519*8b1b81beSAndreas Gohr    }
520*8b1b81beSAndreas Gohr
521*8b1b81beSAndreas Gohr    /**
522*8b1b81beSAndreas Gohr     * @param string $match matched syntax
523*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
524*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
525*8b1b81beSAndreas Gohr     * @return bool mode handled?
526*8b1b81beSAndreas Gohr     */
527*8b1b81beSAndreas Gohr    public function html($match, $state, $pos) {
528*8b1b81beSAndreas Gohr        if ( $state == DOKU_LEXER_UNMATCHED ) {
529*8b1b81beSAndreas Gohr            $this->addCall('html', array($match), $pos);
530*8b1b81beSAndreas Gohr        }
531*8b1b81beSAndreas Gohr        return true;
532*8b1b81beSAndreas Gohr    }
533*8b1b81beSAndreas Gohr
534*8b1b81beSAndreas Gohr    /**
535*8b1b81beSAndreas Gohr     * @param string $match matched syntax
536*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
537*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
538*8b1b81beSAndreas Gohr     * @return bool mode handled?
539*8b1b81beSAndreas Gohr     */
540*8b1b81beSAndreas Gohr    public function htmlblock($match, $state, $pos) {
541*8b1b81beSAndreas Gohr        if ( $state == DOKU_LEXER_UNMATCHED ) {
542*8b1b81beSAndreas Gohr            $this->addCall('htmlblock', array($match), $pos);
543*8b1b81beSAndreas Gohr        }
544*8b1b81beSAndreas Gohr        return true;
545*8b1b81beSAndreas Gohr    }
546*8b1b81beSAndreas Gohr
547*8b1b81beSAndreas Gohr    /**
548*8b1b81beSAndreas Gohr     * @param string $match matched syntax
549*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
550*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
551*8b1b81beSAndreas Gohr     * @return bool mode handled?
552*8b1b81beSAndreas Gohr     */
553*8b1b81beSAndreas Gohr    public function preformatted($match, $state, $pos) {
554*8b1b81beSAndreas Gohr        switch ( $state ) {
555*8b1b81beSAndreas Gohr            case DOKU_LEXER_ENTER:
556*8b1b81beSAndreas Gohr                $this->callWriter = new Preformatted($this->callWriter);
557*8b1b81beSAndreas Gohr                $this->addCall('preformatted_start', array(), $pos);
558*8b1b81beSAndreas Gohr            break;
559*8b1b81beSAndreas Gohr            case DOKU_LEXER_EXIT:
560*8b1b81beSAndreas Gohr                $this->addCall('preformatted_end', array(), $pos);
561*8b1b81beSAndreas Gohr                /** @var Preformatted $reWriter */
562*8b1b81beSAndreas Gohr                $reWriter = $this->callWriter;
563*8b1b81beSAndreas Gohr                $this->callWriter = $reWriter->process();
564*8b1b81beSAndreas Gohr            break;
565*8b1b81beSAndreas Gohr            case DOKU_LEXER_MATCHED:
566*8b1b81beSAndreas Gohr                $this->addCall('preformatted_newline', array(), $pos);
567*8b1b81beSAndreas Gohr            break;
568*8b1b81beSAndreas Gohr            case DOKU_LEXER_UNMATCHED:
569*8b1b81beSAndreas Gohr                $this->addCall('preformatted_content', array($match), $pos);
570*8b1b81beSAndreas Gohr            break;
571*8b1b81beSAndreas Gohr        }
572*8b1b81beSAndreas Gohr
573*8b1b81beSAndreas Gohr        return true;
574*8b1b81beSAndreas Gohr    }
575*8b1b81beSAndreas Gohr
576*8b1b81beSAndreas Gohr    /**
577*8b1b81beSAndreas Gohr     * @param string $match matched syntax
578*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
579*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
580*8b1b81beSAndreas Gohr     * @return bool mode handled?
581*8b1b81beSAndreas Gohr     */
582*8b1b81beSAndreas Gohr    public function quote($match, $state, $pos) {
583*8b1b81beSAndreas Gohr
584*8b1b81beSAndreas Gohr        switch ( $state ) {
585*8b1b81beSAndreas Gohr
586*8b1b81beSAndreas Gohr            case DOKU_LEXER_ENTER:
587*8b1b81beSAndreas Gohr                $this->callWriter = new Quote($this->callWriter);
588*8b1b81beSAndreas Gohr                $this->addCall('quote_start', array($match), $pos);
589*8b1b81beSAndreas Gohr            break;
590*8b1b81beSAndreas Gohr
591*8b1b81beSAndreas Gohr            case DOKU_LEXER_EXIT:
592*8b1b81beSAndreas Gohr                $this->addCall('quote_end', array(), $pos);
593*8b1b81beSAndreas Gohr                /** @var Lists $reWriter */
594*8b1b81beSAndreas Gohr                $reWriter = $this->callWriter;
595*8b1b81beSAndreas Gohr                $this->callWriter = $reWriter->process();
596*8b1b81beSAndreas Gohr            break;
597*8b1b81beSAndreas Gohr
598*8b1b81beSAndreas Gohr            case DOKU_LEXER_MATCHED:
599*8b1b81beSAndreas Gohr                $this->addCall('quote_newline', array($match), $pos);
600*8b1b81beSAndreas Gohr            break;
601*8b1b81beSAndreas Gohr
602*8b1b81beSAndreas Gohr            case DOKU_LEXER_UNMATCHED:
603*8b1b81beSAndreas Gohr                $this->addCall('cdata', array($match), $pos);
604*8b1b81beSAndreas Gohr            break;
605*8b1b81beSAndreas Gohr
606*8b1b81beSAndreas Gohr        }
607*8b1b81beSAndreas Gohr
608*8b1b81beSAndreas Gohr        return true;
609*8b1b81beSAndreas Gohr    }
610*8b1b81beSAndreas Gohr
611*8b1b81beSAndreas Gohr    /**
612*8b1b81beSAndreas Gohr     * @param string $match matched syntax
613*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
614*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
615*8b1b81beSAndreas Gohr     * @return bool mode handled?
616*8b1b81beSAndreas Gohr     */
617*8b1b81beSAndreas Gohr    public function file($match, $state, $pos) {
6183d491f75SAndreas Gohr        return $this->code($match, $state, $pos, 'file');
6193d491f75SAndreas Gohr    }
6203d491f75SAndreas Gohr
621*8b1b81beSAndreas Gohr    /**
622*8b1b81beSAndreas Gohr     * @param string $match matched syntax
623*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
624*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
625*8b1b81beSAndreas Gohr     * @param string $type either 'code' or 'file'
626*8b1b81beSAndreas Gohr     * @return bool mode handled?
627*8b1b81beSAndreas Gohr     */
628*8b1b81beSAndreas Gohr    public function code($match, $state, $pos, $type='code') {
6293d491f75SAndreas Gohr        if ( $state == DOKU_LEXER_UNMATCHED ) {
6304b7f9e70STom N Harris            $matches = explode('>',$match,2);
631e2d88156SLarsDW223            // Cut out variable options enclosed in []
632e2d88156SLarsDW223            preg_match('/\[.*\]/', $matches[0], $options);
633e2d88156SLarsDW223            if (!empty($options[0])) {
634e2d88156SLarsDW223                $matches[0] = str_replace($options[0], '', $matches[0]);
635e2d88156SLarsDW223            }
6360139312bSAdrian Lang            $param = preg_split('/\s+/', $matches[0], 2, PREG_SPLIT_NO_EMPTY);
6370139312bSAdrian Lang            while(count($param) < 2) array_push($param, null);
6380139312bSAdrian Lang            // We shortcut html here.
6390139312bSAdrian Lang            if ($param[0] == 'html') $param[0] = 'html4strict';
6400139312bSAdrian Lang            if ($param[0] == '-') $param[0] = null;
6410139312bSAdrian Lang            array_unshift($param, $matches[1]);
642e2d88156SLarsDW223            if (!empty($options[0])) {
643e2d88156SLarsDW223                $param [] = $this->parse_highlight_options ($options[0]);
644e2d88156SLarsDW223            }
645*8b1b81beSAndreas Gohr            $this->addCall($type, $param, $pos);
6460cecf9d5Sandi        }
64744881bd0Shenning.noren        return true;
6480cecf9d5Sandi    }
6490cecf9d5Sandi
650*8b1b81beSAndreas Gohr    /**
651*8b1b81beSAndreas Gohr     * @param string $match matched syntax
652*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
653*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
654*8b1b81beSAndreas Gohr     * @return bool mode handled?
655*8b1b81beSAndreas Gohr     */
656*8b1b81beSAndreas Gohr    public function acronym($match, $state, $pos) {
657*8b1b81beSAndreas Gohr        $this->addCall('acronym', array($match), $pos);
65844881bd0Shenning.noren        return true;
6590cecf9d5Sandi    }
6600cecf9d5Sandi
661*8b1b81beSAndreas Gohr    /**
662*8b1b81beSAndreas Gohr     * @param string $match matched syntax
663*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
664*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
665*8b1b81beSAndreas Gohr     * @return bool mode handled?
666*8b1b81beSAndreas Gohr     */
667*8b1b81beSAndreas Gohr    public function smiley($match, $state, $pos) {
668*8b1b81beSAndreas Gohr        $this->addCall('smiley', array($match), $pos);
66944881bd0Shenning.noren        return true;
6700cecf9d5Sandi    }
6710cecf9d5Sandi
672*8b1b81beSAndreas Gohr    /**
673*8b1b81beSAndreas Gohr     * @param string $match matched syntax
674*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
675*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
676*8b1b81beSAndreas Gohr     * @return bool mode handled?
677*8b1b81beSAndreas Gohr     */
678*8b1b81beSAndreas Gohr    public function wordblock($match, $state, $pos) {
679*8b1b81beSAndreas Gohr        $this->addCall('wordblock', array($match), $pos);
68044881bd0Shenning.noren        return true;
6810cecf9d5Sandi    }
6820cecf9d5Sandi
683*8b1b81beSAndreas Gohr    /**
684*8b1b81beSAndreas Gohr     * @param string $match matched syntax
685*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
686*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
687*8b1b81beSAndreas Gohr     * @return bool mode handled?
688*8b1b81beSAndreas Gohr     */
689*8b1b81beSAndreas Gohr    public function entity($match, $state, $pos) {
690*8b1b81beSAndreas Gohr        $this->addCall('entity', array($match), $pos);
69144881bd0Shenning.noren        return true;
6920cecf9d5Sandi    }
6930cecf9d5Sandi
694*8b1b81beSAndreas Gohr    /**
695*8b1b81beSAndreas Gohr     * @param string $match matched syntax
696*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
697*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
698*8b1b81beSAndreas Gohr     * @return bool mode handled?
699*8b1b81beSAndreas Gohr     */
700*8b1b81beSAndreas Gohr    public function multiplyentity($match, $state, $pos) {
7010cecf9d5Sandi        preg_match_all('/\d+/',$match,$matches);
702*8b1b81beSAndreas Gohr        $this->addCall('multiplyentity', array($matches[0][0], $matches[0][1]), $pos);
70344881bd0Shenning.noren        return true;
7040cecf9d5Sandi    }
7050cecf9d5Sandi
706*8b1b81beSAndreas Gohr    /**
707*8b1b81beSAndreas Gohr     * @param string $match matched syntax
708*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
709*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
710*8b1b81beSAndreas Gohr     * @return bool mode handled?
711*8b1b81beSAndreas Gohr     */
712*8b1b81beSAndreas Gohr    public function singlequoteopening($match, $state, $pos) {
713*8b1b81beSAndreas Gohr        $this->addCall('singlequoteopening', array(), $pos);
71444881bd0Shenning.noren        return true;
7150cecf9d5Sandi    }
7160cecf9d5Sandi
717*8b1b81beSAndreas Gohr    /**
718*8b1b81beSAndreas Gohr     * @param string $match matched syntax
719*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
720*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
721*8b1b81beSAndreas Gohr     * @return bool mode handled?
722*8b1b81beSAndreas Gohr     */
723*8b1b81beSAndreas Gohr    public function singlequoteclosing($match, $state, $pos) {
724*8b1b81beSAndreas Gohr        $this->addCall('singlequoteclosing', array(), $pos);
72544881bd0Shenning.noren        return true;
7260cecf9d5Sandi    }
7270cecf9d5Sandi
728*8b1b81beSAndreas Gohr    /**
729*8b1b81beSAndreas Gohr     * @param string $match matched syntax
730*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
731*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
732*8b1b81beSAndreas Gohr     * @return bool mode handled?
733*8b1b81beSAndreas Gohr     */
734*8b1b81beSAndreas Gohr    public function apostrophe($match, $state, $pos) {
735*8b1b81beSAndreas Gohr        $this->addCall('apostrophe', array(), $pos);
73657d757d1SAndreas Gohr        return true;
73757d757d1SAndreas Gohr    }
73857d757d1SAndreas Gohr
739*8b1b81beSAndreas Gohr    /**
740*8b1b81beSAndreas Gohr     * @param string $match matched syntax
741*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
742*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
743*8b1b81beSAndreas Gohr     * @return bool mode handled?
744*8b1b81beSAndreas Gohr     */
745*8b1b81beSAndreas Gohr    public function doublequoteopening($match, $state, $pos) {
746*8b1b81beSAndreas Gohr        $this->addCall('doublequoteopening', array(), $pos);
747e950d12fSChristopher Smith        $this->status['doublequote']++;
74844881bd0Shenning.noren        return true;
7490cecf9d5Sandi    }
7500cecf9d5Sandi
751*8b1b81beSAndreas Gohr    /**
752*8b1b81beSAndreas Gohr     * @param string $match matched syntax
753*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
754*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
755*8b1b81beSAndreas Gohr     * @return bool mode handled?
756*8b1b81beSAndreas Gohr     */
757*8b1b81beSAndreas Gohr    public function doublequoteclosing($match, $state, $pos) {
758e950d12fSChristopher Smith        if ($this->status['doublequote'] <= 0) {
759e950d12fSChristopher Smith            $this->doublequoteopening($match, $state, $pos);
760e950d12fSChristopher Smith        } else {
761*8b1b81beSAndreas Gohr            $this->addCall('doublequoteclosing', array(), $pos);
762e950d12fSChristopher Smith            $this->status['doublequote'] = max(0, --$this->status['doublequote']);
763e950d12fSChristopher Smith        }
76444881bd0Shenning.noren        return true;
7650cecf9d5Sandi    }
7660cecf9d5Sandi
767*8b1b81beSAndreas Gohr    /**
768*8b1b81beSAndreas Gohr     * @param string $match matched syntax
769*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
770*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
771*8b1b81beSAndreas Gohr     * @return bool mode handled?
772*8b1b81beSAndreas Gohr     */
773*8b1b81beSAndreas Gohr    public function camelcaselink($match, $state, $pos) {
774*8b1b81beSAndreas Gohr        $this->addCall('camelcaselink', array($match), $pos);
77544881bd0Shenning.noren        return true;
7760cecf9d5Sandi    }
7770cecf9d5Sandi
778*8b1b81beSAndreas Gohr    /**
779*8b1b81beSAndreas Gohr     * @param string $match matched syntax
780*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
781*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
782*8b1b81beSAndreas Gohr     * @return bool mode handled?
7830cecf9d5Sandi     */
784*8b1b81beSAndreas Gohr    public function internallink($match, $state, $pos) {
7850cecf9d5Sandi        // Strip the opening and closing markup
7860cecf9d5Sandi        $link = preg_replace(array('/^\[\[/','/\]\]$/u'),'',$match);
7870cecf9d5Sandi
7880cecf9d5Sandi        // Split title from URL
7894b7f9e70STom N Harris        $link = explode('|',$link,2);
7900cecf9d5Sandi        if ( !isset($link[1]) ) {
7910ea51e63SMatt Perry            $link[1] = null;
7920cecf9d5Sandi        } else if ( preg_match('/^\{\{[^\}]+\}\}$/',$link[1]) ) {
7935578eb8fSandi            // If the title is an image, convert it to an array containing the image details
794b625487dSandi            $link[1] = Doku_Handler_Parse_Media($link[1]);
7950cecf9d5Sandi        }
7960b7c14c2Sandi        $link[0] = trim($link[0]);
7970cecf9d5Sandi
7980e1c636eSandi        //decide which kind of link it is
7990e1c636eSandi
8006efc45a2SDmitry Katsubo        if ( link_isinterwiki($link[0]) ) {
8010e1c636eSandi            // Interwiki
8024b7f9e70STom N Harris            $interwiki = explode('>',$link[0],2);
803*8b1b81beSAndreas Gohr            $this->addCall(
8040cecf9d5Sandi                'interwikilink',
8050cecf9d5Sandi                array($link[0],$link[1],strtolower($interwiki[0]),$interwiki[1]),
8060cecf9d5Sandi                $pos
8070cecf9d5Sandi                );
80815f1b77cSAndreas Gohr        }elseif ( preg_match('/^\\\\\\\\[^\\\\]+?\\\\/u',$link[0]) ) {
8090e1c636eSandi            // Windows Share
810*8b1b81beSAndreas Gohr            $this->addCall(
8110cecf9d5Sandi                'windowssharelink',
8120cecf9d5Sandi                array($link[0],$link[1]),
8130cecf9d5Sandi                $pos
8140cecf9d5Sandi                );
8154468cb4cSAndreas Gohr        }elseif ( preg_match('#^([a-z0-9\-\.+]+?)://#i',$link[0]) ) {
8160e1c636eSandi            // external link (accepts all protocols)
817*8b1b81beSAndreas Gohr            $this->addCall(
8180cecf9d5Sandi                    'externallink',
8190cecf9d5Sandi                    array($link[0],$link[1]),
8200cecf9d5Sandi                    $pos
8210cecf9d5Sandi                    );
8220a1d30bfSchris        }elseif ( preg_match('<'.PREG_PATTERN_VALID_EMAIL.'>',$link[0]) ) {
8230a1d30bfSchris            // E-Mail (pattern above is defined in inc/mail.php)
824*8b1b81beSAndreas Gohr            $this->addCall(
825a6755281Sandi                'emaillink',
826a6755281Sandi                array($link[0],$link[1]),
827a6755281Sandi                $pos
828a6755281Sandi                );
8290b7c14c2Sandi        }elseif ( preg_match('!^#.+!',$link[0]) ){
8300b7c14c2Sandi            // local link
831*8b1b81beSAndreas Gohr            $this->addCall(
8320b7c14c2Sandi                'locallink',
8330b7c14c2Sandi                array(substr($link[0],1),$link[1]),
8340b7c14c2Sandi                $pos
8350b7c14c2Sandi                );
8360e1c636eSandi        }else{
8370e1c636eSandi            // internal link
838*8b1b81beSAndreas Gohr            $this->addCall(
8390e1c636eSandi                'internallink',
8400e1c636eSandi                array($link[0],$link[1]),
8410e1c636eSandi                $pos
8420e1c636eSandi                );
8430cecf9d5Sandi        }
8440e1c636eSandi
84544881bd0Shenning.noren        return true;
8460cecf9d5Sandi    }
8470cecf9d5Sandi
848*8b1b81beSAndreas Gohr    /**
849*8b1b81beSAndreas Gohr     * @param string $match matched syntax
850*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
851*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
852*8b1b81beSAndreas Gohr     * @return bool mode handled?
853*8b1b81beSAndreas Gohr     */
854*8b1b81beSAndreas Gohr    public function filelink($match, $state, $pos) {
855*8b1b81beSAndreas Gohr        $this->addCall('filelink', array($match, null), $pos);
85644881bd0Shenning.noren        return true;
8570cecf9d5Sandi    }
8580cecf9d5Sandi
859*8b1b81beSAndreas Gohr    /**
860*8b1b81beSAndreas Gohr     * @param string $match matched syntax
861*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
862*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
863*8b1b81beSAndreas Gohr     * @return bool mode handled?
864*8b1b81beSAndreas Gohr     */
865*8b1b81beSAndreas Gohr    public function windowssharelink($match, $state, $pos) {
866*8b1b81beSAndreas Gohr        $this->addCall('windowssharelink', array($match, null), $pos);
86744881bd0Shenning.noren        return true;
8680cecf9d5Sandi    }
8690cecf9d5Sandi
870*8b1b81beSAndreas Gohr    /**
871*8b1b81beSAndreas Gohr     * @param string $match matched syntax
872*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
873*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
874*8b1b81beSAndreas Gohr     * @return bool mode handled?
875*8b1b81beSAndreas Gohr     */
876*8b1b81beSAndreas Gohr    public function media($match, $state, $pos) {
8770cecf9d5Sandi        $p = Doku_Handler_Parse_Media($match);
8780cecf9d5Sandi
879*8b1b81beSAndreas Gohr        $this->addCall(
8800cecf9d5Sandi              $p['type'],
881dc673a5bSjoe.lapp              array($p['src'], $p['title'], $p['align'], $p['width'],
882dc673a5bSjoe.lapp                     $p['height'], $p['cache'], $p['linking']),
8830cecf9d5Sandi              $pos
8840cecf9d5Sandi             );
88544881bd0Shenning.noren        return true;
8860cecf9d5Sandi    }
8870cecf9d5Sandi
888*8b1b81beSAndreas Gohr    /**
889*8b1b81beSAndreas Gohr     * @param string $match matched syntax
890*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
891*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
892*8b1b81beSAndreas Gohr     * @return bool mode handled?
893*8b1b81beSAndreas Gohr     */
894*8b1b81beSAndreas Gohr    public function rss($match, $state, $pos) {
895b625487dSandi        $link = preg_replace(array('/^\{\{rss>/','/\}\}$/'),'',$match);
8963db95becSAndreas Gohr
8973db95becSAndreas Gohr        // get params
8983db95becSAndreas Gohr        list($link,$params) = explode(' ',$link,2);
8993db95becSAndreas Gohr
9003db95becSAndreas Gohr        $p = array();
9013db95becSAndreas Gohr        if(preg_match('/\b(\d+)\b/',$params,$match)){
9023db95becSAndreas Gohr            $p['max'] = $match[1];
9033db95becSAndreas Gohr        }else{
9043db95becSAndreas Gohr            $p['max'] = 8;
9053db95becSAndreas Gohr        }
9063db95becSAndreas Gohr        $p['reverse'] = (preg_match('/rev/',$params));
9073db95becSAndreas Gohr        $p['author']  = (preg_match('/\b(by|author)/',$params));
9083db95becSAndreas Gohr        $p['date']    = (preg_match('/\b(date)/',$params));
9093db95becSAndreas Gohr        $p['details'] = (preg_match('/\b(desc|detail)/',$params));
91038c6f603SRobin H. Johnson        $p['nosort']  = (preg_match('/\b(nosort)\b/',$params));
9113db95becSAndreas Gohr
9120a69dff7Schris        if (preg_match('/\b(\d+)([dhm])\b/',$params,$match)) {
9130a69dff7Schris            $period = array('d' => 86400, 'h' => 3600, 'm' => 60);
9140a69dff7Schris            $p['refresh'] = max(600,$match[1]*$period[$match[2]]);  // n * period in seconds, minimum 10 minutes
9150a69dff7Schris        } else {
9160a69dff7Schris            $p['refresh'] = 14400;   // default to 4 hours
9170a69dff7Schris        }
9180a69dff7Schris
919*8b1b81beSAndreas Gohr        $this->addCall('rss', array($link, $p), $pos);
92044881bd0Shenning.noren        return true;
921b625487dSandi    }
922b625487dSandi
923*8b1b81beSAndreas Gohr    /**
924*8b1b81beSAndreas Gohr     * @param string $match matched syntax
925*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
926*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
927*8b1b81beSAndreas Gohr     * @return bool mode handled?
928*8b1b81beSAndreas Gohr     */
929*8b1b81beSAndreas Gohr    public function externallink($match, $state, $pos) {
930da9f31c5SAndreas Gohr        $url   = $match;
931da9f31c5SAndreas Gohr        $title = null;
9320cecf9d5Sandi
933da9f31c5SAndreas Gohr        // add protocol on simple short URLs
934da9f31c5SAndreas Gohr        if(substr($url,0,3) == 'ftp' && (substr($url,0,6) != 'ftp://')){
935da9f31c5SAndreas Gohr            $title = $url;
936da9f31c5SAndreas Gohr            $url   = 'ftp://'.$url;
937da9f31c5SAndreas Gohr        }
938da9f31c5SAndreas Gohr        if(substr($url,0,3) == 'www' && (substr($url,0,7) != 'http://')){
939da9f31c5SAndreas Gohr            $title = $url;
940da9f31c5SAndreas Gohr            $url = 'http://'.$url;
941da9f31c5SAndreas Gohr        }
942da9f31c5SAndreas Gohr
943*8b1b81beSAndreas Gohr        $this->addCall('externallink', array($url, $title), $pos);
94444881bd0Shenning.noren        return true;
9450cecf9d5Sandi    }
9460cecf9d5Sandi
947*8b1b81beSAndreas Gohr    /**
948*8b1b81beSAndreas Gohr     * @param string $match matched syntax
949*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
950*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
951*8b1b81beSAndreas Gohr     * @return bool mode handled?
952*8b1b81beSAndreas Gohr     */
953*8b1b81beSAndreas Gohr    public function emaillink($match, $state, $pos) {
9540cecf9d5Sandi        $email = preg_replace(array('/^</','/>$/'),'',$match);
955*8b1b81beSAndreas Gohr        $this->addCall('emaillink', array($email, null), $pos);
95644881bd0Shenning.noren        return true;
9570cecf9d5Sandi    }
9580cecf9d5Sandi
959*8b1b81beSAndreas Gohr    /**
960*8b1b81beSAndreas Gohr     * @param string $match matched syntax
961*8b1b81beSAndreas Gohr     * @param int $state a LEXER_STATE_* constant
962*8b1b81beSAndreas Gohr     * @param int $pos byte position in the original source file
963*8b1b81beSAndreas Gohr     * @return bool mode handled?
964*8b1b81beSAndreas Gohr     */
965*8b1b81beSAndreas Gohr    public function table($match, $state, $pos) {
9660cecf9d5Sandi        switch ( $state ) {
9670cecf9d5Sandi
9680cecf9d5Sandi            case DOKU_LEXER_ENTER:
9690cecf9d5Sandi
970*8b1b81beSAndreas Gohr                $this->callWriter = new Table($this->callWriter);
9710cecf9d5Sandi
972*8b1b81beSAndreas Gohr                $this->addCall('table_start', array($pos + 1), $pos);
9730cecf9d5Sandi                if ( trim($match) == '^' ) {
974*8b1b81beSAndreas Gohr                    $this->addCall('tableheader', array(), $pos);
9750cecf9d5Sandi                } else {
976*8b1b81beSAndreas Gohr                    $this->addCall('tablecell', array(), $pos);
9770cecf9d5Sandi                }
9780cecf9d5Sandi            break;
9790cecf9d5Sandi
9800cecf9d5Sandi            case DOKU_LEXER_EXIT:
981*8b1b81beSAndreas Gohr                $this->addCall('table_end', array($pos), $pos);
9825c2aad12SAndreas Gohr                /** @var Table $reWriter */
983*8b1b81beSAndreas Gohr                $reWriter = $this->callWriter;
984*8b1b81beSAndreas Gohr                $this->callWriter = $reWriter->process();
9850cecf9d5Sandi            break;
9860cecf9d5Sandi
9870cecf9d5Sandi            case DOKU_LEXER_UNMATCHED:
9880cecf9d5Sandi                if ( trim($match) != '' ) {
989*8b1b81beSAndreas Gohr                    $this->addCall('cdata', array($match), $pos);
9900cecf9d5Sandi                }
9910cecf9d5Sandi            break;
9920cecf9d5Sandi
9930cecf9d5Sandi            case DOKU_LEXER_MATCHED:
9949ab75d9eSAndreas Gohr                if ( $match == ' ' ){
995*8b1b81beSAndreas Gohr                    $this->addCall('cdata', array($match), $pos);
99625b97867Shakan.sandell                } else if ( preg_match('/:::/',$match) ) {
997*8b1b81beSAndreas Gohr                    $this->addCall('rowspan', array($match), $pos);
998e205b721SAndreas Gohr                } else if ( preg_match('/\t+/',$match) ) {
999*8b1b81beSAndreas Gohr                    $this->addCall('table_align', array($match), $pos);
1000e205b721SAndreas Gohr                } else if ( preg_match('/ {2,}/',$match) ) {
1001*8b1b81beSAndreas Gohr                    $this->addCall('table_align', array($match), $pos);
10020cecf9d5Sandi                } else if ( $match == "\n|" ) {
1003*8b1b81beSAndreas Gohr                    $this->addCall('table_row', array(), $pos);
1004*8b1b81beSAndreas Gohr                    $this->addCall('tablecell', array(), $pos);
10050cecf9d5Sandi                } else if ( $match == "\n^" ) {
1006*8b1b81beSAndreas Gohr                    $this->addCall('table_row', array(), $pos);
1007*8b1b81beSAndreas Gohr                    $this->addCall('tableheader', array(), $pos);
10080cecf9d5Sandi                } else if ( $match == '|' ) {
1009*8b1b81beSAndreas Gohr                    $this->addCall('tablecell', array(), $pos);
10100cecf9d5Sandi                } else if ( $match == '^' ) {
1011*8b1b81beSAndreas Gohr                    $this->addCall('tableheader', array(), $pos);
10120cecf9d5Sandi                }
10130cecf9d5Sandi            break;
10140cecf9d5Sandi        }
101544881bd0Shenning.noren        return true;
10160cecf9d5Sandi    }
1017*8b1b81beSAndreas Gohr
1018*8b1b81beSAndreas Gohr    // endregion modes
10190cecf9d5Sandi}
10200cecf9d5Sandi
10210cecf9d5Sandi//------------------------------------------------------------------------
10220cecf9d5Sandifunction Doku_Handler_Parse_Media($match) {
10230cecf9d5Sandi
10240cecf9d5Sandi    // Strip the opening and closing markup
10250cecf9d5Sandi    $link = preg_replace(array('/^\{\{/','/\}\}$/u'),'',$match);
10260cecf9d5Sandi
10270cecf9d5Sandi    // Split title from URL
10284b7f9e70STom N Harris    $link = explode('|',$link,2);
10290cecf9d5Sandi
10300cecf9d5Sandi    // Check alignment
10310cecf9d5Sandi    $ralign = (bool)preg_match('/^ /',$link[0]);
10320cecf9d5Sandi    $lalign = (bool)preg_match('/ $/',$link[0]);
10330cecf9d5Sandi
10340cecf9d5Sandi    // Logic = what's that ;)...
10350cecf9d5Sandi    if ( $lalign & $ralign ) {
10360cecf9d5Sandi        $align = 'center';
10370cecf9d5Sandi    } else if ( $ralign ) {
10380cecf9d5Sandi        $align = 'right';
10390cecf9d5Sandi    } else if ( $lalign ) {
10400cecf9d5Sandi        $align = 'left';
10410cecf9d5Sandi    } else {
10420ea51e63SMatt Perry        $align = null;
10430cecf9d5Sandi    }
10440cecf9d5Sandi
10450cecf9d5Sandi    // The title...
10460cecf9d5Sandi    if ( !isset($link[1]) ) {
10470ea51e63SMatt Perry        $link[1] = null;
10480cecf9d5Sandi    }
10490cecf9d5Sandi
10504826ab45Sandi    //remove aligning spaces
10514826ab45Sandi    $link[0] = trim($link[0]);
10520cecf9d5Sandi
10534826ab45Sandi    //split into src and parameters (using the very last questionmark)
10544826ab45Sandi    $pos = strrpos($link[0], '?');
10554826ab45Sandi    if($pos !== false){
10564826ab45Sandi        $src   = substr($link[0],0,$pos);
10574826ab45Sandi        $param = substr($link[0],$pos+1);
10580cecf9d5Sandi    }else{
10594826ab45Sandi        $src   = $link[0];
10604826ab45Sandi        $param = '';
10610cecf9d5Sandi    }
10620cecf9d5Sandi
10634826ab45Sandi    //parse width and height
10644826ab45Sandi    if(preg_match('#(\d+)(x(\d+))?#i',$param,$size)){
1065443e135dSChristopher Smith        !empty($size[1]) ? $w = $size[1] : $w = null;
1066443e135dSChristopher Smith        !empty($size[3]) ? $h = $size[3] : $h = null;
1067fc1c55b1Shfuecks    } else {
10680ea51e63SMatt Perry        $w = null;
10690ea51e63SMatt Perry        $h = null;
10700cecf9d5Sandi    }
10710cecf9d5Sandi
1072dc673a5bSjoe.lapp    //get linking command
1073d35ab615Shenning.noren    if(preg_match('/nolink/i',$param)){
1074dc673a5bSjoe.lapp        $linking = 'nolink';
1075d35ab615Shenning.noren    }else if(preg_match('/direct/i',$param)){
1076dc673a5bSjoe.lapp        $linking = 'direct';
10778acb3108SAndreas Gohr    }else if(preg_match('/linkonly/i',$param)){
10788acb3108SAndreas Gohr        $linking = 'linkonly';
1079dc673a5bSjoe.lapp    }else{
1080dc673a5bSjoe.lapp        $linking = 'details';
1081dc673a5bSjoe.lapp    }
1082dc673a5bSjoe.lapp
10834826ab45Sandi    //get caching command
10844826ab45Sandi    if (preg_match('/(nocache|recache)/i',$param,$cachemode)){
10854826ab45Sandi        $cache = $cachemode[1];
10860cecf9d5Sandi    }else{
10874826ab45Sandi        $cache = 'cache';
10880cecf9d5Sandi    }
10890cecf9d5Sandi
10906efc45a2SDmitry Katsubo    // Check whether this is a local or remote image or interwiki
10916efc45a2SDmitry Katsubo    if (media_isexternal($src) || link_isinterwiki($src)){
10924826ab45Sandi        $call = 'externalmedia';
10930cecf9d5Sandi    } else {
10944826ab45Sandi        $call = 'internalmedia';
10950cecf9d5Sandi    }
10960cecf9d5Sandi
10970cecf9d5Sandi    $params = array(
10980cecf9d5Sandi        'type'=>$call,
10994826ab45Sandi        'src'=>$src,
11000cecf9d5Sandi        'title'=>$link[1],
11010cecf9d5Sandi        'align'=>$align,
11024826ab45Sandi        'width'=>$w,
11034826ab45Sandi        'height'=>$h,
11040cecf9d5Sandi        'cache'=>$cache,
1105dc673a5bSjoe.lapp        'linking'=>$linking,
11060cecf9d5Sandi    );
11070cecf9d5Sandi
11080cecf9d5Sandi    return $params;
11090cecf9d5Sandi}
11100cecf9d5Sandi
1111