[ Index ]
 

Code source de DokuWiki 2006-11-06

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

/inc/parser/ -> handler.php (source)

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


Généré le : Tue Apr 3 20:47:31 2007 par Balluche grâce à PHPXref 0.7