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