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