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