xref: /dokuwiki/inc/parser/parser.php (revision 87dd614c04b8fc978860f84a8a0ca91f86b646e3)
1<?php
2if(!defined('DOKU_INC')) die('meh.');
3require_once DOKU_INC . 'inc/parser/lexer.php';
4require_once DOKU_INC . 'inc/parser/handler.php';
5
6
7/**
8 * Define various types of modes used by the parser - they are used to
9 * populate the list of modes another mode accepts
10 */
11global $PARSER_MODES;
12$PARSER_MODES = array(
13    // containers are complex modes that can contain many other modes
14    // hr breaks the principle but they shouldn't be used in tables / lists
15    // so they are put here
16    'container'    => array('listblock','table','quote','hr'),
17
18    // some mode are allowed inside the base mode only
19    'baseonly'     => array('header'),
20
21    // modes for styling text -- footnote behaves similar to styling
22    'formatting'   => array('strong', 'emphasis', 'underline', 'monospace',
23                            'subscript', 'superscript', 'deleted', 'footnote'),
24
25    // modes where the token is simply replaced - they can not contain any
26    // other modes
27    'substition'   => array('acronym','smiley','wordblock','entity',
28                            'camelcaselink', 'internallink','media',
29                            'externallink','linebreak','emaillink',
30                            'windowssharelink','filelink','notoc',
31                            'nocache','multiplyentity','quotes','rss'),
32
33    // modes which have a start and end token but inside which
34    // no other modes should be applied
35    'protected'    => array('preformatted','code','file','php','html','htmlblock','phpblock'),
36
37    // inside this mode no wiki markup should be applied but lineendings
38    // and whitespace isn't preserved
39    'disabled'     => array('unformatted'),
40
41    // used to mark paragraph boundaries
42    'paragraphs'   => array('eol')
43);
44
45//-------------------------------------------------------------------
46
47/**
48* Sets up the Lexer with modes and points it to the Handler
49* For an intro to the Lexer see: wiki:parser
50*/
51class Doku_Parser {
52
53    var $Handler;
54
55    var $Lexer;
56
57    var $modes = array();
58
59    var $connected = false;
60
61    function addBaseMode(& $BaseMode) {
62        $this->modes['base'] = & $BaseMode;
63        if ( !$this->Lexer ) {
64            $this->Lexer = new Doku_Lexer($this->Handler,'base', true);
65        }
66        $this->modes['base']->Lexer = & $this->Lexer;
67    }
68
69    /**
70    * PHP preserves order of associative elements
71    * Mode sequence is important
72    */
73    function addMode($name, & $Mode) {
74        if ( !isset($this->modes['base']) ) {
75            $this->addBaseMode(new Doku_Parser_Mode_base());
76        }
77        $Mode->Lexer = & $this->Lexer;
78        $this->modes[$name] = & $Mode;
79    }
80
81    function connectModes() {
82
83        if ( $this->connected ) {
84            return;
85        }
86
87        foreach ( array_keys($this->modes) as $mode ) {
88
89            // Base isn't connected to anything
90            if ( $mode == 'base' ) {
91                continue;
92            }
93
94            $this->modes[$mode]->preConnect();
95
96            foreach ( array_keys($this->modes) as $cm ) {
97
98                if ( $this->modes[$cm]->accepts($mode) ) {
99                    $this->modes[$mode]->connectTo($cm);
100                }
101
102            }
103
104            $this->modes[$mode]->postConnect();
105        }
106
107        $this->connected = true;
108    }
109
110    function parse($doc) {
111        if ( $this->Lexer ) {
112            $this->connectModes();
113            // Normalize CRs and pad doc
114            $doc = "\n".str_replace("\r\n","\n",$doc)."\n";
115            $this->Lexer->parse($doc);
116            $this->Handler->_finalize();
117            return $this->Handler->calls;
118        } else {
119            return false;
120        }
121    }
122
123}
124
125//-------------------------------------------------------------------
126/**
127 * This class and all the subclasses below are
128 * used to reduce the effort required to register
129 * modes with the Lexer. For performance these
130 * could all be eliminated later perhaps, or
131 * the Parser could be serialized to a file once
132 * all modes are registered
133 *
134 * @author Harry Fuecks <hfuecks@gmail.com>
135*/
136class Doku_Parser_Mode {
137
138    var $Lexer;
139
140    var $allowedModes = array();
141
142    // returns a number used to determine in which order modes are added
143    function getSort() {
144        trigger_error('getSort() not implemented in '.get_class($this), E_USER_WARNING);
145    }
146
147    // Called before any calls to connectTo
148    function preConnect() {}
149
150    // Connects the mode
151    function connectTo($mode) {}
152
153    // Called after all calls to connectTo
154    function postConnect() {}
155
156    function accepts($mode) {
157        return in_array($mode, (array) $this->allowedModes );
158    }
159
160}
161
162//-------------------------------------------------------------------
163class Doku_Parser_Mode_base extends Doku_Parser_Mode {
164
165    function Doku_Parser_Mode_base() {
166        global $PARSER_MODES;
167
168        $this->allowedModes = array_merge (
169                $PARSER_MODES['container'],
170                $PARSER_MODES['baseonly'],
171                $PARSER_MODES['paragraphs'],
172                $PARSER_MODES['formatting'],
173                $PARSER_MODES['substition'],
174                $PARSER_MODES['protected'],
175                $PARSER_MODES['disabled']
176            );
177    }
178
179    function getSort() {
180        return 0;
181    }
182}
183
184//-------------------------------------------------------------------
185class Doku_Parser_Mode_footnote extends Doku_Parser_Mode {
186
187    function Doku_Parser_Mode_footnote() {
188        global $PARSER_MODES;
189
190        $this->allowedModes = array_merge (
191                $PARSER_MODES['container'],
192                $PARSER_MODES['formatting'],
193                $PARSER_MODES['substition'],
194                $PARSER_MODES['protected'],
195                $PARSER_MODES['disabled']
196            );
197
198        unset($this->allowedModes[array_search('footnote', $this->allowedModes)]);
199    }
200
201    function connectTo($mode) {
202        $this->Lexer->addEntryPattern(
203            '\x28\x28(?=.*\x29\x29)',$mode,'footnote'
204            );
205    }
206
207    function postConnect() {
208        $this->Lexer->addExitPattern(
209            '\x29\x29','footnote'
210            );
211    }
212
213    function getSort() {
214        return 150;
215    }
216}
217
218//-------------------------------------------------------------------
219class Doku_Parser_Mode_header extends Doku_Parser_Mode {
220
221    function preConnect() {
222        //we're not picky about the closing ones, two are enough
223        $this->Lexer->addSpecialPattern(
224                            '[ \t]*={2,}[^\n]+={2,}[ \t]*(?=\n)',
225                            'base',
226                            'header'
227                        );
228    }
229
230    function getSort() {
231        return 50;
232    }
233}
234
235//-------------------------------------------------------------------
236class Doku_Parser_Mode_notoc extends Doku_Parser_Mode {
237
238    function connectTo($mode) {
239        $this->Lexer->addSpecialPattern('~~NOTOC~~',$mode,'notoc');
240    }
241
242    function getSort() {
243        return 30;
244    }
245}
246
247//-------------------------------------------------------------------
248class Doku_Parser_Mode_nocache extends Doku_Parser_Mode {
249
250    function connectTo($mode) {
251        $this->Lexer->addSpecialPattern('~~NOCACHE~~',$mode,'nocache');
252    }
253
254    function getSort() {
255        return 40;
256    }
257}
258
259//-------------------------------------------------------------------
260class Doku_Parser_Mode_linebreak extends Doku_Parser_Mode {
261
262    function connectTo($mode) {
263        $this->Lexer->addSpecialPattern('\x5C{2}(?:[ \t]|(?=\n))',$mode,'linebreak');
264    }
265
266    function getSort() {
267        return 140;
268    }
269}
270
271//-------------------------------------------------------------------
272class Doku_Parser_Mode_eol extends Doku_Parser_Mode {
273
274    function connectTo($mode) {
275        $badModes = array('listblock','table');
276        if ( in_array($mode, $badModes) ) {
277            return;
278        }
279        // see FS#1652, pattern extended to swallow preceding whitespace to avoid issues with lines that only contain whitespace
280        $this->Lexer->addSpecialPattern('(?:^[ \t]*)?\n',$mode,'eol');
281    }
282
283    function getSort() {
284        return 370;
285    }
286}
287
288//-------------------------------------------------------------------
289class Doku_Parser_Mode_hr extends Doku_Parser_Mode {
290
291    function connectTo($mode) {
292        $this->Lexer->addSpecialPattern('\n[ \t]*-{4,}[ \t]*(?=\n)',$mode,'hr');
293    }
294
295    function getSort() {
296        return 160;
297    }
298}
299
300//-------------------------------------------------------------------
301// sets the markup for bold (=strong), italic (=emphasis), underline,
302// etc.
303class Doku_Parser_Mode_formatting extends Doku_Parser_Mode {
304    var $type;
305
306    var $formatting = array (
307        'strong' => array (
308            'entry'=>'\*\*(?=.*\*\*)',
309            'exit'=>'\*\*',
310            'sort'=>70
311            ),
312
313        'emphasis'=> array (
314            'entry'=>'//(?=[^\x00]*[^:])', //hack for bugs #384 #763 #1468
315            'exit'=>'//',
316            'sort'=>80
317            ),
318
319        'underline'=> array (
320            'entry'=>'__(?=.*__)',
321            'exit'=>'__',
322            'sort'=>90
323            ),
324
325        'monospace'=> array (
326            'entry'=>'\x27\x27(?=.*\x27\x27)',
327            'exit'=>'\x27\x27',
328            'sort'=>100
329            ),
330
331        'subscript'=> array (
332            'entry'=>'<sub>(?=.*</sub>)',
333            'exit'=>'</sub>',
334            'sort'=>110
335            ),
336
337        'superscript'=> array (
338            'entry'=>'<sup>(?=.*</sup>)',
339            'exit'=>'</sup>',
340            'sort'=>120
341            ),
342
343        'deleted'=> array (
344            'entry'=>'<del>(?=.*</del>)',
345            'exit'=>'</del>',
346            'sort'=>130
347            ),
348        );
349
350    function Doku_Parser_Mode_formatting($type) {
351        global $PARSER_MODES;
352
353        if ( !array_key_exists($type, $this->formatting) ) {
354            trigger_error('Invalid formatting type '.$type, E_USER_WARNING);
355        }
356
357        $this->type = $type;
358
359        // formatting may contain other formatting but not it self
360        $modes = $PARSER_MODES['formatting'];
361        $key = array_search($type, $modes);
362        if ( is_int($key) ) {
363            unset($modes[$key]);
364        }
365
366        $this->allowedModes = array_merge (
367                $modes,
368                $PARSER_MODES['substition'],
369                $PARSER_MODES['disabled']
370            );
371    }
372
373    function connectTo($mode) {
374
375        // Can't nest formatting in itself
376        if ( $mode == $this->type ) {
377            return;
378        }
379
380        $this->Lexer->addEntryPattern(
381                $this->formatting[$this->type]['entry'],
382                $mode,
383                $this->type
384            );
385    }
386
387    function postConnect() {
388
389        $this->Lexer->addExitPattern(
390            $this->formatting[$this->type]['exit'],
391            $this->type
392            );
393
394    }
395
396    function getSort() {
397        return $this->formatting[$this->type]['sort'];
398    }
399}
400
401//-------------------------------------------------------------------
402class Doku_Parser_Mode_listblock extends Doku_Parser_Mode {
403
404    function Doku_Parser_Mode_listblock() {
405        global $PARSER_MODES;
406
407        $this->allowedModes = array_merge (
408                $PARSER_MODES['formatting'],
409                $PARSER_MODES['substition'],
410                $PARSER_MODES['disabled'],
411                $PARSER_MODES['protected'] #XXX new
412            );
413
414    //    $this->allowedModes[] = 'footnote';
415    }
416
417    function connectTo($mode) {
418        $this->Lexer->addEntryPattern('[ \t]*\n {2,}[\-\*]',$mode,'listblock');
419        $this->Lexer->addEntryPattern('[ \t]*\n\t{1,}[\-\*]',$mode,'listblock');
420
421        $this->Lexer->addPattern('\n {2,}[\-\*]','listblock');
422        $this->Lexer->addPattern('\n\t{1,}[\-\*]','listblock');
423
424    }
425
426    function postConnect() {
427        $this->Lexer->addExitPattern('\n','listblock');
428    }
429
430    function getSort() {
431        return 10;
432    }
433}
434
435//-------------------------------------------------------------------
436class Doku_Parser_Mode_table extends Doku_Parser_Mode {
437
438    function Doku_Parser_Mode_table() {
439        global $PARSER_MODES;
440
441        $this->allowedModes = array_merge (
442                $PARSER_MODES['formatting'],
443                $PARSER_MODES['substition'],
444                $PARSER_MODES['disabled'],
445                $PARSER_MODES['protected']
446            );
447    }
448
449    function connectTo($mode) {
450        $this->Lexer->addEntryPattern('\n\^',$mode,'table');
451        $this->Lexer->addEntryPattern('\n\|',$mode,'table');
452    }
453
454    function postConnect() {
455        $this->Lexer->addPattern('\n\^','table');
456        $this->Lexer->addPattern('\n\|','table');
457        $this->Lexer->addPattern('[\t ]*:::[\t ]*(?=[\|\^])','table');
458        $this->Lexer->addPattern('[\t ]+','table');
459        $this->Lexer->addPattern('\^','table');
460        $this->Lexer->addPattern('\|','table');
461        $this->Lexer->addExitPattern('\n','table');
462    }
463
464    function getSort() {
465        return 60;
466    }
467}
468
469//-------------------------------------------------------------------
470class Doku_Parser_Mode_unformatted extends Doku_Parser_Mode {
471
472    function connectTo($mode) {
473        $this->Lexer->addEntryPattern('<nowiki>(?=.*</nowiki>)',$mode,'unformatted');
474        $this->Lexer->addEntryPattern('%%(?=.*%%)',$mode,'unformattedalt');
475    }
476
477    function postConnect() {
478        $this->Lexer->addExitPattern('</nowiki>','unformatted');
479        $this->Lexer->addExitPattern('%%','unformattedalt');
480        $this->Lexer->mapHandler('unformattedalt','unformatted');
481    }
482
483    function getSort() {
484        return 170;
485    }
486}
487
488//-------------------------------------------------------------------
489class Doku_Parser_Mode_php extends Doku_Parser_Mode {
490
491    function connectTo($mode) {
492        $this->Lexer->addEntryPattern('<php>(?=.*</php>)',$mode,'php');
493        $this->Lexer->addEntryPattern('<PHP>(?=.*</PHP>)',$mode,'phpblock');
494    }
495
496    function postConnect() {
497        $this->Lexer->addExitPattern('</php>','php');
498        $this->Lexer->addExitPattern('</PHP>','phpblock');
499    }
500
501    function getSort() {
502        return 180;
503    }
504}
505
506//-------------------------------------------------------------------
507class Doku_Parser_Mode_html extends Doku_Parser_Mode {
508
509    function connectTo($mode) {
510        $this->Lexer->addEntryPattern('<html>(?=.*</html>)',$mode,'html');
511        $this->Lexer->addEntryPattern('<HTML>(?=.*</HTML>)',$mode,'htmlblock');
512    }
513
514    function postConnect() {
515        $this->Lexer->addExitPattern('</html>','html');
516        $this->Lexer->addExitPattern('</HTML>','htmlblock');
517    }
518
519    function getSort() {
520        return 190;
521    }
522}
523
524//-------------------------------------------------------------------
525class Doku_Parser_Mode_preformatted extends Doku_Parser_Mode {
526
527    function connectTo($mode) {
528        // Has hard coded awareness of lists...
529        $this->Lexer->addEntryPattern('\n  (?![\*\-])',$mode,'preformatted');
530        $this->Lexer->addEntryPattern('\n\t(?![\*\-])',$mode,'preformatted');
531
532        // How to effect a sub pattern with the Lexer!
533        $this->Lexer->addPattern('\n  ','preformatted');
534        $this->Lexer->addPattern('\n\t','preformatted');
535
536    }
537
538    function postConnect() {
539        $this->Lexer->addExitPattern('\n','preformatted');
540    }
541
542    function getSort() {
543        return 20;
544    }
545}
546
547//-------------------------------------------------------------------
548class Doku_Parser_Mode_code extends Doku_Parser_Mode {
549
550    function connectTo($mode) {
551        $this->Lexer->addEntryPattern('<code(?=.*</code>)',$mode,'code');
552    }
553
554    function postConnect() {
555        $this->Lexer->addExitPattern('</code>','code');
556    }
557
558    function getSort() {
559        return 200;
560    }
561}
562
563//-------------------------------------------------------------------
564class Doku_Parser_Mode_file extends Doku_Parser_Mode {
565
566    function connectTo($mode) {
567        $this->Lexer->addEntryPattern('<file(?=.*</file>)',$mode,'file');
568    }
569
570    function postConnect() {
571        $this->Lexer->addExitPattern('</file>','file');
572    }
573
574    function getSort() {
575        return 210;
576    }
577}
578
579//-------------------------------------------------------------------
580class Doku_Parser_Mode_quote extends Doku_Parser_Mode {
581
582    function Doku_Parser_Mode_quote() {
583        global $PARSER_MODES;
584
585        $this->allowedModes = array_merge (
586                $PARSER_MODES['formatting'],
587                $PARSER_MODES['substition'],
588                $PARSER_MODES['disabled'],
589                $PARSER_MODES['protected'] #XXX new
590            );
591            #$this->allowedModes[] = 'footnote';
592            #$this->allowedModes[] = 'preformatted';
593            #$this->allowedModes[] = 'unformatted';
594    }
595
596    function connectTo($mode) {
597        $this->Lexer->addEntryPattern('\n>{1,}',$mode,'quote');
598    }
599
600    function postConnect() {
601        $this->Lexer->addPattern('\n>{1,}','quote');
602        $this->Lexer->addExitPattern('\n','quote');
603    }
604
605    function getSort() {
606        return 220;
607    }
608}
609
610//-------------------------------------------------------------------
611class Doku_Parser_Mode_acronym extends Doku_Parser_Mode {
612    // A list
613    var $acronyms = array();
614    var $pattern = '';
615
616    function Doku_Parser_Mode_acronym($acronyms) {
617    	usort($acronyms,array($this,'_compare'));
618        $this->acronyms = $acronyms;
619    }
620
621    function preConnect() {
622        if(!count($this->acronyms)) return;
623
624        $bound = '[\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]';
625        $acronyms = array_map('Doku_Lexer_Escape',$this->acronyms);
626        $this->pattern = '(?<=^|'.$bound.')(?:'.join('|',$acronyms).')(?='.$bound.')';
627    }
628
629    function connectTo($mode) {
630        if(!count($this->acronyms)) return;
631
632        if ( strlen($this->pattern) > 0 ) {
633            $this->Lexer->addSpecialPattern($this->pattern,$mode,'acronym');
634        }
635    }
636
637    function getSort() {
638        return 240;
639    }
640
641    /**
642     * sort callback to order by string length descending
643     */
644    function _compare($a,$b) {
645        $a_len = strlen($a);
646        $b_len = strlen($b);
647        if ($a_len > $b_len) {
648          return -1;
649        } else if ($a_len < $b_len) {
650          return 1;
651        }
652
653    	return 0;
654    }
655}
656
657//-------------------------------------------------------------------
658class Doku_Parser_Mode_smiley extends Doku_Parser_Mode {
659    // A list
660    var $smileys = array();
661    var $pattern = '';
662
663    function Doku_Parser_Mode_smiley($smileys) {
664        $this->smileys = $smileys;
665    }
666
667    function preConnect() {
668        if(!count($this->smileys) || $this->pattern != '') return;
669
670        $sep = '';
671        foreach ( $this->smileys as $smiley ) {
672            $this->pattern .= $sep.'(?<=\W|^)'.Doku_Lexer_Escape($smiley).'(?=\W|$)';
673            $sep = '|';
674        }
675    }
676
677    function connectTo($mode) {
678        if(!count($this->smileys)) return;
679
680        if ( strlen($this->pattern) > 0 ) {
681            $this->Lexer->addSpecialPattern($this->pattern,$mode,'smiley');
682        }
683    }
684
685    function getSort() {
686        return 230;
687    }
688}
689
690//-------------------------------------------------------------------
691class Doku_Parser_Mode_wordblock extends Doku_Parser_Mode {
692    // A list
693    var $badwords = array();
694    var $pattern = '';
695
696    function Doku_Parser_Mode_wordblock($badwords) {
697        $this->badwords = $badwords;
698    }
699
700    function preConnect() {
701
702        if ( count($this->badwords) == 0 || $this->pattern != '') {
703            return;
704        }
705
706        $sep = '';
707        foreach ( $this->badwords as $badword ) {
708            $this->pattern .= $sep.'(?<=\b)(?i)'.Doku_Lexer_Escape($badword).'(?-i)(?=\b)';
709            $sep = '|';
710        }
711
712    }
713
714    function connectTo($mode) {
715        if ( strlen($this->pattern) > 0 ) {
716            $this->Lexer->addSpecialPattern($this->pattern,$mode,'wordblock');
717        }
718    }
719
720    function getSort() {
721        return 250;
722    }
723}
724
725//-------------------------------------------------------------------
726class Doku_Parser_Mode_entity extends Doku_Parser_Mode {
727    // A list
728    var $entities = array();
729    var $pattern = '';
730
731    function Doku_Parser_Mode_entity($entities) {
732        $this->entities = $entities;
733    }
734
735    function preConnect() {
736        if(!count($this->entities) || $this->pattern != '') return;
737
738        $sep = '';
739        foreach ( $this->entities as $entity ) {
740            $this->pattern .= $sep.Doku_Lexer_Escape($entity);
741            $sep = '|';
742        }
743    }
744
745    function connectTo($mode) {
746        if(!count($this->entities)) return;
747
748        if ( strlen($this->pattern) > 0 ) {
749            $this->Lexer->addSpecialPattern($this->pattern,$mode,'entity');
750        }
751    }
752
753    function getSort() {
754        return 260;
755    }
756}
757
758//-------------------------------------------------------------------
759// Implements the 640x480 replacement
760class Doku_Parser_Mode_multiplyentity extends Doku_Parser_Mode {
761
762    function connectTo($mode) {
763
764        $this->Lexer->addSpecialPattern(
765                    '(?<=\b)(?:[1-9]|\d{2,})[xX]\d+(?=\b)',$mode,'multiplyentity'
766                );
767
768    }
769
770    function getSort() {
771        return 270;
772    }
773}
774
775//-------------------------------------------------------------------
776class Doku_Parser_Mode_quotes extends Doku_Parser_Mode {
777
778    function connectTo($mode) {
779        global $conf;
780
781        $ws   =  '\s/\#~:+=&%@\-\x28\x29\]\[{}><"\'';   // whitespace
782        $punc =  ';,\.?!';
783
784        if($conf['typography'] == 2){
785            $this->Lexer->addSpecialPattern(
786                        "(?<=^|[$ws])'(?=[^$ws$punc])",$mode,'singlequoteopening'
787                    );
788            $this->Lexer->addSpecialPattern(
789                        "(?<=^|[^$ws]|[$punc])'(?=$|[$ws$punc])",$mode,'singlequoteclosing'
790                    );
791            $this->Lexer->addSpecialPattern(
792                        "(?<=^|[^$ws$punc])'(?=$|[^$ws$punc])",$mode,'apostrophe'
793                    );
794        }
795
796        $this->Lexer->addSpecialPattern(
797                    "(?<=^|[$ws])\"(?=[^$ws$punc])",$mode,'doublequoteopening'
798                );
799        $this->Lexer->addSpecialPattern(
800                    "\"",$mode,'doublequoteclosing'
801                );
802
803
804    }
805
806    function getSort() {
807        return 280;
808    }
809}
810
811//-------------------------------------------------------------------
812class Doku_Parser_Mode_camelcaselink extends Doku_Parser_Mode {
813
814    function connectTo($mode) {
815        $this->Lexer->addSpecialPattern(
816                '\b[A-Z]+[a-z]+[A-Z][A-Za-z]*\b',$mode,'camelcaselink'
817            );
818    }
819
820    function getSort() {
821        return 290;
822    }
823}
824
825//-------------------------------------------------------------------
826class Doku_Parser_Mode_internallink extends Doku_Parser_Mode {
827
828    function connectTo($mode) {
829        // Word boundaries?
830        $this->Lexer->addSpecialPattern("\[\[.+?\]\]",$mode,'internallink');
831    }
832
833    function getSort() {
834        return 300;
835    }
836}
837
838//-------------------------------------------------------------------
839class Doku_Parser_Mode_media extends Doku_Parser_Mode {
840
841    function connectTo($mode) {
842        // Word boundaries?
843        $this->Lexer->addSpecialPattern("\{\{[^\}]+\}\}",$mode,'media');
844    }
845
846    function getSort() {
847        return 320;
848    }
849}
850
851//-------------------------------------------------------------------
852class Doku_Parser_Mode_rss extends Doku_Parser_Mode {
853
854    function connectTo($mode) {
855        $this->Lexer->addSpecialPattern("\{\{rss>[^\}]+\}\}",$mode,'rss');
856    }
857
858    function getSort() {
859        return 310;
860    }
861}
862
863//-------------------------------------------------------------------
864class Doku_Parser_Mode_externallink extends Doku_Parser_Mode {
865    var $schemes = array();
866    var $patterns = array();
867
868    function preConnect() {
869        if(count($this->patterns)) return;
870
871        $ltrs = '\w';
872        $gunk = '/\#~:.?+=&%@!\-';
873        $punc = '.:?\-;,';
874        $host = $ltrs.$punc;
875        $any  = $ltrs.$gunk.$punc;
876
877        $this->schemes = getSchemes();
878        foreach ( $this->schemes as $scheme ) {
879            $this->patterns[] = '\b(?i)'.$scheme.'(?-i)://['.$any.']+?(?=['.$punc.']*[^'.$any.'])';
880        }
881
882        $this->patterns[] = '\b(?i)www?(?-i)\.['.$host.']+?\.['.$host.']+?['.$any.']+?(?=['.$punc.']*[^'.$any.'])';
883        $this->patterns[] = '\b(?i)ftp?(?-i)\.['.$host.']+?\.['.$host.']+?['.$any.']+?(?=['.$punc.']*[^'.$any.'])';
884    }
885
886    function connectTo($mode) {
887
888        foreach ( $this->patterns as $pattern ) {
889            $this->Lexer->addSpecialPattern($pattern,$mode,'externallink');
890        }
891    }
892
893    function getSort() {
894        return 330;
895    }
896}
897
898//-------------------------------------------------------------------
899class Doku_Parser_Mode_filelink extends Doku_Parser_Mode {
900
901    var $pattern;
902
903    function preConnect() {
904
905        $ltrs = '\w';
906        $gunk = '/\#~:.?+=&%@!\-';
907        $punc = '.:?\-;,';
908        $host = $ltrs.$punc;
909        $any  = $ltrs.$gunk.$punc;
910
911        $this->pattern = '\b(?i)file(?-i)://['.$any.']+?['.
912            $punc.']*[^'.$any.']';
913    }
914
915    function connectTo($mode) {
916        $this->Lexer->addSpecialPattern(
917            $this->pattern,$mode,'filelink');
918    }
919
920    function getSort() {
921        return 360;
922    }
923}
924
925//-------------------------------------------------------------------
926class Doku_Parser_Mode_windowssharelink extends Doku_Parser_Mode {
927
928    var $pattern;
929
930    function preConnect() {
931        $this->pattern = "\\\\\\\\\w+?(?:\\\\[\w$]+)+";
932    }
933
934    function connectTo($mode) {
935        $this->Lexer->addSpecialPattern(
936            $this->pattern,$mode,'windowssharelink');
937    }
938
939    function getSort() {
940        return 350;
941    }
942}
943
944//-------------------------------------------------------------------
945class Doku_Parser_Mode_emaillink extends Doku_Parser_Mode {
946
947    function connectTo($mode) {
948        // pattern below is defined in inc/mail.php
949        $this->Lexer->addSpecialPattern('<'.PREG_PATTERN_VALID_EMAIL.'>',$mode,'emaillink');
950    }
951
952    function getSort() {
953        return 340;
954    }
955}
956
957
958//Setup VIM: ex: et ts=4 enc=utf-8 :
959