xref: /dokuwiki/inc/parser/parser.php (revision aae3ddc7642357af78e2a1690388a08d5e3e05b0)
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}(?=\s)',$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        $this->Lexer->addSpecialPattern('\n',$mode,'eol');
280    }
281
282    function getSort() {
283        return 370;
284    }
285}
286
287//-------------------------------------------------------------------
288class Doku_Parser_Mode_hr extends Doku_Parser_Mode {
289
290    function connectTo($mode) {
291        $this->Lexer->addSpecialPattern('\n[ \t]*-{4,}[ \t]*(?=\n)',$mode,'hr');
292    }
293
294    function getSort() {
295        return 160;
296    }
297}
298
299//-------------------------------------------------------------------
300class Doku_Parser_Mode_formatting extends Doku_Parser_Mode {
301    var $type;
302
303    var $formatting = array (
304        'strong' => array (
305            'entry'=>'\*\*(?=.*\*\*)',
306            'exit'=>'\*\*',
307            'sort'=>70
308            ),
309
310        'emphasis'=> array (
311            'entry'=>'//(?=[^\x00]*[^:])', //hack for bugs #384 #763 #1468
312            'exit'=>'//',
313            'sort'=>80
314            ),
315
316        'underline'=> array (
317            'entry'=>'__(?=.*__)',
318            'exit'=>'__',
319            'sort'=>90
320            ),
321
322        'monospace'=> array (
323            'entry'=>'\x27\x27(?=.*\x27\x27)',
324            'exit'=>'\x27\x27',
325            'sort'=>100
326            ),
327
328        'subscript'=> array (
329            'entry'=>'<sub>(?=.*</sub>)',
330            'exit'=>'</sub>',
331            'sort'=>110
332            ),
333
334        'superscript'=> array (
335            'entry'=>'<sup>(?=.*</sup>)',
336            'exit'=>'</sup>',
337            'sort'=>120
338            ),
339
340        'deleted'=> array (
341            'entry'=>'<del>(?=.*</del>)',
342            'exit'=>'</del>',
343            'sort'=>130
344            ),
345        );
346
347    function Doku_Parser_Mode_formatting($type) {
348        global $PARSER_MODES;
349
350        if ( !array_key_exists($type, $this->formatting) ) {
351            trigger_error('Invalid formatting type '.$type, E_USER_WARNING);
352        }
353
354        $this->type = $type;
355
356        // formatting may contain other formatting but not it self
357        $modes = $PARSER_MODES['formatting'];
358        $key = array_search($type, $modes);
359        if ( is_int($key) ) {
360            unset($modes[$key]);
361        }
362
363        $this->allowedModes = array_merge (
364                $modes,
365                $PARSER_MODES['substition'],
366                $PARSER_MODES['disabled']
367            );
368    }
369
370    function connectTo($mode) {
371
372        // Can't nest formatting in itself
373        if ( $mode == $this->type ) {
374            return;
375        }
376
377        $this->Lexer->addEntryPattern(
378                $this->formatting[$this->type]['entry'],
379                $mode,
380                $this->type
381            );
382    }
383
384    function postConnect() {
385
386        $this->Lexer->addExitPattern(
387            $this->formatting[$this->type]['exit'],
388            $this->type
389            );
390
391    }
392
393    function getSort() {
394        return $this->formatting[$this->type]['sort'];
395    }
396}
397
398//-------------------------------------------------------------------
399class Doku_Parser_Mode_listblock extends Doku_Parser_Mode {
400
401    function Doku_Parser_Mode_listblock() {
402        global $PARSER_MODES;
403
404        $this->allowedModes = array_merge (
405                $PARSER_MODES['formatting'],
406                $PARSER_MODES['substition'],
407                $PARSER_MODES['disabled'],
408                $PARSER_MODES['protected'] #XXX new
409            );
410
411    //    $this->allowedModes[] = 'footnote';
412    }
413
414    function connectTo($mode) {
415        $this->Lexer->addEntryPattern('\n {2,}[\-\*]',$mode,'listblock');
416        $this->Lexer->addEntryPattern('\n\t{1,}[\-\*]',$mode,'listblock');
417
418        $this->Lexer->addPattern('\n {2,}[\-\*]','listblock');
419        $this->Lexer->addPattern('\n\t{1,}[\-\*]','listblock');
420
421    }
422
423    function postConnect() {
424        $this->Lexer->addExitPattern('\n','listblock');
425    }
426
427    function getSort() {
428        return 10;
429    }
430}
431
432//-------------------------------------------------------------------
433class Doku_Parser_Mode_table extends Doku_Parser_Mode {
434
435    function Doku_Parser_Mode_table() {
436        global $PARSER_MODES;
437
438        $this->allowedModes = array_merge (
439                $PARSER_MODES['formatting'],
440                $PARSER_MODES['substition'],
441                $PARSER_MODES['disabled'],
442                $PARSER_MODES['protected']
443            );
444    }
445
446    function connectTo($mode) {
447        $this->Lexer->addEntryPattern('\n\^',$mode,'table');
448        $this->Lexer->addEntryPattern('\n\|',$mode,'table');
449    }
450
451    function postConnect() {
452        $this->Lexer->addPattern('\n\^','table');
453        $this->Lexer->addPattern('\n\|','table');
454        #$this->Lexer->addPattern(' {2,}','table');
455        $this->Lexer->addPattern('[\t ]+','table');
456        $this->Lexer->addPattern('\^','table');
457        $this->Lexer->addPattern('\|','table');
458        $this->Lexer->addExitPattern('\n','table');
459    }
460
461    function getSort() {
462        return 60;
463    }
464}
465
466//-------------------------------------------------------------------
467class Doku_Parser_Mode_unformatted extends Doku_Parser_Mode {
468
469    function connectTo($mode) {
470        $this->Lexer->addEntryPattern('<nowiki>(?=.*</nowiki>)',$mode,'unformatted');
471        $this->Lexer->addEntryPattern('%%(?=.*%%)',$mode,'unformattedalt');
472    }
473
474    function postConnect() {
475        $this->Lexer->addExitPattern('</nowiki>','unformatted');
476        $this->Lexer->addExitPattern('%%','unformattedalt');
477        $this->Lexer->mapHandler('unformattedalt','unformatted');
478    }
479
480    function getSort() {
481        return 170;
482    }
483}
484
485//-------------------------------------------------------------------
486class Doku_Parser_Mode_php extends Doku_Parser_Mode {
487
488    function connectTo($mode) {
489        $this->Lexer->addEntryPattern('<php>(?=.*</php>)',$mode,'php');
490        $this->Lexer->addEntryPattern('<PHP>(?=.*</PHP>)',$mode,'phpblock');
491    }
492
493    function postConnect() {
494        $this->Lexer->addExitPattern('</php>','php');
495        $this->Lexer->addExitPattern('</PHP>','phpblock');
496    }
497
498    function getSort() {
499        return 180;
500    }
501}
502
503//-------------------------------------------------------------------
504class Doku_Parser_Mode_html extends Doku_Parser_Mode {
505
506    function connectTo($mode) {
507        $this->Lexer->addEntryPattern('<html>(?=.*</html>)',$mode,'html');
508        $this->Lexer->addEntryPattern('<HTML>(?=.*</HTML>)',$mode,'htmlblock');
509    }
510
511    function postConnect() {
512        $this->Lexer->addExitPattern('</html>','html');
513        $this->Lexer->addExitPattern('</HTML>','htmlblock');
514    }
515
516    function getSort() {
517        return 190;
518    }
519}
520
521//-------------------------------------------------------------------
522class Doku_Parser_Mode_preformatted extends Doku_Parser_Mode {
523
524    function connectTo($mode) {
525        // Has hard coded awareness of lists...
526        $this->Lexer->addEntryPattern('\n  (?![\*\-])',$mode,'preformatted');
527        $this->Lexer->addEntryPattern('\n\t(?![\*\-])',$mode,'preformatted');
528
529        // How to effect a sub pattern with the Lexer!
530        $this->Lexer->addPattern('\n  ','preformatted');
531        $this->Lexer->addPattern('\n\t','preformatted');
532
533    }
534
535    function postConnect() {
536        $this->Lexer->addExitPattern('\n','preformatted');
537    }
538
539    function getSort() {
540        return 20;
541    }
542}
543
544//-------------------------------------------------------------------
545class Doku_Parser_Mode_code extends Doku_Parser_Mode {
546
547    function connectTo($mode) {
548        $this->Lexer->addEntryPattern('<code(?=.*</code>)',$mode,'code');
549    }
550
551    function postConnect() {
552        $this->Lexer->addExitPattern('</code>','code');
553    }
554
555    function getSort() {
556        return 200;
557    }
558}
559
560//-------------------------------------------------------------------
561class Doku_Parser_Mode_file extends Doku_Parser_Mode {
562
563    function connectTo($mode) {
564        $this->Lexer->addEntryPattern('<file>(?=.*</file>)',$mode,'file');
565    }
566
567    function postConnect() {
568        $this->Lexer->addExitPattern('</file>','file');
569    }
570
571    function getSort() {
572        return 210;
573    }
574}
575
576//-------------------------------------------------------------------
577class Doku_Parser_Mode_quote extends Doku_Parser_Mode {
578
579    function Doku_Parser_Mode_quote() {
580        global $PARSER_MODES;
581
582        $this->allowedModes = array_merge (
583                $PARSER_MODES['formatting'],
584                $PARSER_MODES['substition'],
585                $PARSER_MODES['disabled'],
586                $PARSER_MODES['protected'] #XXX new
587            );
588            #$this->allowedModes[] = 'footnote';
589            #$this->allowedModes[] = 'preformatted';
590            #$this->allowedModes[] = 'unformatted';
591    }
592
593    function connectTo($mode) {
594        $this->Lexer->addEntryPattern('\n>{1,}',$mode,'quote');
595    }
596
597    function postConnect() {
598        $this->Lexer->addPattern('\n>{1,}','quote');
599        $this->Lexer->addExitPattern('\n','quote');
600    }
601
602    function getSort() {
603        return 220;
604    }
605}
606
607//-------------------------------------------------------------------
608class Doku_Parser_Mode_acronym extends Doku_Parser_Mode {
609    // A list
610    var $acronyms = array();
611    var $pattern = '';
612
613    function Doku_Parser_Mode_acronym($acronyms) {
614    	usort($acronyms,array($this,'_compare'));
615        $this->acronyms = $acronyms;
616    }
617
618    function preConnect() {
619        if(!count($this->acronyms)) return;
620
621        $bound = '[\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]';
622        $acronyms = array_map('Doku_Lexer_Escape',$this->acronyms);
623        $this->pattern = '(?<=^|'.$bound.')(?:'.join('|',$acronyms).')(?='.$bound.')';
624    }
625
626    function connectTo($mode) {
627        if(!count($this->acronyms)) return;
628
629        if ( strlen($this->pattern) > 0 ) {
630            $this->Lexer->addSpecialPattern($this->pattern,$mode,'acronym');
631        }
632    }
633
634    function getSort() {
635        return 240;
636    }
637
638    /**
639     * sort callback to order by string length descending
640     */
641    function _compare($a,$b) {
642        $a_len = strlen($a);
643        $b_len = strlen($b);
644        if ($a_len > $b_len) {
645          return -1;
646        } else if ($a_len < $b_len) {
647          return 1;
648        }
649
650    	return 0;
651    }
652}
653
654//-------------------------------------------------------------------
655class Doku_Parser_Mode_smiley extends Doku_Parser_Mode {
656    // A list
657    var $smileys = array();
658    var $pattern = '';
659
660    function Doku_Parser_Mode_smiley($smileys) {
661        $this->smileys = $smileys;
662    }
663
664    function preConnect() {
665        if(!count($this->smileys) || $this->pattern != '') return;
666
667        $sep = '';
668        foreach ( $this->smileys as $smiley ) {
669            $this->pattern .= $sep.'(?<=\W|^)'.Doku_Lexer_Escape($smiley).'(?=\W|$)';
670            $sep = '|';
671        }
672    }
673
674    function connectTo($mode) {
675        if(!count($this->smileys)) return;
676
677        if ( strlen($this->pattern) > 0 ) {
678            $this->Lexer->addSpecialPattern($this->pattern,$mode,'smiley');
679        }
680    }
681
682    function getSort() {
683        return 230;
684    }
685}
686
687//-------------------------------------------------------------------
688class Doku_Parser_Mode_wordblock extends Doku_Parser_Mode {
689    // A list
690    var $badwords = array();
691    var $pattern = '';
692
693    function Doku_Parser_Mode_wordblock($badwords) {
694        $this->badwords = $badwords;
695    }
696
697    function preConnect() {
698
699        if ( count($this->badwords) == 0 || $this->pattern != '') {
700            return;
701        }
702
703        $sep = '';
704        foreach ( $this->badwords as $badword ) {
705            $this->pattern .= $sep.'(?<=\b)(?i)'.Doku_Lexer_Escape($badword).'(?-i)(?=\b)';
706            $sep = '|';
707        }
708
709    }
710
711    function connectTo($mode) {
712        if ( strlen($this->pattern) > 0 ) {
713            $this->Lexer->addSpecialPattern($this->pattern,$mode,'wordblock');
714        }
715    }
716
717    function getSort() {
718        return 250;
719    }
720}
721
722//-------------------------------------------------------------------
723class Doku_Parser_Mode_entity extends Doku_Parser_Mode {
724    // A list
725    var $entities = array();
726    var $pattern = '';
727
728    function Doku_Parser_Mode_entity($entities) {
729        $this->entities = $entities;
730    }
731
732    function preConnect() {
733        if(!count($this->entities) || $this->pattern != '') return;
734
735        $sep = '';
736        foreach ( $this->entities as $entity ) {
737            $this->pattern .= $sep.Doku_Lexer_Escape($entity);
738            $sep = '|';
739        }
740    }
741
742    function connectTo($mode) {
743        if(!count($this->entities)) return;
744
745        if ( strlen($this->pattern) > 0 ) {
746            $this->Lexer->addSpecialPattern($this->pattern,$mode,'entity');
747        }
748    }
749
750    function getSort() {
751        return 260;
752    }
753}
754
755//-------------------------------------------------------------------
756// Implements the 640x480 replacement
757class Doku_Parser_Mode_multiplyentity extends Doku_Parser_Mode {
758
759    function connectTo($mode) {
760
761        $this->Lexer->addSpecialPattern(
762                    '(?<=\b)(?:[1-9]|\d{2,})[xX]\d+(?=\b)',$mode,'multiplyentity'
763                );
764
765    }
766
767    function getSort() {
768        return 270;
769    }
770}
771
772//-------------------------------------------------------------------
773class Doku_Parser_Mode_quotes extends Doku_Parser_Mode {
774
775    function connectTo($mode) {
776        global $conf;
777
778        $ws   =  '\s/\#~:+=&%@\-\x28\x29\]\[{}><"\'';   // whitespace
779        $punc =  ';,\.?!';
780
781        if($conf['typography'] == 2){
782            $this->Lexer->addSpecialPattern(
783                        "(?<=^|[$ws])'(?=[^$ws$punc])",$mode,'singlequoteopening'
784                    );
785            $this->Lexer->addSpecialPattern(
786                        "(?<=^|[^$ws]|[$punc])'(?=$|[$ws$punc])",$mode,'singlequoteclosing'
787                    );
788            $this->Lexer->addSpecialPattern(
789                        "(?<=^|[^$ws$punc])'(?=$|[^$ws$punc])",$mode,'apostrophe'
790                    );
791        }
792
793        $this->Lexer->addSpecialPattern(
794                    "(?<=^|[$ws])\"(?=[^$ws$punc])",$mode,'doublequoteopening'
795                );
796        $this->Lexer->addSpecialPattern(
797                    "\"",$mode,'doublequoteclosing'
798                );
799
800
801    }
802
803    function getSort() {
804        return 280;
805    }
806}
807
808//-------------------------------------------------------------------
809class Doku_Parser_Mode_camelcaselink extends Doku_Parser_Mode {
810
811    function connectTo($mode) {
812        $this->Lexer->addSpecialPattern(
813                '\b[A-Z]+[a-z]+[A-Z][A-Za-z]*\b',$mode,'camelcaselink'
814            );
815    }
816
817    function getSort() {
818        return 290;
819    }
820}
821
822//-------------------------------------------------------------------
823class Doku_Parser_Mode_internallink extends Doku_Parser_Mode {
824
825    function connectTo($mode) {
826        // Word boundaries?
827        $this->Lexer->addSpecialPattern("\[\[.+?\]\]",$mode,'internallink');
828    }
829
830    function getSort() {
831        return 300;
832    }
833}
834
835//-------------------------------------------------------------------
836class Doku_Parser_Mode_media extends Doku_Parser_Mode {
837
838    function connectTo($mode) {
839        // Word boundaries?
840        $this->Lexer->addSpecialPattern("\{\{[^\}]+\}\}",$mode,'media');
841    }
842
843    function getSort() {
844        return 320;
845    }
846}
847
848//-------------------------------------------------------------------
849class Doku_Parser_Mode_rss extends Doku_Parser_Mode {
850
851    function connectTo($mode) {
852        $this->Lexer->addSpecialPattern("\{\{rss>[^\}]+\}\}",$mode,'rss');
853    }
854
855    function getSort() {
856        return 310;
857    }
858}
859
860//-------------------------------------------------------------------
861class Doku_Parser_Mode_externallink extends Doku_Parser_Mode {
862    var $schemes = array();
863    var $patterns = array();
864
865    function preConnect() {
866        if(count($this->patterns)) return;
867
868        $ltrs = '\w';
869        $gunk = '/\#~:.?+=&%@!\-';
870        $punc = '.:?\-;,';
871        $host = $ltrs.$punc;
872        $any  = $ltrs.$gunk.$punc;
873
874        $this->schemes = getSchemes();
875        foreach ( $this->schemes as $scheme ) {
876            $this->patterns[] = '\b(?i)'.$scheme.'(?-i)://['.$any.']+?(?=['.$punc.']*[^'.$any.'])';
877        }
878
879        $this->patterns[] = '\b(?i)www?(?-i)\.['.$host.']+?\.['.$host.']+?['.$any.']+?(?=['.$punc.']*[^'.$any.'])';
880        $this->patterns[] = '\b(?i)ftp?(?-i)\.['.$host.']+?\.['.$host.']+?['.$any.']+?(?=['.$punc.']*[^'.$any.'])';
881    }
882
883    function connectTo($mode) {
884
885        foreach ( $this->patterns as $pattern ) {
886            $this->Lexer->addSpecialPattern($pattern,$mode,'externallink');
887        }
888    }
889
890    function getSort() {
891        return 330;
892    }
893}
894
895//-------------------------------------------------------------------
896class Doku_Parser_Mode_filelink extends Doku_Parser_Mode {
897
898    var $pattern;
899
900    function preConnect() {
901
902        $ltrs = '\w';
903        $gunk = '/\#~:.?+=&%@!\-';
904        $punc = '.:?\-;,';
905        $host = $ltrs.$punc;
906        $any  = $ltrs.$gunk.$punc;
907
908        $this->pattern = '\b(?i)file(?-i)://['.$any.']+?['.
909            $punc.']*[^'.$any.']';
910    }
911
912    function connectTo($mode) {
913        $this->Lexer->addSpecialPattern(
914            $this->pattern,$mode,'filelink');
915    }
916
917    function getSort() {
918        return 360;
919    }
920}
921
922//-------------------------------------------------------------------
923class Doku_Parser_Mode_windowssharelink extends Doku_Parser_Mode {
924
925    var $pattern;
926
927    function preConnect() {
928        $this->pattern = "\\\\\\\\\w+?(?:\\\\[\w$]+)+";
929    }
930
931    function connectTo($mode) {
932        $this->Lexer->addSpecialPattern(
933            $this->pattern,$mode,'windowssharelink');
934    }
935
936    function getSort() {
937        return 350;
938    }
939}
940
941//-------------------------------------------------------------------
942class Doku_Parser_Mode_emaillink extends Doku_Parser_Mode {
943
944    function connectTo($mode) {
945        // pattern below is defined in inc/mail.php
946        $this->Lexer->addSpecialPattern('<'.PREG_PATTERN_VALID_EMAIL.'>',$mode,'emaillink');
947    }
948
949    function getSort() {
950        return 340;
951    }
952}
953
954
955//Setup VIM: ex: et ts=4 enc=utf-8 :
956