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