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