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