source: documentation/trunk/packages/dokuwiki-2011-05-25a/inc/parser/handler.php@ 25027

Last change on this file since 25027 was 25027, checked in by jmt12, 12 years ago

Adding the packages directory, and within it a configured version of dokuwiki all ready to run

File size: 51.0 KB
Line 
1<?php
2if(!defined('DOKU_INC')) die('meh.');
3if (!defined('DOKU_PARSER_EOL')) define('DOKU_PARSER_EOL',"\n"); // add this to make handling test cases simpler
4
5class Doku_Handler {
6
7 var $Renderer = NULL;
8
9 var $CallWriter = NULL;
10
11 var $calls = array();
12
13 var $status = array(
14 'section' => false,
15 );
16
17 var $rewriteBlocks = true;
18
19 function Doku_Handler() {
20 $this->CallWriter = new Doku_Handler_CallWriter($this);
21 }
22
23 function _addCall($handler, $args, $pos) {
24 $call = array($handler,$args, $pos);
25 $this->CallWriter->writeCall($call);
26 }
27
28 function addPluginCall($plugin, $args, $state, $pos, $match) {
29 $call = array('plugin',array($plugin, $args, $state, $match), $pos);
30 $this->CallWriter->writeCall($call);
31 }
32
33 function _finalize(){
34
35 $this->CallWriter->finalise();
36
37 if ( $this->status['section'] ) {
38 $last_call = end($this->calls);
39 array_push($this->calls,array('section_close',array(), $last_call[2]));
40 }
41
42 if ( $this->rewriteBlocks ) {
43 $B = new Doku_Handler_Block();
44 $this->calls = $B->process($this->calls);
45 }
46
47 trigger_event('PARSER_HANDLER_DONE',$this);
48
49 array_unshift($this->calls,array('document_start',array(),0));
50 $last_call = end($this->calls);
51 array_push($this->calls,array('document_end',array(),$last_call[2]));
52 }
53
54 function fetch() {
55 $call = each($this->calls);
56 if ( $call ) {
57 return $call['value'];
58 }
59 return false;
60 }
61
62
63 /**
64 * Special plugin handler
65 *
66 * This handler is called for all modes starting with 'plugin_'.
67 * An additional parameter with the plugin name is passed
68 *
69 * @author Andreas Gohr <[email protected]>
70 */
71 function plugin($match, $state, $pos, $pluginname){
72 $data = array($match);
73 $plugin =& plugin_load('syntax',$pluginname);
74 if($plugin != null){
75 $data = $plugin->handle($match, $state, $pos, $this);
76 }
77 if ($data !== false) {
78 $this->addPluginCall($pluginname,$data,$state,$pos,$match);
79 }
80 return true;
81 }
82
83 function base($match, $state, $pos) {
84 switch ( $state ) {
85 case DOKU_LEXER_UNMATCHED:
86 $this->_addCall('cdata',array($match), $pos);
87 return true;
88 break;
89 }
90 }
91
92 function header($match, $state, $pos) {
93 // get level and title
94 $title = trim($match);
95 $level = 7 - strspn($title,'=');
96 if($level < 1) $level = 1;
97 $title = trim($title,'=');
98 $title = trim($title);
99
100 // Hide HTML comments [jmt12]
101 $title = preg_replace('/<\!--[^>]*-->/','',$title);
102
103 if ($this->status['section']) $this->_addCall('section_close',array(),$pos);
104
105 $this->_addCall('header',array($title,$level,$pos), $pos);
106
107 $this->_addCall('section_open',array($level),$pos);
108 $this->status['section'] = true;
109 return true;
110 }
111
112 function notoc($match, $state, $pos) {
113 $this->_addCall('notoc',array(),$pos);
114 return true;
115 }
116
117 function nocache($match, $state, $pos) {
118 $this->_addCall('nocache',array(),$pos);
119 return true;
120 }
121
122 function linebreak($match, $state, $pos) {
123 $this->_addCall('linebreak',array(),$pos);
124 return true;
125 }
126
127 function eol($match, $state, $pos) {
128 $this->_addCall('eol',array(),$pos);
129 return true;
130 }
131
132 function hr($match, $state, $pos) {
133 $this->_addCall('hr',array(),$pos);
134 return true;
135 }
136
137 function _nestingTag($match, $state, $pos, $name) {
138 switch ( $state ) {
139 case DOKU_LEXER_ENTER:
140 $this->_addCall($name.'_open', array(), $pos);
141 break;
142 case DOKU_LEXER_EXIT:
143 $this->_addCall($name.'_close', array(), $pos);
144 break;
145 case DOKU_LEXER_UNMATCHED:
146 $this->_addCall('cdata',array($match), $pos);
147 break;
148 }
149 }
150
151 function strong($match, $state, $pos) {
152 $this->_nestingTag($match, $state, $pos, 'strong');
153 return true;
154 }
155
156 function emphasis($match, $state, $pos) {
157 $this->_nestingTag($match, $state, $pos, 'emphasis');
158 return true;
159 }
160
161 function underline($match, $state, $pos) {
162 $this->_nestingTag($match, $state, $pos, 'underline');
163 return true;
164 }
165
166 function monospace($match, $state, $pos) {
167 $this->_nestingTag($match, $state, $pos, 'monospace');
168 return true;
169 }
170
171 function subscript($match, $state, $pos) {
172 $this->_nestingTag($match, $state, $pos, 'subscript');
173 return true;
174 }
175
176 function superscript($match, $state, $pos) {
177 $this->_nestingTag($match, $state, $pos, 'superscript');
178 return true;
179 }
180
181 function deleted($match, $state, $pos) {
182 $this->_nestingTag($match, $state, $pos, 'deleted');
183 return true;
184 }
185
186
187 function footnote($match, $state, $pos) {
188// $this->_nestingTag($match, $state, $pos, 'footnote');
189 if (!isset($this->_footnote)) $this->_footnote = false;
190
191 switch ( $state ) {
192 case DOKU_LEXER_ENTER:
193 // footnotes can not be nested - however due to limitations in lexer it can't be prevented
194 // we will still enter a new footnote mode, we just do nothing
195 if ($this->_footnote) {
196 $this->_addCall('cdata',array($match), $pos);
197 break;
198 }
199
200 $this->_footnote = true;
201
202 $ReWriter = new Doku_Handler_Nest($this->CallWriter,'footnote_close');
203 $this->CallWriter = & $ReWriter;
204 $this->_addCall('footnote_open', array(), $pos);
205 break;
206 case DOKU_LEXER_EXIT:
207 // check whether we have already exitted the footnote mode, can happen if the modes were nested
208 if (!$this->_footnote) {
209 $this->_addCall('cdata',array($match), $pos);
210 break;
211 }
212
213 $this->_footnote = false;
214
215 $this->_addCall('footnote_close', array(), $pos);
216 $this->CallWriter->process();
217 $ReWriter = & $this->CallWriter;
218 $this->CallWriter = & $ReWriter->CallWriter;
219 break;
220 case DOKU_LEXER_UNMATCHED:
221 $this->_addCall('cdata', array($match), $pos);
222 break;
223 }
224 return true;
225 }
226
227 function listblock($match, $state, $pos) {
228 switch ( $state ) {
229 case DOKU_LEXER_ENTER:
230 $ReWriter = new Doku_Handler_List($this->CallWriter);
231 $this->CallWriter = & $ReWriter;
232 $this->_addCall('list_open', array($match), $pos);
233 break;
234 case DOKU_LEXER_EXIT:
235 $this->_addCall('list_close', array(), $pos);
236 $this->CallWriter->process();
237 $ReWriter = & $this->CallWriter;
238 $this->CallWriter = & $ReWriter->CallWriter;
239 break;
240 case DOKU_LEXER_MATCHED:
241 $this->_addCall('list_item', array($match), $pos);
242 break;
243 case DOKU_LEXER_UNMATCHED:
244 $this->_addCall('cdata', array($match), $pos);
245 break;
246 }
247 return true;
248 }
249
250 function unformatted($match, $state, $pos) {
251 if ( $state == DOKU_LEXER_UNMATCHED ) {
252 $this->_addCall('unformatted',array($match), $pos);
253 }
254 return true;
255 }
256
257 function php($match, $state, $pos) {
258 global $conf;
259 if ( $state == DOKU_LEXER_UNMATCHED ) {
260 $this->_addCall('php',array($match), $pos);
261 }
262 return true;
263 }
264
265 function phpblock($match, $state, $pos) {
266 global $conf;
267 if ( $state == DOKU_LEXER_UNMATCHED ) {
268 $this->_addCall('phpblock',array($match), $pos);
269 }
270 return true;
271 }
272
273 function html($match, $state, $pos) {
274 global $conf;
275 if ( $state == DOKU_LEXER_UNMATCHED ) {
276 $this->_addCall('html',array($match), $pos);
277 }
278 return true;
279 }
280
281 function htmlblock($match, $state, $pos) {
282 global $conf;
283 if ( $state == DOKU_LEXER_UNMATCHED ) {
284 $this->_addCall('htmlblock',array($match), $pos);
285 }
286 return true;
287 }
288
289 function preformatted($match, $state, $pos) {
290 switch ( $state ) {
291 case DOKU_LEXER_ENTER:
292 $ReWriter = new Doku_Handler_Preformatted($this->CallWriter);
293 $this->CallWriter = & $ReWriter;
294 $this->_addCall('preformatted_start',array(), $pos);
295 break;
296 case DOKU_LEXER_EXIT:
297 $this->_addCall('preformatted_end',array(), $pos);
298 $this->CallWriter->process();
299 $ReWriter = & $this->CallWriter;
300 $this->CallWriter = & $ReWriter->CallWriter;
301 break;
302 case DOKU_LEXER_MATCHED:
303 $this->_addCall('preformatted_newline',array(), $pos);
304 break;
305 case DOKU_LEXER_UNMATCHED:
306 $this->_addCall('preformatted_content',array($match), $pos);
307 break;
308 }
309
310 return true;
311 }
312
313 function quote($match, $state, $pos) {
314
315 switch ( $state ) {
316
317 case DOKU_LEXER_ENTER:
318 $ReWriter = new Doku_Handler_Quote($this->CallWriter);
319 $this->CallWriter = & $ReWriter;
320 $this->_addCall('quote_start',array($match), $pos);
321 break;
322
323 case DOKU_LEXER_EXIT:
324 $this->_addCall('quote_end',array(), $pos);
325 $this->CallWriter->process();
326 $ReWriter = & $this->CallWriter;
327 $this->CallWriter = & $ReWriter->CallWriter;
328 break;
329
330 case DOKU_LEXER_MATCHED:
331 $this->_addCall('quote_newline',array($match), $pos);
332 break;
333
334 case DOKU_LEXER_UNMATCHED:
335 $this->_addCall('cdata',array($match), $pos);
336 break;
337
338 }
339
340 return true;
341 }
342
343 function file($match, $state, $pos) {
344 return $this->code($match, $state, $pos, 'file');
345 }
346
347 function code($match, $state, $pos, $type='code') {
348 if ( $state == DOKU_LEXER_UNMATCHED ) {
349 $matches = explode('>',$match,2);
350
351 $param = preg_split('/\s+/', $matches[0], 2, PREG_SPLIT_NO_EMPTY);
352 while(count($param) < 2) array_push($param, null);
353
354 // We shortcut html here.
355 if ($param[0] == 'html') $param[0] = 'html4strict';
356 if ($param[0] == '-') $param[0] = null;
357 array_unshift($param, $matches[1]);
358
359 $this->_addCall($type, $param, $pos);
360 }
361 return true;
362 }
363
364 function acronym($match, $state, $pos) {
365 $this->_addCall('acronym',array($match), $pos);
366 return true;
367 }
368
369 function smiley($match, $state, $pos) {
370 $this->_addCall('smiley',array($match), $pos);
371 return true;
372 }
373
374 function wordblock($match, $state, $pos) {
375 $this->_addCall('wordblock',array($match), $pos);
376 return true;
377 }
378
379 function entity($match, $state, $pos) {
380 $this->_addCall('entity',array($match), $pos);
381 return true;
382 }
383
384 function multiplyentity($match, $state, $pos) {
385 preg_match_all('/\d+/',$match,$matches);
386 $this->_addCall('multiplyentity',array($matches[0][0],$matches[0][1]), $pos);
387 return true;
388 }
389
390 function singlequoteopening($match, $state, $pos) {
391 $this->_addCall('singlequoteopening',array(), $pos);
392 return true;
393 }
394
395 function singlequoteclosing($match, $state, $pos) {
396 $this->_addCall('singlequoteclosing',array(), $pos);
397 return true;
398 }
399
400 function apostrophe($match, $state, $pos) {
401 $this->_addCall('apostrophe',array(), $pos);
402 return true;
403 }
404
405 function doublequoteopening($match, $state, $pos) {
406 $this->_addCall('doublequoteopening',array(), $pos);
407 return true;
408 }
409
410 function doublequoteclosing($match, $state, $pos) {
411 $this->_addCall('doublequoteclosing',array(), $pos);
412 return true;
413 }
414
415 function camelcaselink($match, $state, $pos) {
416 $this->_addCall('camelcaselink',array($match), $pos);
417 return true;
418 }
419
420 /*
421 */
422 function internallink($match, $state, $pos) {
423 // Strip the opening and closing markup
424 $link = preg_replace(array('/^\[\[/','/\]\]$/u'),'',$match);
425
426 // Split title from URL
427 $link = explode('|',$link,2);
428 if ( !isset($link[1]) ) {
429 $link[1] = NULL;
430 } else if ( preg_match('/^\{\{[^\}]+\}\}$/',$link[1]) ) {
431 // If the title is an image, convert it to an array containing the image details
432 $link[1] = Doku_Handler_Parse_Media($link[1]);
433 }
434 $link[0] = trim($link[0]);
435
436 //decide which kind of link it is
437
438 if ( preg_match('/^[a-zA-Z0-9\.]+>{1}.*$/u',$link[0]) ) {
439 // Interwiki
440 $interwiki = explode('>',$link[0],2);
441 $this->_addCall(
442 'interwikilink',
443 array($link[0],$link[1],strtolower($interwiki[0]),$interwiki[1]),
444 $pos
445 );
446 }elseif ( preg_match('/^\\\\\\\\[^\\\\]+?\\\\/u',$link[0]) ) {
447 // Windows Share
448 $this->_addCall(
449 'windowssharelink',
450 array($link[0],$link[1]),
451 $pos
452 );
453 }elseif ( preg_match('#^([a-z0-9\-\.+]+?)://#i',$link[0]) ) {
454 // external link (accepts all protocols)
455 $this->_addCall(
456 'externallink',
457 array($link[0],$link[1]),
458 $pos
459 );
460 }elseif ( preg_match('<'.PREG_PATTERN_VALID_EMAIL.'>',$link[0]) ) {
461 // E-Mail (pattern above is defined in inc/mail.php)
462 $this->_addCall(
463 'emaillink',
464 array($link[0],$link[1]),
465 $pos
466 );
467 }elseif ( preg_match('!^#.+!',$link[0]) ){
468 // local link
469 $this->_addCall(
470 'locallink',
471 array(substr($link[0],1),$link[1]),
472 $pos
473 );
474 }else{
475 // internal link
476 $this->_addCall(
477 'internallink',
478 array($link[0],$link[1]),
479 $pos
480 );
481 }
482
483 return true;
484 }
485
486 function filelink($match, $state, $pos) {
487 $this->_addCall('filelink',array($match, NULL), $pos);
488 return true;
489 }
490
491 function windowssharelink($match, $state, $pos) {
492 $this->_addCall('windowssharelink',array($match, NULL), $pos);
493 return true;
494 }
495
496 function media($match, $state, $pos) {
497 $p = Doku_Handler_Parse_Media($match);
498
499 $this->_addCall(
500 $p['type'],
501 array($p['src'], $p['title'], $p['align'], $p['width'],
502 $p['height'], $p['cache'], $p['linking']),
503 $pos
504 );
505 return true;
506 }
507
508 function rss($match, $state, $pos) {
509 $link = preg_replace(array('/^\{\{rss>/','/\}\}$/'),'',$match);
510
511 // get params
512 list($link,$params) = explode(' ',$link,2);
513
514 $p = array();
515 if(preg_match('/\b(\d+)\b/',$params,$match)){
516 $p['max'] = $match[1];
517 }else{
518 $p['max'] = 8;
519 }
520 $p['reverse'] = (preg_match('/rev/',$params));
521 $p['author'] = (preg_match('/\b(by|author)/',$params));
522 $p['date'] = (preg_match('/\b(date)/',$params));
523 $p['details'] = (preg_match('/\b(desc|detail)/',$params));
524
525 if (preg_match('/\b(\d+)([dhm])\b/',$params,$match)) {
526 $period = array('d' => 86400, 'h' => 3600, 'm' => 60);
527 $p['refresh'] = max(600,$match[1]*$period[$match[2]]); // n * period in seconds, minimum 10 minutes
528 } else {
529 $p['refresh'] = 14400; // default to 4 hours
530 }
531
532 $this->_addCall('rss',array($link,$p),$pos);
533 return true;
534 }
535
536 function externallink($match, $state, $pos) {
537 $url = $match;
538 $title = null;
539
540 // add protocol on simple short URLs
541 if(substr($url,0,3) == 'ftp' && (substr($url,0,6) != 'ftp://')){
542 $title = $url;
543 $url = 'ftp://'.$url;
544 }
545 if(substr($url,0,3) == 'www' && (substr($url,0,7) != 'http://')){
546 $title = $url;
547 $url = 'http://'.$url;
548 }
549
550 $this->_addCall('externallink',array($url, $title), $pos);
551 return true;
552 }
553
554 function emaillink($match, $state, $pos) {
555 $email = preg_replace(array('/^</','/>$/'),'',$match);
556 $this->_addCall('emaillink',array($email, NULL), $pos);
557 return true;
558 }
559
560 function table($match, $state, $pos) {
561 switch ( $state ) {
562
563 case DOKU_LEXER_ENTER:
564
565 $ReWriter = new Doku_Handler_Table($this->CallWriter);
566 $this->CallWriter = & $ReWriter;
567
568 $this->_addCall('table_start', array($pos + 1), $pos);
569 if ( trim($match) == '^' ) {
570 $this->_addCall('tableheader', array(), $pos);
571 } else {
572 $this->_addCall('tablecell', array(), $pos);
573 }
574 break;
575
576 case DOKU_LEXER_EXIT:
577 $this->_addCall('table_end', array($pos), $pos);
578 $this->CallWriter->process();
579 $ReWriter = & $this->CallWriter;
580 $this->CallWriter = & $ReWriter->CallWriter;
581 break;
582
583 case DOKU_LEXER_UNMATCHED:
584 if ( trim($match) != '' ) {
585 $this->_addCall('cdata',array($match), $pos);
586 }
587 break;
588
589 case DOKU_LEXER_MATCHED:
590 if ( $match == ' ' ){
591 $this->_addCall('cdata', array($match), $pos);
592 } else if ( preg_match('/:::/',$match) ) {
593 $this->_addCall('rowspan', array($match), $pos);
594 } else if ( preg_match('/\t+/',$match) ) {
595 $this->_addCall('table_align', array($match), $pos);
596 } else if ( preg_match('/ {2,}/',$match) ) {
597 $this->_addCall('table_align', array($match), $pos);
598 } else if ( $match == "\n|" ) {
599 $this->_addCall('table_row', array(), $pos);
600 $this->_addCall('tablecell', array(), $pos);
601 } else if ( $match == "\n^" ) {
602 $this->_addCall('table_row', array(), $pos);
603 $this->_addCall('tableheader', array(), $pos);
604 } else if ( $match == '|' ) {
605 $this->_addCall('tablecell', array(), $pos);
606 } else if ( $match == '^' ) {
607 $this->_addCall('tableheader', array(), $pos);
608 }
609 break;
610 }
611 return true;
612 }
613}
614
615//------------------------------------------------------------------------
616function Doku_Handler_Parse_Media($match) {
617
618 // Strip the opening and closing markup
619 $link = preg_replace(array('/^\{\{/','/\}\}$/u'),'',$match);
620
621 // Split title from URL
622 $link = explode('|',$link,2);
623
624
625 // Check alignment
626 $ralign = (bool)preg_match('/^ /',$link[0]);
627 $lalign = (bool)preg_match('/ $/',$link[0]);
628
629 // Logic = what's that ;)...
630 if ( $lalign & $ralign ) {
631 $align = 'center';
632 } else if ( $ralign ) {
633 $align = 'right';
634 } else if ( $lalign ) {
635 $align = 'left';
636 } else {
637 $align = NULL;
638 }
639
640 // The title...
641 if ( !isset($link[1]) ) {
642 $link[1] = NULL;
643 }
644
645 //remove aligning spaces
646 $link[0] = trim($link[0]);
647
648 //split into src and parameters (using the very last questionmark)
649 $pos = strrpos($link[0], '?');
650 if($pos !== false){
651 $src = substr($link[0],0,$pos);
652 $param = substr($link[0],$pos+1);
653 }else{
654 $src = $link[0];
655 $param = '';
656 }
657
658 //parse width and height
659 if(preg_match('#(\d+)(x(\d+))?#i',$param,$size)){
660 ($size[1]) ? $w = $size[1] : $w = NULL;
661 ($size[3]) ? $h = $size[3] : $h = NULL;
662 } else {
663 $w = NULL;
664 $h = NULL;
665 }
666
667 //get linking command
668 if(preg_match('/nolink/i',$param)){
669 $linking = 'nolink';
670 }else if(preg_match('/direct/i',$param)){
671 $linking = 'direct';
672 }else if(preg_match('/linkonly/i',$param)){
673 $linking = 'linkonly';
674 }else{
675 $linking = 'details';
676 }
677
678 //get caching command
679 if (preg_match('/(nocache|recache)/i',$param,$cachemode)){
680 $cache = $cachemode[1];
681 }else{
682 $cache = 'cache';
683 }
684
685 // Check whether this is a local or remote image
686 if ( preg_match('#^(https?|ftp)#i',$src) ) {
687 $call = 'externalmedia';
688 } else {
689 $call = 'internalmedia';
690 }
691
692 $params = array(
693 'type'=>$call,
694 'src'=>$src,
695 'title'=>$link[1],
696 'align'=>$align,
697 'width'=>$w,
698 'height'=>$h,
699 'cache'=>$cache,
700 'linking'=>$linking,
701 );
702
703 return $params;
704}
705
706//------------------------------------------------------------------------
707class Doku_Handler_CallWriter {
708
709 var $Handler;
710
711 function Doku_Handler_CallWriter(& $Handler) {
712 $this->Handler = & $Handler;
713 }
714
715 function writeCall($call) {
716 $this->Handler->calls[] = $call;
717 }
718
719 function writeCalls($calls) {
720 $this->Handler->calls = array_merge($this->Handler->calls, $calls);
721 }
722
723 // function is required, but since this call writer is first/highest in
724 // the chain it is not required to do anything
725 function finalise() {
726 unset($this->Handler);
727 }
728}
729
730//------------------------------------------------------------------------
731/**
732 * Generic call writer class to handle nesting of rendering instructions
733 * within a render instruction. Also see nest() method of renderer base class
734 *
735 * @author Chris Smith <[email protected]>
736 */
737class Doku_Handler_Nest {
738
739 var $CallWriter;
740 var $calls = array();
741
742 var $closingInstruction;
743
744 /**
745 * constructor
746 *
747 * @param object $CallWriter the renderers current call writer
748 * @param string $close closing instruction name, this is required to properly terminate the
749 * syntax mode if the document ends without a closing pattern
750 */
751 function Doku_Handler_Nest(& $CallWriter, $close="nest_close") {
752 $this->CallWriter = & $CallWriter;
753
754 $this->closingInstruction = $close;
755 }
756
757 function writeCall($call) {
758 $this->calls[] = $call;
759 }
760
761 function writeCalls($calls) {
762 $this->calls = array_merge($this->calls, $calls);
763 }
764
765 function finalise() {
766 $last_call = end($this->calls);
767 $this->writeCall(array($this->closingInstruction,array(), $last_call[2]));
768
769 $this->process();
770 $this->CallWriter->finalise();
771 unset($this->CallWriter);
772 }
773
774 function process() {
775 // merge consecutive cdata
776 $unmerged_calls = $this->calls;
777 $this->calls = array();
778
779 foreach ($unmerged_calls as $call) $this->addCall($call);
780
781 $first_call = reset($this->calls);
782 $this->CallWriter->writeCall(array("nest", array($this->calls), $first_call[2]));
783 }
784
785 function addCall($call) {
786 $key = count($this->calls);
787 if ($key and ($call[0] == 'cdata') and ($this->calls[$key-1][0] == 'cdata')) {
788 $this->calls[$key-1][1][0] .= $call[1][0];
789 } else if ($call[0] == 'eol') {
790 // do nothing (eol shouldn't be allowed, to counter preformatted fix in #1652 & #1699)
791 } else {
792 $this->calls[] = $call;
793 }
794 }
795}
796
797class Doku_Handler_List {
798
799 var $CallWriter;
800
801 var $calls = array();
802 var $listCalls = array();
803 var $listStack = array();
804
805 function Doku_Handler_List(& $CallWriter) {
806 $this->CallWriter = & $CallWriter;
807 }
808
809 function writeCall($call) {
810 $this->calls[] = $call;
811 }
812
813 // Probably not needed but just in case...
814 function writeCalls($calls) {
815 $this->calls = array_merge($this->calls, $calls);
816# $this->CallWriter->writeCalls($this->calls);
817 }
818
819 function finalise() {
820 $last_call = end($this->calls);
821 $this->writeCall(array('list_close',array(), $last_call[2]));
822
823 $this->process();
824 $this->CallWriter->finalise();
825 unset($this->CallWriter);
826 }
827
828 //------------------------------------------------------------------------
829 function process() {
830
831 foreach ( $this->calls as $call ) {
832 switch ($call[0]) {
833 case 'list_item':
834 $this->listOpen($call);
835 break;
836 case 'list_open':
837 $this->listStart($call);
838 break;
839 case 'list_close':
840 $this->listEnd($call);
841 break;
842 default:
843 $this->listContent($call);
844 break;
845 }
846 }
847
848 $this->CallWriter->writeCalls($this->listCalls);
849 }
850
851 //------------------------------------------------------------------------
852 function listStart($call) {
853 $depth = $this->interpretSyntax($call[1][0], $listType);
854
855 $this->initialDepth = $depth;
856 $this->listStack[] = array($listType, $depth);
857
858 $this->listCalls[] = array('list'.$listType.'_open',array(),$call[2]);
859 $this->listCalls[] = array('listitem_open',array(1),$call[2]);
860 $this->listCalls[] = array('listcontent_open',array(),$call[2]);
861 }
862
863 //------------------------------------------------------------------------
864 function listEnd($call) {
865 $closeContent = true;
866
867 while ( $list = array_pop($this->listStack) ) {
868 if ( $closeContent ) {
869 $this->listCalls[] = array('listcontent_close',array(),$call[2]);
870 $closeContent = false;
871 }
872 $this->listCalls[] = array('listitem_close',array(),$call[2]);
873 $this->listCalls[] = array('list'.$list[0].'_close', array(), $call[2]);
874 }
875 }
876
877 //------------------------------------------------------------------------
878 function listOpen($call) {
879 $depth = $this->interpretSyntax($call[1][0], $listType);
880 $end = end($this->listStack);
881
882 // Not allowed to be shallower than initialDepth
883 if ( $depth < $this->initialDepth ) {
884 $depth = $this->initialDepth;
885 }
886
887 //------------------------------------------------------------------------
888 if ( $depth == $end[1] ) {
889
890 // Just another item in the list...
891 if ( $listType == $end[0] ) {
892 $this->listCalls[] = array('listcontent_close',array(),$call[2]);
893 $this->listCalls[] = array('listitem_close',array(),$call[2]);
894 $this->listCalls[] = array('listitem_open',array($depth-1),$call[2]);
895 $this->listCalls[] = array('listcontent_open',array(),$call[2]);
896
897 // Switched list type...
898 } else {
899
900 $this->listCalls[] = array('listcontent_close',array(),$call[2]);
901 $this->listCalls[] = array('listitem_close',array(),$call[2]);
902 $this->listCalls[] = array('list'.$end[0].'_close', array(), $call[2]);
903 $this->listCalls[] = array('list'.$listType.'_open', array(), $call[2]);
904 $this->listCalls[] = array('listitem_open', array($depth-1), $call[2]);
905 $this->listCalls[] = array('listcontent_open',array(),$call[2]);
906
907 array_pop($this->listStack);
908 $this->listStack[] = array($listType, $depth);
909 }
910
911 //------------------------------------------------------------------------
912 // Getting deeper...
913 } else if ( $depth > $end[1] ) {
914
915 $this->listCalls[] = array('listcontent_close',array(),$call[2]);
916 $this->listCalls[] = array('list'.$listType.'_open', array(), $call[2]);
917 $this->listCalls[] = array('listitem_open', array($depth-1), $call[2]);
918 $this->listCalls[] = array('listcontent_open',array(),$call[2]);
919
920 $this->listStack[] = array($listType, $depth);
921
922 //------------------------------------------------------------------------
923 // Getting shallower ( $depth < $end[1] )
924 } else {
925 $this->listCalls[] = array('listcontent_close',array(),$call[2]);
926 $this->listCalls[] = array('listitem_close',array(),$call[2]);
927 $this->listCalls[] = array('list'.$end[0].'_close',array(),$call[2]);
928
929 // Throw away the end - done
930 array_pop($this->listStack);
931
932 while (1) {
933 $end = end($this->listStack);
934
935 if ( $end[1] <= $depth ) {
936
937 // Normalize depths
938 $depth = $end[1];
939
940 $this->listCalls[] = array('listitem_close',array(),$call[2]);
941
942 if ( $end[0] == $listType ) {
943 $this->listCalls[] = array('listitem_open',array($depth-1),$call[2]);
944 $this->listCalls[] = array('listcontent_open',array(),$call[2]);
945
946 } else {
947 // Switching list type...
948 $this->listCalls[] = array('list'.$end[0].'_close', array(), $call[2]);
949 $this->listCalls[] = array('list'.$listType.'_open', array(), $call[2]);
950 $this->listCalls[] = array('listitem_open', array($depth-1), $call[2]);
951 $this->listCalls[] = array('listcontent_open',array(),$call[2]);
952
953 array_pop($this->listStack);
954 $this->listStack[] = array($listType, $depth);
955 }
956
957 break;
958
959 // Haven't dropped down far enough yet.... ( $end[1] > $depth )
960 } else {
961
962 $this->listCalls[] = array('listitem_close',array(),$call[2]);
963 $this->listCalls[] = array('list'.$end[0].'_close',array(),$call[2]);
964
965 array_pop($this->listStack);
966
967 }
968
969 }
970
971 }
972 }
973
974 //------------------------------------------------------------------------
975 function listContent($call) {
976 $this->listCalls[] = $call;
977 }
978
979 //------------------------------------------------------------------------
980 function interpretSyntax($match, & $type) {
981 if ( substr($match,-1) == '*' ) {
982 $type = 'u';
983 } else {
984 $type = 'o';
985 }
986 // Is the +1 needed? It used to be count(explode(...))
987 // but I don't think the number is seen outside this handler
988 return substr_count(str_replace("\t",' ',$match), ' ') + 1;
989 }
990}
991
992//------------------------------------------------------------------------
993class Doku_Handler_Preformatted {
994
995 var $CallWriter;
996
997 var $calls = array();
998 var $pos;
999 var $text ='';
1000
1001
1002
1003 function Doku_Handler_Preformatted(& $CallWriter) {
1004 $this->CallWriter = & $CallWriter;
1005 }
1006
1007 function writeCall($call) {
1008 $this->calls[] = $call;
1009 }
1010
1011 // Probably not needed but just in case...
1012 function writeCalls($calls) {
1013 $this->calls = array_merge($this->calls, $calls);
1014# $this->CallWriter->writeCalls($this->calls);
1015 }
1016
1017 function finalise() {
1018 $last_call = end($this->calls);
1019 $this->writeCall(array('preformatted_end',array(), $last_call[2]));
1020
1021 $this->process();
1022 $this->CallWriter->finalise();
1023 unset($this->CallWriter);
1024 }
1025
1026 function process() {
1027 foreach ( $this->calls as $call ) {
1028 switch ($call[0]) {
1029 case 'preformatted_start':
1030 $this->pos = $call[2];
1031 break;
1032 case 'preformatted_newline':
1033 $this->text .= "\n";
1034 break;
1035 case 'preformatted_content':
1036 $this->text .= $call[1][0];
1037 break;
1038 case 'preformatted_end':
1039 if (trim($this->text)) {
1040 $this->CallWriter->writeCall(array('preformatted',array($this->text),$this->pos));
1041 }
1042 // see FS#1699 & FS#1652, add 'eol' instructions to ensure proper triggering of following p_open
1043 $this->CallWriter->writeCall(array('eol',array(),$this->pos));
1044 $this->CallWriter->writeCall(array('eol',array(),$this->pos));
1045 break;
1046 }
1047 }
1048 }
1049
1050}
1051
1052//------------------------------------------------------------------------
1053class Doku_Handler_Quote {
1054
1055 var $CallWriter;
1056
1057 var $calls = array();
1058
1059 var $quoteCalls = array();
1060
1061 function Doku_Handler_Quote(& $CallWriter) {
1062 $this->CallWriter = & $CallWriter;
1063 }
1064
1065 function writeCall($call) {
1066 $this->calls[] = $call;
1067 }
1068
1069 // Probably not needed but just in case...
1070 function writeCalls($calls) {
1071 $this->calls = array_merge($this->calls, $calls);
1072 }
1073
1074 function finalise() {
1075 $last_call = end($this->calls);
1076 $this->writeCall(array('quote_end',array(), $last_call[2]));
1077
1078 $this->process();
1079 $this->CallWriter->finalise();
1080 unset($this->CallWriter);
1081 }
1082
1083 function process() {
1084
1085 $quoteDepth = 1;
1086
1087 foreach ( $this->calls as $call ) {
1088 switch ($call[0]) {
1089
1090 case 'quote_start':
1091
1092 $this->quoteCalls[] = array('quote_open',array(),$call[2]);
1093
1094 case 'quote_newline':
1095
1096 $quoteLength = $this->getDepth($call[1][0]);
1097
1098 if ( $quoteLength > $quoteDepth ) {
1099 $quoteDiff = $quoteLength - $quoteDepth;
1100 for ( $i = 1; $i <= $quoteDiff; $i++ ) {
1101 $this->quoteCalls[] = array('quote_open',array(),$call[2]);
1102 }
1103 } else if ( $quoteLength < $quoteDepth ) {
1104 $quoteDiff = $quoteDepth - $quoteLength;
1105 for ( $i = 1; $i <= $quoteDiff; $i++ ) {
1106 $this->quoteCalls[] = array('quote_close',array(),$call[2]);
1107 }
1108 } else {
1109 if ($call[0] != 'quote_start') $this->quoteCalls[] = array('linebreak',array(),$call[2]);
1110 }
1111
1112 $quoteDepth = $quoteLength;
1113
1114 break;
1115
1116 case 'quote_end':
1117
1118 if ( $quoteDepth > 1 ) {
1119 $quoteDiff = $quoteDepth - 1;
1120 for ( $i = 1; $i <= $quoteDiff; $i++ ) {
1121 $this->quoteCalls[] = array('quote_close',array(),$call[2]);
1122 }
1123 }
1124
1125 $this->quoteCalls[] = array('quote_close',array(),$call[2]);
1126
1127 $this->CallWriter->writeCalls($this->quoteCalls);
1128 break;
1129
1130 default:
1131 $this->quoteCalls[] = $call;
1132 break;
1133 }
1134 }
1135 }
1136
1137 function getDepth($marker) {
1138 preg_match('/>{1,}/', $marker, $matches);
1139 $quoteLength = strlen($matches[0]);
1140 return $quoteLength;
1141 }
1142}
1143
1144//------------------------------------------------------------------------
1145class Doku_Handler_Table {
1146
1147 var $CallWriter;
1148
1149 var $calls = array();
1150 var $tableCalls = array();
1151 var $maxCols = 0;
1152 var $maxRows = 1;
1153 var $currentCols = 0;
1154 var $firstCell = false;
1155 var $lastCellType = 'tablecell';
1156
1157 function Doku_Handler_Table(& $CallWriter) {
1158 $this->CallWriter = & $CallWriter;
1159 }
1160
1161 function writeCall($call) {
1162 $this->calls[] = $call;
1163 }
1164
1165 // Probably not needed but just in case...
1166 function writeCalls($calls) {
1167 $this->calls = array_merge($this->calls, $calls);
1168 }
1169
1170 function finalise() {
1171 $last_call = end($this->calls);
1172 $this->writeCall(array('table_end',array(), $last_call[2]));
1173
1174 $this->process();
1175 $this->CallWriter->finalise();
1176 unset($this->CallWriter);
1177 }
1178
1179 //------------------------------------------------------------------------
1180 function process() {
1181 foreach ( $this->calls as $call ) {
1182 switch ( $call[0] ) {
1183 case 'table_start':
1184 $this->tableStart($call);
1185 break;
1186 case 'table_row':
1187 $this->tableRowClose($call);
1188 $this->tableRowOpen(array('tablerow_open',$call[1],$call[2]));
1189 break;
1190 case 'tableheader':
1191 case 'tablecell':
1192 $this->tableCell($call);
1193 break;
1194 case 'table_end':
1195 $this->tableRowClose($call);
1196 $this->tableEnd($call);
1197 break;
1198 default:
1199 $this->tableDefault($call);
1200 break;
1201 }
1202 }
1203 $this->CallWriter->writeCalls($this->tableCalls);
1204 }
1205
1206 function tableStart($call) {
1207 $this->tableCalls[] = array('table_open',$call[1],$call[2]);
1208 $this->tableCalls[] = array('tablerow_open',array(),$call[2]);
1209 $this->firstCell = true;
1210 }
1211
1212 function tableEnd($call) {
1213 $this->tableCalls[] = array('table_close',$call[1],$call[2]);
1214 $this->finalizeTable();
1215 }
1216
1217 function tableRowOpen($call) {
1218 $this->tableCalls[] = $call;
1219 $this->currentCols = 0;
1220 $this->firstCell = true;
1221 $this->lastCellType = 'tablecell';
1222 $this->maxRows++;
1223 }
1224
1225 function tableRowClose($call) {
1226 // Strip off final cell opening and anything after it
1227 while ( $discard = array_pop($this->tableCalls ) ) {
1228
1229 if ( $discard[0] == 'tablecell_open' || $discard[0] == 'tableheader_open') {
1230 break;
1231 }
1232 }
1233 $this->tableCalls[] = array('tablerow_close', array(), $call[2]);
1234
1235 if ( $this->currentCols > $this->maxCols ) {
1236 $this->maxCols = $this->currentCols;
1237 }
1238 }
1239
1240 function tableCell($call) {
1241 if ( !$this->firstCell ) {
1242
1243 // Increase the span
1244 $lastCall = end($this->tableCalls);
1245
1246 // A cell call which follows an open cell means an empty cell so span
1247 if ( $lastCall[0] == 'tablecell_open' || $lastCall[0] == 'tableheader_open' ) {
1248 $this->tableCalls[] = array('colspan',array(),$call[2]);
1249
1250 }
1251
1252 $this->tableCalls[] = array($this->lastCellType.'_close',array(),$call[2]);
1253 $this->tableCalls[] = array($call[0].'_open',array(1,NULL,1),$call[2]);
1254 $this->lastCellType = $call[0];
1255
1256 } else {
1257
1258 $this->tableCalls[] = array($call[0].'_open',array(1,NULL,1),$call[2]);
1259 $this->lastCellType = $call[0];
1260 $this->firstCell = false;
1261
1262 }
1263
1264 $this->currentCols++;
1265 }
1266
1267 function tableDefault($call) {
1268 $this->tableCalls[] = $call;
1269 }
1270
1271 function finalizeTable() {
1272
1273 // Add the max cols and rows to the table opening
1274 if ( $this->tableCalls[0][0] == 'table_open' ) {
1275 // Adjust to num cols not num col delimeters
1276 $this->tableCalls[0][1][] = $this->maxCols - 1;
1277 $this->tableCalls[0][1][] = $this->maxRows;
1278 $this->tableCalls[0][1][] = array_shift($this->tableCalls[0][1]);
1279 } else {
1280 trigger_error('First element in table call list is not table_open');
1281 }
1282
1283 $lastRow = 0;
1284 $lastCell = 0;
1285 $cellKey = array();
1286 $toDelete = array();
1287
1288 // Look for the colspan elements and increment the colspan on the
1289 // previous non-empty opening cell. Once done, delete all the cells
1290 // that contain colspans
1291 for ($key = 0 ; $key < count($this->tableCalls) ; ++$key) {
1292 $call = $this->tableCalls[$key];
1293
1294 switch ($call[0]) {
1295 case 'tablerow_open':
1296
1297 $lastRow++;
1298 $lastCell = 0;
1299 break;
1300
1301 case 'tablecell_open':
1302 case 'tableheader_open':
1303
1304 $lastCell++;
1305 $cellKey[$lastRow][$lastCell] = $key;
1306 break;
1307
1308 case 'table_align':
1309
1310 $prev = in_array($this->tableCalls[$key-1][0], array('tablecell_open', 'tableheader_open'));
1311 $next = in_array($this->tableCalls[$key+1][0], array('tablecell_close', 'tableheader_close'));
1312 // If the cell is empty, align left
1313 if ($prev && $next) {
1314 $this->tableCalls[$key-1][1][1] = 'left';
1315
1316 // If the previous element was a cell open, align right
1317 } elseif ($prev) {
1318 $this->tableCalls[$key-1][1][1] = 'right';
1319
1320 // If the next element is the close of an element, align either center or left
1321 } elseif ( $next) {
1322 if ( $this->tableCalls[$cellKey[$lastRow][$lastCell]][1][1] == 'right' ) {
1323 $this->tableCalls[$cellKey[$lastRow][$lastCell]][1][1] = 'center';
1324 } else {
1325 $this->tableCalls[$cellKey[$lastRow][$lastCell]][1][1] = 'left';
1326 }
1327
1328 }
1329
1330 // Now convert the whitespace back to cdata
1331 $this->tableCalls[$key][0] = 'cdata';
1332 break;
1333
1334 case 'colspan':
1335
1336 $this->tableCalls[$key-1][1][0] = false;
1337
1338 for($i = $key-2; $i >= $cellKey[$lastRow][1]; $i--) {
1339
1340 if ( $this->tableCalls[$i][0] == 'tablecell_open' || $this->tableCalls[$i][0] == 'tableheader_open' ) {
1341
1342 if ( false !== $this->tableCalls[$i][1][0] ) {
1343 $this->tableCalls[$i][1][0]++;
1344 break;
1345 }
1346
1347
1348 }
1349 }
1350
1351 $toDelete[] = $key-1;
1352 $toDelete[] = $key;
1353 $toDelete[] = $key+1;
1354 break;
1355
1356 case 'rowspan':
1357
1358 if ( $this->tableCalls[$key-1][0] == 'cdata' ) {
1359 // ignore rowspan if previous call was cdata (text mixed with :::) we don't have to check next call as that wont match regex
1360 $this->tableCalls[$key][0] = 'cdata';
1361
1362 } else {
1363
1364 $spanning_cell = null;
1365 for($i = $lastRow-1; $i > 0; $i--) {
1366
1367 if ( $this->tableCalls[$cellKey[$i][$lastCell]][0] == 'tablecell_open' || $this->tableCalls[$cellKey[$i][$lastCell]][0] == 'tableheader_open' ) {
1368
1369 if ($this->tableCalls[$cellKey[$i][$lastCell]][1][2] >= $lastRow - $i) {
1370 $spanning_cell = $i;
1371 break;
1372 }
1373
1374
1375 }
1376 }
1377 if (is_null($spanning_cell)) {
1378 // No spanning cell found, so convert this cell to
1379 // an empty one to avoid broken tables
1380 $this->tableCells[$key][1][1] = '';
1381 continue;
1382 }
1383 $this->tableCalls[$cellKey[$spanning_cell][$lastCell]][1][2]++;
1384
1385 $this->tableCalls[$key-1][1][2] = false;
1386
1387 $toDelete[] = $key-1;
1388 $toDelete[] = $key;
1389 $toDelete[] = $key+1;
1390 }
1391 break;
1392
1393 case 'tablerow_close':
1394
1395 // Fix broken tables by adding missing cells
1396 while (++$lastCell < $this->maxCols) {
1397 array_splice($this->tableCalls, $key, 0, array(
1398 array('tablecell_open', array(1, null, 1), $call[2]),
1399 array('cdata', array(''), $call[2]),
1400 array('tablecell_close', array(), $call[2])));
1401 $key += 3;
1402 }
1403
1404 break;
1405
1406 }
1407 }
1408
1409
1410 // condense cdata
1411 $cnt = count($this->tableCalls);
1412 for( $key = 0; $key < $cnt; $key++){
1413 if($this->tableCalls[$key][0] == 'cdata'){
1414 $ckey = $key;
1415 $key++;
1416 while($this->tableCalls[$key][0] == 'cdata'){
1417 $this->tableCalls[$ckey][1][0] .= $this->tableCalls[$key][1][0];
1418 $toDelete[] = $key;
1419 $key++;
1420 }
1421 continue;
1422 }
1423 }
1424
1425 foreach ( $toDelete as $delete ) {
1426 unset($this->tableCalls[$delete]);
1427 }
1428 $this->tableCalls = array_values($this->tableCalls);
1429 }
1430}
1431
1432
1433/**
1434 * Handler for paragraphs
1435 *
1436 * @author Harry Fuecks <[email protected]>
1437 */
1438class Doku_Handler_Block {
1439 var $calls = array();
1440 var $skipEol = false;
1441
1442 // Blocks these should not be inside paragraphs
1443 var $blockOpen = array(
1444 'header',
1445 'listu_open','listo_open','listitem_open','listcontent_open',
1446 'table_open','tablerow_open','tablecell_open','tableheader_open',
1447 'quote_open',
1448 'code','file','hr','preformatted','rss',
1449 'htmlblock','phpblock',
1450 'footnote_open',
1451 );
1452
1453 var $blockClose = array(
1454 'header',
1455 'listu_close','listo_close','listitem_close','listcontent_close',
1456 'table_close','tablerow_close','tablecell_close','tableheader_close',
1457 'quote_close',
1458 'code','file','hr','preformatted','rss',
1459 'htmlblock','phpblock',
1460 'footnote_close',
1461 );
1462
1463 // Stacks can contain paragraphs
1464 var $stackOpen = array(
1465 'section_open',
1466 );
1467
1468 var $stackClose = array(
1469 'section_close',
1470 );
1471
1472
1473 /**
1474 * Constructor. Adds loaded syntax plugins to the block and stack
1475 * arrays
1476 *
1477 * @author Andreas Gohr <[email protected]>
1478 */
1479 function Doku_Handler_Block(){
1480 global $DOKU_PLUGINS;
1481 //check if syntax plugins were loaded
1482 if(empty($DOKU_PLUGINS['syntax'])) return;
1483 foreach($DOKU_PLUGINS['syntax'] as $n => $p){
1484 $ptype = $p->getPType();
1485 if($ptype == 'block'){
1486 $this->blockOpen[] = 'plugin_'.$n;
1487 $this->blockClose[] = 'plugin_'.$n;
1488 }elseif($ptype == 'stack'){
1489 $this->stackOpen[] = 'plugin_'.$n;
1490 $this->stackClose[] = 'plugin_'.$n;
1491 }
1492 }
1493 }
1494
1495 function openParagraph($pos){
1496 if ($this->inParagraph) return;
1497 $this->calls[] = array('p_open',array(), $pos);
1498 $this->inParagraph = true;
1499 $this->skipEol = true;
1500 }
1501
1502 /**
1503 * Close a paragraph if needed
1504 *
1505 * This function makes sure there are no empty paragraphs on the stack
1506 *
1507 * @author Andreas Gohr <[email protected]>
1508 */
1509 function closeParagraph($pos){
1510 if (!$this->inParagraph) return;
1511 // look back if there was any content - we don't want empty paragraphs
1512 $content = '';
1513 for($i=count($this->calls)-1; $i>=0; $i--){
1514 if($this->calls[$i][0] == 'p_open'){
1515 break;
1516 }elseif($this->calls[$i][0] == 'cdata'){
1517 $content .= $this->calls[$i][1][0];
1518 }else{
1519 $content = 'found markup';
1520 break;
1521 }
1522 }
1523
1524 if(trim($content)==''){
1525 //remove the whole paragraph
1526 array_splice($this->calls,$i);
1527 }else{
1528 // remove ending linebreaks in the paragraph
1529 $i=count($this->calls)-1;
1530 if ($this->calls[$i][0] == 'cdata') $this->calls[$i][1][0] = rtrim($this->calls[$i][1][0],DOKU_PARSER_EOL);
1531 $this->calls[] = array('p_close',array(), $pos);
1532 }
1533
1534 $this->inParagraph = false;
1535 $this->skipEol = true;
1536 }
1537
1538 function addCall($call) {
1539 $key = count($this->calls);
1540 if ($key and ($call[0] == 'cdata') and ($this->calls[$key-1][0] == 'cdata')) {
1541 $this->calls[$key-1][1][0] .= $call[1][0];
1542 } else {
1543 $this->calls[] = $call;
1544 }
1545 }
1546
1547 // simple version of addCall, without checking cdata
1548 function storeCall($call) {
1549 $this->calls[] = $call;
1550 }
1551
1552 /**
1553 * Processes the whole instruction stack to open and close paragraphs
1554 *
1555 * @author Harry Fuecks <[email protected]>
1556 * @author Andreas Gohr <[email protected]>
1557 */
1558 function process($calls) {
1559 // open first paragraph
1560 $this->openParagraph(0);
1561 foreach ( $calls as $key => $call ) {
1562 $cname = $call[0];
1563 if ($cname == 'plugin') {
1564 $cname='plugin_'.$call[1][0];
1565 $plugin = true;
1566 $plugin_open = (($call[1][2] == DOKU_LEXER_ENTER) || ($call[1][2] == DOKU_LEXER_SPECIAL));
1567 $plugin_close = (($call[1][2] == DOKU_LEXER_EXIT) || ($call[1][2] == DOKU_LEXER_SPECIAL));
1568 } else {
1569 $plugin = false;
1570 }
1571 /* stack */
1572 if ( in_array($cname,$this->stackClose ) && (!$plugin || $plugin_close)) {
1573 $this->closeParagraph($call[2]);
1574 $this->storeCall($call);
1575 $this->openParagraph($call[2]);
1576 continue;
1577 }
1578 if ( in_array($cname,$this->stackOpen ) && (!$plugin || $plugin_open) ) {
1579 $this->closeParagraph($call[2]);
1580 $this->storeCall($call);
1581 $this->openParagraph($call[2]);
1582 continue;
1583 }
1584 /* block */
1585 // If it's a substition it opens and closes at the same call.
1586 // To make sure next paragraph is correctly started, let close go first.
1587 if ( in_array($cname, $this->blockClose) && (!$plugin || $plugin_close)) {
1588 $this->closeParagraph($call[2]);
1589 $this->storeCall($call);
1590 $this->openParagraph($call[2]);
1591 continue;
1592 }
1593 if ( in_array($cname, $this->blockOpen) && (!$plugin || $plugin_open)) {
1594 $this->closeParagraph($call[2]);
1595 $this->storeCall($call);
1596 continue;
1597 }
1598 /* eol */
1599 if ( $cname == 'eol' ) {
1600 // Check this isn't an eol instruction to skip...
1601 if ( !$this->skipEol ) {
1602 // Next is EOL => double eol => mark as paragraph
1603 if ( isset($calls[$key+1]) && $calls[$key+1][0] == 'eol' ) {
1604 $this->closeParagraph($call[2]);
1605 $this->openParagraph($call[2]);
1606 } else {
1607 //if this is just a single eol make a space from it
1608 $this->addCall(array('cdata',array(DOKU_PARSER_EOL), $call[2]));
1609 }
1610 }
1611 continue;
1612 }
1613 /* normal */
1614 $this->addCall($call);
1615 $this->skipEol = false;
1616 }
1617 // close last paragraph
1618 $call = end($this->calls);
1619 $this->closeParagraph($call[2]);
1620 return $this->calls;
1621 }
1622}
1623
1624//Setup VIM: ex: et ts=4 :
Note: See TracBrowser for help on using the repository browser.