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