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