xref: /dokuwiki/inc/parser/parser.php (revision 0cecf9d507451346a32ddf45a85b425784fbb0f8)
1*0cecf9d5Sandi<?php
2*0cecf9d5Sandi
3*0cecf9d5Sandiif(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
4*0cecf9d5Sandi
5*0cecf9d5Sandirequire_once DOKU_INC . 'inc/parser/lexer.php';
6*0cecf9d5Sandirequire_once DOKU_INC . 'inc/parser/handler.php';
7*0cecf9d5Sandi
8*0cecf9d5Sandi//-------------------------------------------------------------------
9*0cecf9d5Sandi
10*0cecf9d5Sandi/**
11*0cecf9d5Sandi* Sets up the Lexer with modes and points it to the Handler
12*0cecf9d5Sandi* For an intro to the Lexer see: wiki:parser
13*0cecf9d5Sandi*/
14*0cecf9d5Sandiclass Doku_Parser {
15*0cecf9d5Sandi
16*0cecf9d5Sandi    var $Handler;
17*0cecf9d5Sandi
18*0cecf9d5Sandi    var $Lexer;
19*0cecf9d5Sandi
20*0cecf9d5Sandi    var $modes = array();
21*0cecf9d5Sandi
22*0cecf9d5Sandi    var $connected = FALSE;
23*0cecf9d5Sandi
24*0cecf9d5Sandi    function addBaseMode() {
25*0cecf9d5Sandi        $this->modes['base'] = & new Doku_Parser_Mode_Base();
26*0cecf9d5Sandi        if ( !$this->Lexer ) {
27*0cecf9d5Sandi            $this->Lexer = & new Doku_Lexer($this->Handler,'base', TRUE);
28*0cecf9d5Sandi        }
29*0cecf9d5Sandi        $this->modes['base']->Lexer = & $this->Lexer;
30*0cecf9d5Sandi    }
31*0cecf9d5Sandi
32*0cecf9d5Sandi    /**
33*0cecf9d5Sandi    * PHP preserves order of associative elements
34*0cecf9d5Sandi    * Mode sequence is important
35*0cecf9d5Sandi    */
36*0cecf9d5Sandi    function addMode($name, & $Mode) {
37*0cecf9d5Sandi        if ( !isset($this->modes['base']) ) {
38*0cecf9d5Sandi            $this->addBaseMode();
39*0cecf9d5Sandi        }
40*0cecf9d5Sandi        $Mode->Lexer = & $this->Lexer;
41*0cecf9d5Sandi        $this->modes[$name] = & $Mode;
42*0cecf9d5Sandi    }
43*0cecf9d5Sandi
44*0cecf9d5Sandi    function connectModes() {
45*0cecf9d5Sandi
46*0cecf9d5Sandi        if ( $this->connected ) {
47*0cecf9d5Sandi            return;
48*0cecf9d5Sandi        }
49*0cecf9d5Sandi
50*0cecf9d5Sandi        foreach ( array_keys($this->modes) as $mode ) {
51*0cecf9d5Sandi
52*0cecf9d5Sandi            // Base isn't connected to anything
53*0cecf9d5Sandi            if ( $mode == 'base' ) {
54*0cecf9d5Sandi                continue;
55*0cecf9d5Sandi            }
56*0cecf9d5Sandi
57*0cecf9d5Sandi            $this->modes[$mode]->preConnect();
58*0cecf9d5Sandi
59*0cecf9d5Sandi            foreach ( array_keys($this->modes) as $cm ) {
60*0cecf9d5Sandi
61*0cecf9d5Sandi                if ( $this->modes[$cm]->accepts($mode) ) {
62*0cecf9d5Sandi                    $this->modes[$mode]->connectTo($cm);
63*0cecf9d5Sandi                }
64*0cecf9d5Sandi
65*0cecf9d5Sandi            }
66*0cecf9d5Sandi
67*0cecf9d5Sandi            $this->modes[$mode]->postConnect();
68*0cecf9d5Sandi        }
69*0cecf9d5Sandi
70*0cecf9d5Sandi        $this->connected = TRUE;
71*0cecf9d5Sandi    }
72*0cecf9d5Sandi
73*0cecf9d5Sandi    function parse($doc) {
74*0cecf9d5Sandi        if ( $this->Lexer ) {
75*0cecf9d5Sandi            $this->connectModes();
76*0cecf9d5Sandi            // Normalize CRs and pad doc
77*0cecf9d5Sandi            $doc = "\n".str_replace("\r\n","\n",$doc)."\n";
78*0cecf9d5Sandi            $this->Lexer->parse($doc);
79*0cecf9d5Sandi            $this->Handler->__finalize();
80*0cecf9d5Sandi            return $this->Handler->calls;
81*0cecf9d5Sandi        } else {
82*0cecf9d5Sandi            return FALSE;
83*0cecf9d5Sandi        }
84*0cecf9d5Sandi    }
85*0cecf9d5Sandi
86*0cecf9d5Sandi}
87*0cecf9d5Sandi
88*0cecf9d5Sandi//-------------------------------------------------------------------
89*0cecf9d5Sandi/**
90*0cecf9d5Sandi* This class and all the subclasses below are
91*0cecf9d5Sandi* used to reduce the effort required to register
92*0cecf9d5Sandi* modes with the Lexer. For performance these
93*0cecf9d5Sandi* could all be eliminated later perhaps, or
94*0cecf9d5Sandi* the Parser could be serialized to a file once
95*0cecf9d5Sandi* all modes are registered
96*0cecf9d5Sandi*/
97*0cecf9d5Sandiclass Doku_Parser_Mode {
98*0cecf9d5Sandi
99*0cecf9d5Sandi    var $Lexer;
100*0cecf9d5Sandi
101*0cecf9d5Sandi    var $allowedModes = array();
102*0cecf9d5Sandi
103*0cecf9d5Sandi    // Called before any calls to connectTo
104*0cecf9d5Sandi    function preConnect() {}
105*0cecf9d5Sandi
106*0cecf9d5Sandi    function connectTo($mode) {}
107*0cecf9d5Sandi
108*0cecf9d5Sandi    // Called after all calls to connectTo
109*0cecf9d5Sandi    function postConnect() {}
110*0cecf9d5Sandi
111*0cecf9d5Sandi    function accepts($mode) {
112*0cecf9d5Sandi        return in_array($mode, $this->allowedModes );
113*0cecf9d5Sandi    }
114*0cecf9d5Sandi
115*0cecf9d5Sandi}
116*0cecf9d5Sandi
117*0cecf9d5Sandi//-------------------------------------------------------------------
118*0cecf9d5Sandiclass Doku_Parser_Mode_Base extends Doku_Parser_Mode {
119*0cecf9d5Sandi
120*0cecf9d5Sandi    function Doku_Parser_Mode_Base() {
121*0cecf9d5Sandi
122*0cecf9d5Sandi        $this->allowedModes = array_merge (
123*0cecf9d5Sandi                Doku_Parser_BlockContainers(),
124*0cecf9d5Sandi                Doku_Parser_BaseOnly(),
125*0cecf9d5Sandi                Doku_Parser_Paragraphs(),
126*0cecf9d5Sandi                Doku_Parser_Formatting(),
127*0cecf9d5Sandi                Doku_Parser_Substition(),
128*0cecf9d5Sandi                Doku_Parser_Protected(),
129*0cecf9d5Sandi                Doku_Parser_Disabled()
130*0cecf9d5Sandi            );
131*0cecf9d5Sandi    }
132*0cecf9d5Sandi}
133*0cecf9d5Sandi
134*0cecf9d5Sandi//-------------------------------------------------------------------
135*0cecf9d5Sandiclass Doku_Parser_Mode_Footnote extends Doku_Parser_Mode {
136*0cecf9d5Sandi
137*0cecf9d5Sandi    function Doku_Parser_Mode_Footnote() {
138*0cecf9d5Sandi
139*0cecf9d5Sandi        $this->allowedModes = array_merge (
140*0cecf9d5Sandi                Doku_Parser_BlockContainers(),
141*0cecf9d5Sandi                Doku_Parser_Formatting(),
142*0cecf9d5Sandi                Doku_Parser_Substition(),
143*0cecf9d5Sandi                Doku_Parser_Protected(),
144*0cecf9d5Sandi                Doku_Parser_Disabled()
145*0cecf9d5Sandi            );
146*0cecf9d5Sandi
147*0cecf9d5Sandi    }
148*0cecf9d5Sandi
149*0cecf9d5Sandi    function connectTo($mode) {
150*0cecf9d5Sandi        $this->Lexer->addEntryPattern(
151*0cecf9d5Sandi            '\x28\x28(?=.*\x29\x29)',$mode,'footnote'
152*0cecf9d5Sandi            );
153*0cecf9d5Sandi    }
154*0cecf9d5Sandi
155*0cecf9d5Sandi    function postConnect() {
156*0cecf9d5Sandi        $this->Lexer->addExitPattern(
157*0cecf9d5Sandi            '\x29\x29','footnote'
158*0cecf9d5Sandi            );
159*0cecf9d5Sandi
160*0cecf9d5Sandi    }
161*0cecf9d5Sandi
162*0cecf9d5Sandi}
163*0cecf9d5Sandi
164*0cecf9d5Sandi//-------------------------------------------------------------------
165*0cecf9d5Sandiclass Doku_Parser_Mode_Header extends Doku_Parser_Mode {
166*0cecf9d5Sandi
167*0cecf9d5Sandi    function preConnect() {
168*0cecf9d5Sandi
169*0cecf9d5Sandi        // Header 1 is special case - match 6 or more
170*0cecf9d5Sandi        $this->Lexer->addSpecialPattern(
171*0cecf9d5Sandi                            '[ \t]*={6,}[^\n]+={6,}[ \t]*\n',
172*0cecf9d5Sandi                            'base',
173*0cecf9d5Sandi                            'header'
174*0cecf9d5Sandi                        );
175*0cecf9d5Sandi
176*0cecf9d5Sandi        // For the rest, match exactly
177*0cecf9d5Sandi        for ( $i = 5; $i > 1; $i--) {
178*0cecf9d5Sandi            $this->Lexer->addSpecialPattern(
179*0cecf9d5Sandi                                '[ \t]*={'.$i.'}[^\n]+={'.$i.'}[ \t]*\n',
180*0cecf9d5Sandi                                'base',
181*0cecf9d5Sandi                                'header'
182*0cecf9d5Sandi                            );
183*0cecf9d5Sandi        }
184*0cecf9d5Sandi    }
185*0cecf9d5Sandi
186*0cecf9d5Sandi}
187*0cecf9d5Sandi
188*0cecf9d5Sandi//-------------------------------------------------------------------
189*0cecf9d5Sandiclass Doku_Parser_Mode_NoToc extends Doku_Parser_Mode {
190*0cecf9d5Sandi
191*0cecf9d5Sandi    function connectTo($mode) {
192*0cecf9d5Sandi        $this->Lexer->addSpecialPattern('~~NOTOC~~',$mode,'notoc');
193*0cecf9d5Sandi    }
194*0cecf9d5Sandi
195*0cecf9d5Sandi}
196*0cecf9d5Sandi
197*0cecf9d5Sandi//-------------------------------------------------------------------
198*0cecf9d5Sandiclass Doku_Parser_Mode_Linebreak extends Doku_Parser_Mode {
199*0cecf9d5Sandi
200*0cecf9d5Sandi    function connectTo($mode) {
201*0cecf9d5Sandi        $this->Lexer->addSpecialPattern('\x5C{2}\s',$mode,'linebreak');
202*0cecf9d5Sandi    }
203*0cecf9d5Sandi}
204*0cecf9d5Sandi
205*0cecf9d5Sandi//-------------------------------------------------------------------
206*0cecf9d5Sandiclass Doku_Parser_Mode_Eol extends Doku_Parser_Mode {
207*0cecf9d5Sandi
208*0cecf9d5Sandi    function connectTo($mode) {
209*0cecf9d5Sandi        $badModes = array('listblock','table');
210*0cecf9d5Sandi        if ( in_array($mode, $badModes) ) {
211*0cecf9d5Sandi            return;
212*0cecf9d5Sandi        }
213*0cecf9d5Sandi        $this->Lexer->addSpecialPattern('\n',$mode,'eol');
214*0cecf9d5Sandi    }
215*0cecf9d5Sandi}
216*0cecf9d5Sandi
217*0cecf9d5Sandi//-------------------------------------------------------------------
218*0cecf9d5Sandiclass Doku_Parser_Mode_HR extends Doku_Parser_Mode {
219*0cecf9d5Sandi
220*0cecf9d5Sandi    function connectTo($mode) {
221*0cecf9d5Sandi        $this->Lexer->addSpecialPattern('\n[ \t]*-{4,}[ \t]*\n',$mode,'hr');
222*0cecf9d5Sandi    }
223*0cecf9d5Sandi
224*0cecf9d5Sandi}
225*0cecf9d5Sandi
226*0cecf9d5Sandi//-------------------------------------------------------------------
227*0cecf9d5Sandiclass Doku_Parser_Mode_Formatting extends Doku_Parser_Mode {
228*0cecf9d5Sandi
229*0cecf9d5Sandi    var $type;
230*0cecf9d5Sandi
231*0cecf9d5Sandi    var $formatting = array (
232*0cecf9d5Sandi        'strong' => array (
233*0cecf9d5Sandi            'entry'=>'\*\*(?=.*\*\*)',
234*0cecf9d5Sandi            'exit'=>'\*\*',
235*0cecf9d5Sandi            ),
236*0cecf9d5Sandi
237*0cecf9d5Sandi        'emphasis'=> array (
238*0cecf9d5Sandi            'entry'=>'//(?=.*//)',
239*0cecf9d5Sandi            'exit'=>'//',
240*0cecf9d5Sandi            ),
241*0cecf9d5Sandi
242*0cecf9d5Sandi        'underline'=> array (
243*0cecf9d5Sandi            'entry'=>'__(?=.*__)',
244*0cecf9d5Sandi            'exit'=>'__',
245*0cecf9d5Sandi            ),
246*0cecf9d5Sandi
247*0cecf9d5Sandi        'monospace'=> array (
248*0cecf9d5Sandi            'entry'=>'\x27\x27(?=.*\x27\x27)',
249*0cecf9d5Sandi            'exit'=>'\x27\x27',
250*0cecf9d5Sandi            ),
251*0cecf9d5Sandi
252*0cecf9d5Sandi        'subscript'=> array (
253*0cecf9d5Sandi            'entry'=>'<sub>(?=.*\x3C/sub\x3E)',
254*0cecf9d5Sandi            'exit'=>'</sub>',
255*0cecf9d5Sandi            ),
256*0cecf9d5Sandi
257*0cecf9d5Sandi        'superscript'=> array (
258*0cecf9d5Sandi            'entry'=>'<sup>(?=.*\x3C/sup\x3E)',
259*0cecf9d5Sandi            'exit'=>'</sup>',
260*0cecf9d5Sandi            ),
261*0cecf9d5Sandi
262*0cecf9d5Sandi        'deleted'=> array (
263*0cecf9d5Sandi            'entry'=>'<del>(?=.*\x3C/del\x3E)',
264*0cecf9d5Sandi            'exit'=>'</del>',
265*0cecf9d5Sandi            ),
266*0cecf9d5Sandi        );
267*0cecf9d5Sandi
268*0cecf9d5Sandi    function Doku_Parser_Mode_Formatting($type) {
269*0cecf9d5Sandi
270*0cecf9d5Sandi        if ( !array_key_exists($type, $this->formatting) ) {
271*0cecf9d5Sandi            trigger_error('Invalid formatting type '.$type, E_USER_WARNING);
272*0cecf9d5Sandi        }
273*0cecf9d5Sandi
274*0cecf9d5Sandi        $this->type = $type;
275*0cecf9d5Sandi
276*0cecf9d5Sandi        $this->allowedModes = array_merge (
277*0cecf9d5Sandi                Doku_Parser_Formatting($type),
278*0cecf9d5Sandi                Doku_Parser_Substition(),
279*0cecf9d5Sandi                Doku_Parser_Disabled()
280*0cecf9d5Sandi            );
281*0cecf9d5Sandi
282*0cecf9d5Sandi    }
283*0cecf9d5Sandi
284*0cecf9d5Sandi    function connectTo($mode) {
285*0cecf9d5Sandi
286*0cecf9d5Sandi        // Can't nest formatting in itself
287*0cecf9d5Sandi        if ( $mode == $this->type ) {
288*0cecf9d5Sandi            return;
289*0cecf9d5Sandi        }
290*0cecf9d5Sandi
291*0cecf9d5Sandi        $this->Lexer->addEntryPattern(
292*0cecf9d5Sandi                $this->formatting[$this->type]['entry'],
293*0cecf9d5Sandi                $mode,
294*0cecf9d5Sandi                $this->type
295*0cecf9d5Sandi            );
296*0cecf9d5Sandi    }
297*0cecf9d5Sandi
298*0cecf9d5Sandi    function postConnect() {
299*0cecf9d5Sandi
300*0cecf9d5Sandi        $this->Lexer->addExitPattern(
301*0cecf9d5Sandi            $this->formatting[$this->type]['exit'],
302*0cecf9d5Sandi            $this->type
303*0cecf9d5Sandi            );
304*0cecf9d5Sandi
305*0cecf9d5Sandi    }
306*0cecf9d5Sandi}
307*0cecf9d5Sandi
308*0cecf9d5Sandi//-------------------------------------------------------------------
309*0cecf9d5Sandiclass Doku_Parser_Mode_ListBlock extends Doku_Parser_Mode {
310*0cecf9d5Sandi
311*0cecf9d5Sandi    function Doku_Parser_Mode_ListBlock() {
312*0cecf9d5Sandi
313*0cecf9d5Sandi        $this->allowedModes = array_merge (
314*0cecf9d5Sandi                Doku_Parser_Formatting(),
315*0cecf9d5Sandi                Doku_Parser_Substition(),
316*0cecf9d5Sandi                Doku_Parser_Disabled()
317*0cecf9d5Sandi            );
318*0cecf9d5Sandi        $this->allowedModes[] = 'footnote';
319*0cecf9d5Sandi        $this->allowedModes[] = 'preformatted';
320*0cecf9d5Sandi        $this->allowedModes[] = 'unformatted';
321*0cecf9d5Sandi
322*0cecf9d5Sandi    }
323*0cecf9d5Sandi
324*0cecf9d5Sandi    function connectTo($mode) {
325*0cecf9d5Sandi        $this->Lexer->addEntryPattern('\n {2,}[\-\*]',$mode,'listblock');
326*0cecf9d5Sandi        $this->Lexer->addEntryPattern('\n\t{1,}[\-\*]',$mode,'listblock');
327*0cecf9d5Sandi
328*0cecf9d5Sandi        $this->Lexer->addPattern('\n {2,}[\-\*]','listblock');
329*0cecf9d5Sandi        $this->Lexer->addPattern('\n\t{1,}[\-\*]','listblock');
330*0cecf9d5Sandi
331*0cecf9d5Sandi    }
332*0cecf9d5Sandi
333*0cecf9d5Sandi    function postConnect() {
334*0cecf9d5Sandi        $this->Lexer->addExitPattern('\n','listblock');
335*0cecf9d5Sandi    }
336*0cecf9d5Sandi}
337*0cecf9d5Sandi
338*0cecf9d5Sandi//-------------------------------------------------------------------
339*0cecf9d5Sandiclass Doku_Parser_Mode_Table extends Doku_Parser_Mode {
340*0cecf9d5Sandi
341*0cecf9d5Sandi    function Doku_Parser_Mode_Table() {
342*0cecf9d5Sandi
343*0cecf9d5Sandi        $this->allowedModes = array_merge (
344*0cecf9d5Sandi                Doku_Parser_Formatting(),
345*0cecf9d5Sandi                Doku_Parser_Substition(),
346*0cecf9d5Sandi                Doku_Parser_Disabled()
347*0cecf9d5Sandi            );
348*0cecf9d5Sandi        $this->allowedModes[] = 'footnote';
349*0cecf9d5Sandi        $this->allowedModes[] = 'preformatted';
350*0cecf9d5Sandi        $this->allowedModes[] = 'unformatted';
351*0cecf9d5Sandi    }
352*0cecf9d5Sandi
353*0cecf9d5Sandi    function connectTo($mode) {
354*0cecf9d5Sandi        $this->Lexer->addEntryPattern('\n\^',$mode,'table');
355*0cecf9d5Sandi        $this->Lexer->addEntryPattern('\n\|',$mode,'table');
356*0cecf9d5Sandi    }
357*0cecf9d5Sandi
358*0cecf9d5Sandi    function postConnect() {
359*0cecf9d5Sandi        $this->Lexer->addPattern('\n\^','table');
360*0cecf9d5Sandi        $this->Lexer->addPattern('\n\|','table');
361*0cecf9d5Sandi        $this->Lexer->addPattern(' {2,}','table');
362*0cecf9d5Sandi        $this->Lexer->addPattern('\^','table');
363*0cecf9d5Sandi        $this->Lexer->addPattern('\|','table');
364*0cecf9d5Sandi        $this->Lexer->addExitPattern('\n','table');
365*0cecf9d5Sandi    }
366*0cecf9d5Sandi}
367*0cecf9d5Sandi
368*0cecf9d5Sandi//-------------------------------------------------------------------
369*0cecf9d5Sandiclass Doku_Parser_Mode_Unformatted extends Doku_Parser_Mode {
370*0cecf9d5Sandi
371*0cecf9d5Sandi    function connectTo($mode) {
372*0cecf9d5Sandi        $this->Lexer->addEntryPattern('<nowiki>(?=.*\x3C/nowiki\x3E)',$mode,'unformatted');
373*0cecf9d5Sandi        $this->Lexer->addEntryPattern('%%(?=.*%%)',$mode,'unformattedalt');
374*0cecf9d5Sandi    }
375*0cecf9d5Sandi
376*0cecf9d5Sandi    function postConnect() {
377*0cecf9d5Sandi        $this->Lexer->addExitPattern('</nowiki>','unformatted');
378*0cecf9d5Sandi        $this->Lexer->addExitPattern('%%','unformattedalt');
379*0cecf9d5Sandi        $this->Lexer->mapHandler('unformattedalt','unformatted');
380*0cecf9d5Sandi    }
381*0cecf9d5Sandi
382*0cecf9d5Sandi}
383*0cecf9d5Sandi
384*0cecf9d5Sandi//-------------------------------------------------------------------
385*0cecf9d5Sandiclass Doku_Parser_Mode_PHP extends Doku_Parser_Mode {
386*0cecf9d5Sandi
387*0cecf9d5Sandi    function connectTo($mode) {
388*0cecf9d5Sandi        $this->Lexer->addEntryPattern('<php>(?=.*\x3C/php\x3E)',$mode,'php');
389*0cecf9d5Sandi    }
390*0cecf9d5Sandi
391*0cecf9d5Sandi    function postConnect() {
392*0cecf9d5Sandi        $this->Lexer->addExitPattern('</php>','php');
393*0cecf9d5Sandi    }
394*0cecf9d5Sandi
395*0cecf9d5Sandi}
396*0cecf9d5Sandi
397*0cecf9d5Sandi//-------------------------------------------------------------------
398*0cecf9d5Sandiclass Doku_Parser_Mode_HTML extends Doku_Parser_Mode {
399*0cecf9d5Sandi
400*0cecf9d5Sandi    function connectTo($mode) {
401*0cecf9d5Sandi        $this->Lexer->addEntryPattern('<html>(?=.*\x3C/html\x3E)',$mode,'html');
402*0cecf9d5Sandi    }
403*0cecf9d5Sandi
404*0cecf9d5Sandi    function postConnect() {
405*0cecf9d5Sandi        $this->Lexer->addExitPattern('</html>','html');
406*0cecf9d5Sandi    }
407*0cecf9d5Sandi
408*0cecf9d5Sandi}
409*0cecf9d5Sandi
410*0cecf9d5Sandi//-------------------------------------------------------------------
411*0cecf9d5Sandiclass Doku_Parser_Mode_Preformatted extends Doku_Parser_Mode {
412*0cecf9d5Sandi
413*0cecf9d5Sandi    function connectTo($mode) {
414*0cecf9d5Sandi        // Has hard coded awareness of lists...
415*0cecf9d5Sandi        $this->Lexer->addEntryPattern('\n  (?![\*\-])',$mode,'preformatted');
416*0cecf9d5Sandi        $this->Lexer->addEntryPattern('\n\t(?![\*\-])',$mode,'preformatted');
417*0cecf9d5Sandi
418*0cecf9d5Sandi        // How to effect a sub pattern with the Lexer!
419*0cecf9d5Sandi        $this->Lexer->addPattern('\n  ','preformatted');
420*0cecf9d5Sandi        $this->Lexer->addPattern('\n\t','preformatted');
421*0cecf9d5Sandi
422*0cecf9d5Sandi    }
423*0cecf9d5Sandi
424*0cecf9d5Sandi    function postConnect() {
425*0cecf9d5Sandi        $this->Lexer->addExitPattern('\n','preformatted');
426*0cecf9d5Sandi    }
427*0cecf9d5Sandi
428*0cecf9d5Sandi}
429*0cecf9d5Sandi
430*0cecf9d5Sandi//-------------------------------------------------------------------
431*0cecf9d5Sandiclass Doku_Parser_Mode_Code extends Doku_Parser_Mode {
432*0cecf9d5Sandi
433*0cecf9d5Sandi    function connectTo($mode) {
434*0cecf9d5Sandi        $this->Lexer->addEntryPattern('<code(?=.*\x3C/code\x3E)',$mode,'code');
435*0cecf9d5Sandi    }
436*0cecf9d5Sandi
437*0cecf9d5Sandi    function postConnect() {
438*0cecf9d5Sandi        $this->Lexer->addExitPattern('</code>','code');
439*0cecf9d5Sandi    }
440*0cecf9d5Sandi
441*0cecf9d5Sandi}
442*0cecf9d5Sandi
443*0cecf9d5Sandi//-------------------------------------------------------------------
444*0cecf9d5Sandiclass Doku_Parser_Mode_File extends Doku_Parser_Mode {
445*0cecf9d5Sandi
446*0cecf9d5Sandi    function connectTo($mode) {
447*0cecf9d5Sandi        $this->Lexer->addEntryPattern('<file>(?=.*\x3C/file\x3E)',$mode,'file');
448*0cecf9d5Sandi    }
449*0cecf9d5Sandi
450*0cecf9d5Sandi    function postConnect() {
451*0cecf9d5Sandi        $this->Lexer->addExitPattern('</file>','file');
452*0cecf9d5Sandi    }
453*0cecf9d5Sandi
454*0cecf9d5Sandi}
455*0cecf9d5Sandi
456*0cecf9d5Sandi//-------------------------------------------------------------------
457*0cecf9d5Sandiclass Doku_Parser_Mode_Quote extends Doku_Parser_Mode {
458*0cecf9d5Sandi
459*0cecf9d5Sandi    function Doku_Parser_Mode_Quote() {
460*0cecf9d5Sandi
461*0cecf9d5Sandi        $this->allowedModes = array_merge (
462*0cecf9d5Sandi                Doku_Parser_Formatting(),
463*0cecf9d5Sandi                Doku_Parser_Substition(),
464*0cecf9d5Sandi                Doku_Parser_Disabled()
465*0cecf9d5Sandi            );
466*0cecf9d5Sandi            $this->allowedModes[] = 'footnote';
467*0cecf9d5Sandi            $this->allowedModes[] = 'preformatted';
468*0cecf9d5Sandi            $this->allowedModes[] = 'unformatted';
469*0cecf9d5Sandi    }
470*0cecf9d5Sandi
471*0cecf9d5Sandi    function connectTo($mode) {
472*0cecf9d5Sandi        $this->Lexer->addEntryPattern('\n>{1,}',$mode,'quote');
473*0cecf9d5Sandi    }
474*0cecf9d5Sandi
475*0cecf9d5Sandi    function postConnect() {
476*0cecf9d5Sandi        $this->Lexer->addPattern('\n>{1,}','quote');
477*0cecf9d5Sandi        $this->Lexer->addExitPattern('\n','quote');
478*0cecf9d5Sandi    }
479*0cecf9d5Sandi
480*0cecf9d5Sandi}
481*0cecf9d5Sandi
482*0cecf9d5Sandi//-------------------------------------------------------------------
483*0cecf9d5Sandiclass Doku_Parser_Mode_Acronym extends Doku_Parser_Mode {
484*0cecf9d5Sandi    // A list
485*0cecf9d5Sandi    var $acronyms = array();
486*0cecf9d5Sandi    var $pattern = '';
487*0cecf9d5Sandi
488*0cecf9d5Sandi    function Doku_Parser_Mode_Acronym($acronyms) {
489*0cecf9d5Sandi        $this->acronyms = $acronyms;
490*0cecf9d5Sandi    }
491*0cecf9d5Sandi
492*0cecf9d5Sandi    function preConnect() {
493*0cecf9d5Sandi        $sep = '';
494*0cecf9d5Sandi        foreach ( $this->acronyms as $acronym ) {
495*0cecf9d5Sandi            $this->pattern .= $sep.'(?<=\b)'.Doku_Lexer_Escape($acronym).'(?=\b)';
496*0cecf9d5Sandi            $sep = '|';
497*0cecf9d5Sandi        }
498*0cecf9d5Sandi    }
499*0cecf9d5Sandi
500*0cecf9d5Sandi    function connectTo($mode) {
501*0cecf9d5Sandi        if ( strlen($this->pattern) > 0 ) {
502*0cecf9d5Sandi            $this->Lexer->addSpecialPattern($this->pattern,$mode,'acronym');
503*0cecf9d5Sandi        }
504*0cecf9d5Sandi    }
505*0cecf9d5Sandi
506*0cecf9d5Sandi}
507*0cecf9d5Sandi
508*0cecf9d5Sandi//-------------------------------------------------------------------
509*0cecf9d5Sandiclass Doku_Parser_Mode_Smiley extends Doku_Parser_Mode {
510*0cecf9d5Sandi    // A list
511*0cecf9d5Sandi    var $smileys = array();
512*0cecf9d5Sandi    var $pattern = '';
513*0cecf9d5Sandi
514*0cecf9d5Sandi    function Doku_Parser_Mode_Smiley($smileys) {
515*0cecf9d5Sandi        $this->smileys = $smileys;
516*0cecf9d5Sandi    }
517*0cecf9d5Sandi
518*0cecf9d5Sandi    function preConnect() {
519*0cecf9d5Sandi        $sep = '';
520*0cecf9d5Sandi        foreach ( $this->smileys as $smiley ) {
521*0cecf9d5Sandi            $this->pattern .= $sep.Doku_Lexer_Escape($smiley);
522*0cecf9d5Sandi            $sep = '|';
523*0cecf9d5Sandi        }
524*0cecf9d5Sandi    }
525*0cecf9d5Sandi
526*0cecf9d5Sandi    function connectTo($mode) {
527*0cecf9d5Sandi        if ( strlen($this->pattern) > 0 ) {
528*0cecf9d5Sandi            $this->Lexer->addSpecialPattern($this->pattern,$mode,'smiley');
529*0cecf9d5Sandi        }
530*0cecf9d5Sandi    }
531*0cecf9d5Sandi
532*0cecf9d5Sandi}
533*0cecf9d5Sandi
534*0cecf9d5Sandi//-------------------------------------------------------------------
535*0cecf9d5Sandiclass Doku_Parser_Mode_Wordblock extends Doku_Parser_Mode {
536*0cecf9d5Sandi    // A list
537*0cecf9d5Sandi    var $badwords = array();
538*0cecf9d5Sandi    var $pattern = '';
539*0cecf9d5Sandi
540*0cecf9d5Sandi    function Doku_Parser_Mode_Wordblock($badwords) {
541*0cecf9d5Sandi        $this->badwords = $badwords;
542*0cecf9d5Sandi    }
543*0cecf9d5Sandi
544*0cecf9d5Sandi    function preConnect() {
545*0cecf9d5Sandi
546*0cecf9d5Sandi        if ( count($this->badwords) == 0 ) {
547*0cecf9d5Sandi            return;
548*0cecf9d5Sandi        }
549*0cecf9d5Sandi
550*0cecf9d5Sandi        $sep = '';
551*0cecf9d5Sandi        foreach ( $this->badwords as $badword ) {
552*0cecf9d5Sandi            $this->pattern .= $sep.'(?<=\b)(?i)'.Doku_Lexer_Escape($badword).'(?-i)(?=\b)';
553*0cecf9d5Sandi            $sep = '|';
554*0cecf9d5Sandi        }
555*0cecf9d5Sandi
556*0cecf9d5Sandi    }
557*0cecf9d5Sandi
558*0cecf9d5Sandi    function connectTo($mode) {
559*0cecf9d5Sandi        if ( strlen($this->pattern) > 0 ) {
560*0cecf9d5Sandi            $this->Lexer->addSpecialPattern($this->pattern,$mode,'wordblock');
561*0cecf9d5Sandi        }
562*0cecf9d5Sandi    }
563*0cecf9d5Sandi
564*0cecf9d5Sandi}
565*0cecf9d5Sandi
566*0cecf9d5Sandi//-------------------------------------------------------------------
567*0cecf9d5Sandi/**
568*0cecf9d5Sandi* @TODO Quotes and 640x480 are note supported - just straight replacements here
569*0cecf9d5Sandi*/
570*0cecf9d5Sandiclass Doku_Parser_Mode_Entity extends Doku_Parser_Mode {
571*0cecf9d5Sandi    // A list
572*0cecf9d5Sandi    var $entities = array();
573*0cecf9d5Sandi    var $pattern = '';
574*0cecf9d5Sandi
575*0cecf9d5Sandi    function Doku_Parser_Mode_Entity($entities) {
576*0cecf9d5Sandi        $this->entities = $entities;
577*0cecf9d5Sandi    }
578*0cecf9d5Sandi
579*0cecf9d5Sandi    function preConnect() {
580*0cecf9d5Sandi        $sep = '';
581*0cecf9d5Sandi        foreach ( $this->entities as $entity ) {
582*0cecf9d5Sandi            $this->pattern .= $sep.Doku_Lexer_Escape($entity);
583*0cecf9d5Sandi            $sep = '|';
584*0cecf9d5Sandi        }
585*0cecf9d5Sandi    }
586*0cecf9d5Sandi
587*0cecf9d5Sandi    function connectTo($mode) {
588*0cecf9d5Sandi        if ( strlen($this->pattern) > 0 ) {
589*0cecf9d5Sandi            $this->Lexer->addSpecialPattern($this->pattern,$mode,'entity');
590*0cecf9d5Sandi        }
591*0cecf9d5Sandi    }
592*0cecf9d5Sandi
593*0cecf9d5Sandi}
594*0cecf9d5Sandi
595*0cecf9d5Sandi//-------------------------------------------------------------------
596*0cecf9d5Sandi// Implements the 640x480 replacement
597*0cecf9d5Sandiclass Doku_Parser_Mode_MultiplyEntity extends Doku_Parser_Mode {
598*0cecf9d5Sandi
599*0cecf9d5Sandi    function connectTo($mode) {
600*0cecf9d5Sandi
601*0cecf9d5Sandi        $this->Lexer->addSpecialPattern(
602*0cecf9d5Sandi                    '(?<=\b)\d+[x|X]\d+(?=\b)',$mode,'multiplyentity'
603*0cecf9d5Sandi                );
604*0cecf9d5Sandi
605*0cecf9d5Sandi    }
606*0cecf9d5Sandi
607*0cecf9d5Sandi}
608*0cecf9d5Sandi
609*0cecf9d5Sandi//-------------------------------------------------------------------
610*0cecf9d5Sandiclass Doku_Parser_Mode_Quotes extends Doku_Parser_Mode {
611*0cecf9d5Sandi
612*0cecf9d5Sandi    function connectTo($mode) {
613*0cecf9d5Sandi
614*0cecf9d5Sandi        $this->Lexer->addSpecialPattern(
615*0cecf9d5Sandi                    '(?<=\s)\'(?=\S)',$mode,'singlequoteopening'
616*0cecf9d5Sandi                );
617*0cecf9d5Sandi        $this->Lexer->addSpecialPattern(
618*0cecf9d5Sandi                    '(?<=\S)\'',$mode,'singlequoteclosing'
619*0cecf9d5Sandi                );
620*0cecf9d5Sandi        $this->Lexer->addSpecialPattern(
621*0cecf9d5Sandi                    '(?<=\s)"(?=\S)',$mode,'doublequoteopening'
622*0cecf9d5Sandi                );
623*0cecf9d5Sandi        $this->Lexer->addSpecialPattern(
624*0cecf9d5Sandi                    '(?<=\S)"',$mode,'doublequoteclosing'
625*0cecf9d5Sandi                );
626*0cecf9d5Sandi
627*0cecf9d5Sandi    }
628*0cecf9d5Sandi
629*0cecf9d5Sandi}
630*0cecf9d5Sandi
631*0cecf9d5Sandi//-------------------------------------------------------------------
632*0cecf9d5Sandiclass Doku_Parser_Mode_CamelCaseLink extends Doku_Parser_Mode {
633*0cecf9d5Sandi
634*0cecf9d5Sandi    function connectTo($mode) {
635*0cecf9d5Sandi        $this->Lexer->addSpecialPattern(
636*0cecf9d5Sandi                '\b[A-Z]+[a-z]+[A-Z][A-Za-z]*\b',$mode,'camelcaselink'
637*0cecf9d5Sandi            );
638*0cecf9d5Sandi    }
639*0cecf9d5Sandi
640*0cecf9d5Sandi}
641*0cecf9d5Sandi
642*0cecf9d5Sandi//-------------------------------------------------------------------
643*0cecf9d5Sandiclass Doku_Parser_Mode_InternalLink extends Doku_Parser_Mode {
644*0cecf9d5Sandi
645*0cecf9d5Sandi    function connectTo($mode) {
646*0cecf9d5Sandi        // Word boundaries?
647*0cecf9d5Sandi        $this->Lexer->addSpecialPattern("\[\[[^\]]+?\]\]",$mode,'internallink');
648*0cecf9d5Sandi    }
649*0cecf9d5Sandi
650*0cecf9d5Sandi}
651*0cecf9d5Sandi
652*0cecf9d5Sandi//-------------------------------------------------------------------
653*0cecf9d5Sandiclass Doku_Parser_Mode_Media extends Doku_Parser_Mode {
654*0cecf9d5Sandi
655*0cecf9d5Sandi    function connectTo($mode) {
656*0cecf9d5Sandi        // Word boundaries?
657*0cecf9d5Sandi        $this->Lexer->addSpecialPattern("\{\{[^\}]+\}\}",$mode,'media');
658*0cecf9d5Sandi    }
659*0cecf9d5Sandi
660*0cecf9d5Sandi}
661*0cecf9d5Sandi
662*0cecf9d5Sandi//-------------------------------------------------------------------
663*0cecf9d5Sandiclass Doku_Parser_Mode_ExternalLink extends Doku_Parser_Mode {
664*0cecf9d5Sandi    var $schemes = array('http','https','telnet','gopher','wais','ftp','ed2k','irc');
665*0cecf9d5Sandi    var $patterns = array();
666*0cecf9d5Sandi
667*0cecf9d5Sandi    function preConnect() {
668*0cecf9d5Sandi
669*0cecf9d5Sandi        $ltrs = '\w';
670*0cecf9d5Sandi        $gunk = '/\#~:.?+=&%@!\-';
671*0cecf9d5Sandi        $punc = '.:?\-;,';
672*0cecf9d5Sandi        $host = $ltrs.$punc;
673*0cecf9d5Sandi        $any  = $ltrs.$gunk.$punc;
674*0cecf9d5Sandi
675*0cecf9d5Sandi        foreach ( $this->schemes as $scheme ) {
676*0cecf9d5Sandi            $this->patterns[] = '\b(?i)'.$scheme.'(?-i)://['.$any.']+?['.$punc.']*[^'.$any.']';
677*0cecf9d5Sandi        }
678*0cecf9d5Sandi
679*0cecf9d5Sandi        $this->patterns[] = '\b(?i)www?(?-i)\.['.$host.']+?\.['.$host.']+?['.$any.']+?['.$punc.']*[^'.$any.']';
680*0cecf9d5Sandi        $this->patterns[] = '\b(?i)ftp?(?-i)\.['.$host.']+?\.['.$host.']+?['.$any.']+?['.$punc.']*[^'.$any.']';
681*0cecf9d5Sandi
682*0cecf9d5Sandi    }
683*0cecf9d5Sandi
684*0cecf9d5Sandi    function connectTo($mode) {
685*0cecf9d5Sandi        foreach ( $this->patterns as $pattern ) {
686*0cecf9d5Sandi            $this->Lexer->addSpecialPattern($pattern,$mode,'externallink');
687*0cecf9d5Sandi        }
688*0cecf9d5Sandi    }
689*0cecf9d5Sandi
690*0cecf9d5Sandi}
691*0cecf9d5Sandi
692*0cecf9d5Sandi//-------------------------------------------------------------------
693*0cecf9d5Sandiclass Doku_Parser_Mode_FileLink extends Doku_Parser_Mode {
694*0cecf9d5Sandi
695*0cecf9d5Sandi    var $pattern;
696*0cecf9d5Sandi
697*0cecf9d5Sandi    function preConnect() {
698*0cecf9d5Sandi
699*0cecf9d5Sandi        $ltrs = '\w';
700*0cecf9d5Sandi        $gunk = '/\#~:.?+=&%@!\-';
701*0cecf9d5Sandi        $punc = '.:?\-;,';
702*0cecf9d5Sandi        $host = $ltrs.$punc;
703*0cecf9d5Sandi        $any  = $ltrs.$gunk.$punc;
704*0cecf9d5Sandi
705*0cecf9d5Sandi        $this->pattern = '\b(?i)file(?-i)://['.$any.']+?['.
706*0cecf9d5Sandi            $punc.']*[^'.$any.']';
707*0cecf9d5Sandi    }
708*0cecf9d5Sandi
709*0cecf9d5Sandi    function connectTo($mode) {
710*0cecf9d5Sandi        $this->Lexer->addSpecialPattern(
711*0cecf9d5Sandi            $this->pattern,$mode,'filelink');
712*0cecf9d5Sandi    }
713*0cecf9d5Sandi
714*0cecf9d5Sandi
715*0cecf9d5Sandi}
716*0cecf9d5Sandi
717*0cecf9d5Sandi//-------------------------------------------------------------------
718*0cecf9d5Sandiclass Doku_Parser_Mode_WindowsShareLink extends Doku_Parser_Mode {
719*0cecf9d5Sandi
720*0cecf9d5Sandi    var $pattern;
721*0cecf9d5Sandi
722*0cecf9d5Sandi    function preConnect() {
723*0cecf9d5Sandi
724*0cecf9d5Sandi        $ltrs = '\w';
725*0cecf9d5Sandi        $gunk = '/\#~:.?+=&%@!\-';
726*0cecf9d5Sandi        $punc = '.:?\-;,';
727*0cecf9d5Sandi        $host = $ltrs.$punc;
728*0cecf9d5Sandi        $any  = $ltrs.$gunk.$punc;
729*0cecf9d5Sandi
730*0cecf9d5Sandi        $this->pattern = "[$gunk$punc\s]\\\\\\\\[$host]+?\\\\[$any]+?[$punc]*[^$any]";
731*0cecf9d5Sandi    }
732*0cecf9d5Sandi
733*0cecf9d5Sandi    function connectTo($mode) {
734*0cecf9d5Sandi        $this->Lexer->addSpecialPattern(
735*0cecf9d5Sandi            $this->pattern,$mode,'windowssharelink');
736*0cecf9d5Sandi    }
737*0cecf9d5Sandi
738*0cecf9d5Sandi
739*0cecf9d5Sandi}
740*0cecf9d5Sandi
741*0cecf9d5Sandi//-------------------------------------------------------------------
742*0cecf9d5Sandiclass Doku_Parser_Mode_Email extends Doku_Parser_Mode {
743*0cecf9d5Sandi
744*0cecf9d5Sandi    function connectTo($mode) {
745*0cecf9d5Sandi    //<([\w0-9\-_.]+?)@([\w\-]+\.([\w\-\.]+\.)*[\w]+)>
746*0cecf9d5Sandi        $this->Lexer->addSpecialPattern("<[\w0-9\-_.]+?@[\w\-]+\.[\w\-\.]+\.*[\w]+>",$mode,'email');
747*0cecf9d5Sandi    }
748*0cecf9d5Sandi
749*0cecf9d5Sandi}
750*0cecf9d5Sandi
751*0cecf9d5Sandi//-------------------------------------------------------------------
752*0cecf9d5Sandi// Help fns to keep mode lists - used to make it easier to populate
753*0cecf9d5Sandi// the list of modes another mode accepts
754*0cecf9d5Sandi
755*0cecf9d5Sandi// Can contain many other modes
756*0cecf9d5Sandi// E.g. a footnote can containing formatting etc.
757*0cecf9d5Sandifunction Doku_Parser_BlockContainers() {
758*0cecf9d5Sandi    $modes = array(
759*0cecf9d5Sandi        'footnote', 'listblock', 'table','quote',
760*0cecf9d5Sandi        // hr breaks the principle but HRs should not be used in tables / lists
761*0cecf9d5Sandi        // so put it here
762*0cecf9d5Sandi        'hr',
763*0cecf9d5Sandi    );
764*0cecf9d5Sandi    return $modes;
765*0cecf9d5Sandi}
766*0cecf9d5Sandi
767*0cecf9d5Sandi// Used to mark paragraph boundaries
768*0cecf9d5Sandifunction Doku_Parser_Paragraphs() {
769*0cecf9d5Sandi    $modes = array(
770*0cecf9d5Sandi        'eol'
771*0cecf9d5Sandi    );
772*0cecf9d5Sandi    return $modes;
773*0cecf9d5Sandi}
774*0cecf9d5Sandi
775*0cecf9d5Sandi// Can only be used by the base mode
776*0cecf9d5Sandifunction Doku_Parser_BaseOnly() {
777*0cecf9d5Sandi    $modes = array(
778*0cecf9d5Sandi        'header'
779*0cecf9d5Sandi    );
780*0cecf9d5Sandi    return $modes;
781*0cecf9d5Sandi}
782*0cecf9d5Sandi
783*0cecf9d5Sandi// "Styling" modes that format text.
784*0cecf9d5Sandifunction Doku_Parser_Formatting($remove = '') {
785*0cecf9d5Sandi    $modes = array(
786*0cecf9d5Sandi        'strong', 'emphasis', 'underline', 'monospace',
787*0cecf9d5Sandi        'subscript', 'superscript', 'deleted',
788*0cecf9d5Sandi        );
789*0cecf9d5Sandi    $key = array_search($remove, $modes);
790*0cecf9d5Sandi    if ( is_int($key) ) {
791*0cecf9d5Sandi        unset($modes[$key]);
792*0cecf9d5Sandi    }
793*0cecf9d5Sandi
794*0cecf9d5Sandi    return $modes;
795*0cecf9d5Sandi}
796*0cecf9d5Sandi
797*0cecf9d5Sandi// Modes where the token is simply replaced - contain no
798*0cecf9d5Sandi// other modes
799*0cecf9d5Sandifunction Doku_Parser_Substition() {
800*0cecf9d5Sandi    $modes = array(
801*0cecf9d5Sandi        'acronym','smiley','wordblock','entity','camelcaselink',
802*0cecf9d5Sandi        'internallink','media','externallink','linebreak','email',
803*0cecf9d5Sandi        'windowssharelink','filelink','notoc','multiplyentity',
804*0cecf9d5Sandi        'quotes',
805*0cecf9d5Sandi
806*0cecf9d5Sandi    );
807*0cecf9d5Sandi    return $modes;
808*0cecf9d5Sandi}
809*0cecf9d5Sandi
810*0cecf9d5Sandi// Modes which have a start and end token but inside which
811*0cecf9d5Sandi// no other modes should be applied
812*0cecf9d5Sandifunction Doku_Parser_Protected() {
813*0cecf9d5Sandi    $modes = array(
814*0cecf9d5Sandi        'preformatted','code','file',
815*0cecf9d5Sandi        'php','html','quote',
816*0cecf9d5Sandi    );
817*0cecf9d5Sandi    return $modes;
818*0cecf9d5Sandi}
819*0cecf9d5Sandi
820*0cecf9d5Sandi// Disable wiki markup inside this mode
821*0cecf9d5Sandifunction Doku_Parser_Disabled() {
822*0cecf9d5Sandi    $modes = array(
823*0cecf9d5Sandi        'unformatted'
824*0cecf9d5Sandi    );
825*0cecf9d5Sandi    return $modes;
826*0cecf9d5Sandi}
827*0cecf9d5Sandi
828*0cecf9d5Sandi?>
829