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