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