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