1<?php 2/** 3 * Renderer for XHTML output 4 * 5 * @author Harry Fuecks <hfuecks@gmail.com> 6 * @author Andreas Gohr <andi@splitbrain.org> 7 */ 8 9if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); 10 11if ( !defined('DOKU_LF') ) { 12 // Some whitespace to help View > Source 13 define ('DOKU_LF',"\n"); 14} 15 16if ( !defined('DOKU_TAB') ) { 17 // Some whitespace to help View > Source 18 define ('DOKU_TAB',"\t"); 19} 20 21require_once DOKU_INC . 'inc/parser/renderer.php'; 22require_once DOKU_INC . 'inc/pluginutils.php'; 23 24/** 25 * The Renderer 26 */ 27class Doku_Renderer_xhtml extends Doku_Renderer { 28 29 var $doc = ''; 30 31 var $headers = array(); 32 33 var $footnotes = array(); 34 35 var $acronyms = array(); 36 var $smileys = array(); 37 var $badwords = array(); 38 var $entities = array(); 39 var $interwiki = array(); 40 41 var $lastsec = 0; 42 43 var $store = ''; 44 45 function document_start() { 46 } 47 48 function document_end() { 49 // add button for last section if any and more than one 50 if($this->lastsec > 1) $this->_secedit($this->lastsec,''); 51 52 if ( count ($this->footnotes) > 0 ) { 53 $this->doc .= '<div class="footnotes">'.DOKU_LF; 54 foreach ( $this->footnotes as $footnote ) { 55 $this->doc .= $footnote; 56 } 57 $this->doc .= '</div>'.DOKU_LF; 58 } 59 } 60 61 //handles plugin rendering 62 function plugin($name,$data){ 63 $plugin = null; 64 if(plugin_load('syntax',$name,$plugin)){ 65 $plugin->render('xhtml',$this,$data); 66 } 67 } 68 69 function toc_open() { 70 global $lang; 71 $this->doc .= '<div class="toc">'.DOKU_LF; 72 $this->doc .= '<div class="tocheader">'; 73/* The (+/-) version will work too but needs some CSS tweaking to look fine */ 74/* $this->doc .= ' <script type="text/javascript">showTocToggle("+","-")';*/ 75 $this->doc .= '<script type="text/javascript">showTocToggle('; 76 $this->doc .= '\'<img src="'.DOKU_BASE.'lib/images/arrow_down.gif">\','; 77 $this->doc .= '\'<img src="'.DOKU_BASE.'lib/images/arrow_up.gif">\')'; 78 $this->doc .= '</script>'; 79 $this->doc .= $lang['toc']; 80 $this->doc .= '</div>'.DOKU_LF; 81 $this->doc .= '<div id="tocinside">'.DOKU_LF; 82 } 83 84 function tocbranch_open($level) { 85 $this->doc .= '<ul class="toc">'.DOKU_LF; 86 } 87 88 function tocitem_open($level, $empty = FALSE) { 89 if ( !$empty ) { 90 $this->doc .= '<li class="level'.$level.'">'; 91 } else { 92 $this->doc .= '<li class="clear">'; 93 } 94 } 95 96 function tocelement($level, $title) { 97 $this->doc .= '<span class="li"><a href="#'.$this->_headerToLink($title).'" class="toc">'; 98 $this->doc .= $this->_xmlEntities($title); 99 $this->doc .= '</a></span>'; 100 } 101 102 function tocitem_close($level) { 103 $this->doc .= '</li>'.DOKU_LF; 104 } 105 106 function tocbranch_close($level) { 107 $this->doc .= '</ul>'.DOKU_LF; 108 } 109 110 function toc_close() { 111 $this->doc .= '</div>'.DOKU_LF.'</div>'.DOKU_LF; 112 } 113 114 function header($text, $level, $pos) { 115 global $conf; 116 //handle section editing 117 if($level <= $conf['maxseclevel']){ 118 // add button for last section if any 119 if($this->lastsec) $this->_secedit($this->lastsec,$pos-1); 120 // remember current position 121 $this->lastsec = $pos; 122 } 123 124 $this->doc .= DOKU_LF.'<a name="'.$this->_headerToLink($text).'"></a><h'.$level.'>'; 125 $this->doc .= $this->_xmlEntities($text); 126 $this->doc .= "</h$level>".DOKU_LF; 127 } 128 129 function section_open($level) { 130 $this->doc .= "<div class=\"level$level\">".DOKU_LF; 131 } 132 133 function section_close() { 134 $this->doc .= DOKU_LF.'</div>'.DOKU_LF; 135 } 136 137 function cdata($text) { 138 $this->doc .= $this->_xmlEntities($text); 139 } 140 141 function p_open() { 142 $this->doc .= DOKU_LF.'<p>'.DOKU_LF; 143 } 144 145 function p_close() { 146 $this->doc .= DOKU_LF.'</p>'.DOKU_LF; 147 } 148 149 function linebreak() { 150 $this->doc .= '<br/>'.DOKU_LF; 151 } 152 153 function hr() { 154 $this->doc .= '<hr noshade="noshade" size="1" />'.DOKU_LF; 155 } 156 157 function strong_open() { 158 $this->doc .= '<strong>'; 159 } 160 161 function strong_close() { 162 $this->doc .= '</strong>'; 163 } 164 165 function emphasis_open() { 166 $this->doc .= '<em>'; 167 } 168 169 function emphasis_close() { 170 $this->doc .= '</em>'; 171 } 172 173 function underline_open() { 174 $this->doc .= '<u>'; 175 } 176 177 function underline_close() { 178 $this->doc .= '</u>'; 179 } 180 181 function monospace_open() { 182 $this->doc .= '<code>'; 183 } 184 185 function monospace_close() { 186 $this->doc .= '</code>'; 187 } 188 189 function subscript_open() { 190 $this->doc .= '<sub>'; 191 } 192 193 function subscript_close() { 194 $this->doc .= '</sub>'; 195 } 196 197 function superscript_open() { 198 $this->doc .= '<sup>'; 199 } 200 201 function superscript_close() { 202 $this->doc .= '</sup>'; 203 } 204 205 function deleted_open() { 206 $this->doc .= '<del>'; 207 } 208 209 function deleted_close() { 210 $this->doc .= '</del>'; 211 } 212 213 /** 214 * Callback for footnote start syntax 215 * 216 * All following content will go to the footnote instead of 217 * the document. To achive this the previous rendered content 218 * is moved to $store and $doc is cleared 219 * 220 * @author Andreas Gohr <andi@splitbrain.org> 221 */ 222 function footnote_open() { 223 #$id = $this->_newFootnoteId(); 224 $id = count($this->footnotes)+1; 225 $this->doc .= '<a href="#fn'.$id.'" name="fnt'.$id.'" class="fn_top">'.$id.')</a>'; 226 227 // move current content to store and record footnote 228 $this->store = $this->doc; 229 $this->doc = ''; 230 231 $this->doc .= '<div class="fn">'; 232 $this->doc .= '<a href="#fnt'.$id.'" name="fn'.$id.'" class="fn_bot">'; 233 $this->doc .= $id.')</a> '.DOKU_LF; 234 } 235 236 /** 237 * Callback for footnote end syntax 238 * 239 * All rendered content is moved to the $footnotes array and the old 240 * content is restored from $store again 241 * 242 * @author Andreas Gohr 243 */ 244 function footnote_close() { 245 $this->doc .= '</div>' . DOKU_LF; 246 247 // put recorded footnote into the stack and restore old content 248 $this->footnotes[count($this->footnotes)] = $this->doc; 249 $this->doc = $this->store; 250 $this->store = ''; 251 } 252 253 function listu_open() { 254 $this->doc .= '<ul>'.DOKU_LF; 255 } 256 257 function listu_close() { 258 $this->doc .= '</ul>'.DOKU_LF; 259 } 260 261 function listo_open() { 262 $this->doc .= '<ol>'.DOKU_LF; 263 } 264 265 function listo_close() { 266 $this->doc .= '</ol>'.DOKU_LF; 267 } 268 269 function listitem_open($level) { 270 $this->doc .= '<li class="level'.$level.'">'; 271 } 272 273 function listitem_close() { 274 $this->doc .= '</li>'.DOKU_LF; 275 } 276 277 function listcontent_open() { 278 $this->doc .= '<span class="li">'; 279 } 280 281 function listcontent_close() { 282 $this->doc .= '</span>'.DOKU_LF; 283 } 284 285 function unformatted($text) { 286 $this->doc .= $this->_xmlEntities($text); 287 } 288 289 /** 290 * Execute PHP code if allowed 291 * 292 * @author Andreas Gohr <andi@splitbrain.org> 293 */ 294 function php($text) { 295 global $conf; 296 if($conf['phpok']){ 297 ob_start(); 298 eval($text); 299 $this->doc .= ob_get_contents(); 300 ob_end_clean(); 301 }else{ 302 $this->file($text); 303 } 304 } 305 306 /** 307 * Insert HTML if allowed 308 * 309 * @author Andreas Gohr <andi@splitbrain.org> 310 */ 311 function html($text) { 312 global $conf; 313 if($conf['htmlok']){ 314 $this->doc .= $text; 315 }else{ 316 $this->file($text); 317 } 318 } 319 320 function preformatted($text) { 321 $this->doc .= '<pre class="code">' . $this->_xmlEntities($text) . '</pre>'. DOKU_LF; 322 } 323 324 function file($text) { 325 $this->doc .= '<pre class="file">' . $this->_xmlEntities($text). '</pre>'. DOKU_LF; 326 } 327 328 function quote_open() { 329 $this->doc .= '<blockquote>'.DOKU_LF; 330 } 331 332 function quote_close() { 333 $this->doc .= '</blockquote>'.DOKU_LF; 334 } 335 336 /** 337 * Callback for code text 338 * 339 * Uses GeSHi to highlight language syntax 340 * 341 * @author Andreas Gohr <andi@splitbrain.org> 342 */ 343 function code($text, $language = NULL) { 344 global $conf; 345 346 if ( is_null($language) ) { 347 $this->preformatted($text); 348 } else { 349 //strip leading blank line 350 $text = preg_replace('/^\s*?\n/','',$text); 351 // Handle with Geshi here 352 require_once(DOKU_INC . 'inc/geshi.php'); 353 $geshi = new GeSHi($text, strtolower($language), DOKU_INC . 'inc/geshi'); 354 $geshi->set_encoding('utf-8'); 355 $geshi->enable_classes(); 356 $geshi->set_header_type(GESHI_HEADER_PRE); 357 $geshi->set_overall_class('code'); 358 $geshi->set_link_target($conf['target']['extern']); 359 360 $text = $geshi->parse_code(); 361 $this->doc .= $text; 362 } 363 } 364 365 function acronym($acronym) { 366 367 if ( array_key_exists($acronym, $this->acronyms) ) { 368 369 $title = $this->_xmlEntities($this->acronyms[$acronym]); 370 371 $this->doc .= '<acronym title="'.$title 372 .'">'.$this->_xmlEntities($acronym).'</acronym>'; 373 374 } else { 375 $this->doc .= $this->_xmlEntities($acronym); 376 } 377 } 378 379 function smiley($smiley) { 380 if ( array_key_exists($smiley, $this->smileys) ) { 381 $title = $this->_xmlEntities($this->smileys[$smiley]); 382 $this->doc .= '<img src="'.DOKU_BASE.'lib/images/smileys/'.$this->smileys[$smiley]. 383 '" align="middle" alt="'. 384 $this->_xmlEntities($smiley).'" />'; 385 } else { 386 $this->doc .= $this->_xmlEntities($smiley); 387 } 388 } 389 390 /* 391 * not used 392 function wordblock($word) { 393 if ( array_key_exists($word, $this->badwords) ) { 394 $this->doc .= '** BLEEP **'; 395 } else { 396 $this->doc .= $this->_xmlEntities($word); 397 } 398 } 399 */ 400 401 function entity($entity) { 402 if ( array_key_exists($entity, $this->entities) ) { 403 $this->doc .= $this->entities[$entity]; 404 } else { 405 $this->doc .= $this->_xmlEntities($entity); 406 } 407 } 408 409 function multiplyentity($x, $y) { 410 $this->doc .= "$x×$y"; 411 } 412 413 function singlequoteopening() { 414 $this->doc .= "‘"; 415 } 416 417 function singlequoteclosing() { 418 $this->doc .= "’"; 419 } 420 421 function doublequoteopening() { 422 $this->doc .= "“"; 423 } 424 425 function doublequoteclosing() { 426 $this->doc .= "”"; 427 } 428 429 /** 430 */ 431 function camelcaselink($link) { 432 $this->internallink($link,$link); 433 } 434 435 436 function locallink($hash, $name = NULL){ 437 global $ID; 438 $name = $this->_getLinkTitle($name, $hash, $isImage); 439 $hash = $this->_headerToLink($hash); 440 $title = $ID.' ↵'; 441 $this->doc .= '<a href="#'.$hash.'" title="'.$title.'" class="wikilink1">'; 442 $this->doc .= $name; 443 $this->doc .= '</a>'; 444 } 445 446 /** 447 * Render an internal Wiki Link 448 * 449 * $search and $returnonly are not for the renderer but are used 450 * elsewhere - no need to implement them in other renderers 451 * 452 * @author Andreas Gohr <andi@splitbrain.org> 453 */ 454 function internallink($id, $name = NULL, $search=NULL,$returnonly=false) { 455 global $conf; 456 global $ID; 457 // default name is based on $id as given 458 $default = $this->_simpleTitle($id); 459 // now first resolve and clean up the $id 460 resolve_pageid(getNS($ID),$id,$exists); 461 $name = $this->_getLinkTitle($name, $default, $isImage, $id); 462 if ( !$isImage ) { 463 if ( $exists ) { 464 $class='wikilink1'; 465 } else { 466 $class='wikilink2'; 467 } 468 } else { 469 $class='media'; 470 } 471 472 //keep hash anchor 473 list($id,$hash) = split('#',$id,2); 474 475 //prepare for formating 476 $link['target'] = $conf['target']['wiki']; 477 $link['style'] = ''; 478 $link['pre'] = ''; 479 $link['suf'] = ''; 480 // highlight link to current page 481 if ($id == $ID) { 482 $link['pre'] = '<span class="curid">'; 483 $link['suf'] = '</span>'; 484 } 485 $link['more'] = 'onclick="return svchk()" onkeypress="return svchk()"'; 486 $link['class'] = $class; 487 $link['url'] = wl($id); 488 $link['name'] = $name; 489 $link['title'] = $id; 490 //add search string 491 if($search){ 492 ($conf['userewrite']) ? $link['url'].='?s=' : $link['url'].='&s='; 493 $link['url'] .= urlencode($search); 494 } 495 496 //keep hash 497 if($hash) $link['url'].='#'.$hash; 498 499 //output formatted 500 if($returnonly){ 501 return $this->_formatLink($link); 502 }else{ 503 $this->doc .= $this->_formatLink($link); 504 } 505 } 506 507 function externallink($url, $name = NULL) { 508 global $conf; 509 510 $name = $this->_getLinkTitle($name, $url, $isImage); 511 512 // add protocol on simple short URLs 513 if(substr($url,0,3) == 'ftp' && (substr($url,0,6) != 'ftp://')) $url = 'ftp://'.$url; 514 if(substr($url,0,3) == 'www') $url = 'http://'.$url; 515 516 if ( !$isImage ) { 517 $class='urlextern'; 518 } else { 519 $class='media'; 520 } 521 522 //prepare for formating 523 $link['target'] = $conf['target']['extern']; 524 $link['style'] = ''; 525 $link['pre'] = ''; 526 $link['suf'] = ''; 527 $link['more'] = 'onclick="return svchk()" onkeypress="return svchk()"'; 528 $link['class'] = $class; 529 $link['url'] = $url; 530 $link['name'] = $name; 531 $link['title'] = $this->_xmlEntities($url); 532 if($conf['relnofollow']) $link['more'] .= ' rel="nofollow"'; 533 534 //output formatted 535 $this->doc .= $this->_formatLink($link); 536 } 537 538 /** 539 */ 540 function interwikilink($match, $name = NULL, $wikiName, $wikiUri) { 541 global $conf; 542 543 $link = array(); 544 $link['target'] = $conf['target']['interwiki']; 545 $link['pre'] = ''; 546 $link['suf'] = ''; 547 $link['more'] = 'onclick="return svchk()" onkeypress="return svchk()"'; 548 $link['name'] = $this->_getLinkTitle($name, $wikiUri, $isImage); 549 550 if ( !$isImage ) { 551 $link['class'] = 'interwiki'; 552 } else { 553 $link['class'] = 'media'; 554 } 555 556 //get interwiki URL 557 if ( isset($this->interwiki[$wikiName]) ) { 558 $url = $this->interwiki[$wikiName]; 559 } else { 560 // Default to Google I'm feeling lucky 561 $url = 'http://www.google.com/search?q={URL}&btnI=lucky'; 562 $wikiName = 'go'; 563 } 564 565 if(!$isImage){ 566 //if ico exists set additional style 567 if(@file_exists(DOKU_INC.'lib/images/interwiki/'.$wikiName.'.png')){ 568 $link['style']='background-image: url('.DOKU_BASE.'lib/images/interwiki/'.$wikiName.'.png)'; 569 }elseif(@file_exists(DOKU_INC.'interwiki/'.$wikiName.'.gif')){ 570 $link['style']='background-image: url('.DOKU_BASE.'lib/images/interwiki/'.$wikiName.'.gif)'; 571 } 572 } 573 574 //do we stay at the same server? Use local target 575 if( strpos($url,DOKU_URL) === 0 ){ 576 $link['target'] = $conf['target']['wiki']; 577 } 578 579 //replace placeholder 580 if(preg_match('#\{(URL|NAME|SCHEME|HOST|PORT|PATH|QUERY)\}#',$url)){ 581 //use placeholders 582 $url = str_replace('{URL}',urlencode($wikiUri),$url); 583 $url = str_replace('{NAME}',$wikiUri,$url); 584 $parsed = parse_url($wikiUri); 585 if(!$parsed['port']) $parsed['port'] = 80; 586 $url = str_replace('{SCHEME}',$parsed['scheme'],$url); 587 $url = str_replace('{HOST}',$parsed['host'],$url); 588 $url = str_replace('{PORT}',$parsed['port'],$url); 589 $url = str_replace('{PATH}',$parsed['path'],$url); 590 $url = str_replace('{QUERY}',$parsed['query'],$url); 591 $link['url'] = $url; 592 }else{ 593 //default 594 $link['url'] = $url.urlencode($wikiUri); 595 } 596 597 $link['title'] = htmlspecialchars($link['url']); 598 599 //output formatted 600 $this->doc .= $this->_formatLink($link); 601 } 602 603 /** 604 */ 605 function windowssharelink($url, $name = NULL) { 606 global $conf; 607 global $lang; 608 //simple setup 609 $link['target'] = $conf['target']['windows']; 610 $link['pre'] = ''; 611 $link['suf'] = ''; 612 $link['style'] = ''; 613 //Display error on browsers other than IE 614 $link['more'] = 'onclick="if(document.all == null){alert(\''. 615 $this->_xmlEntities($lang['nosmblinks'],ENT_QUOTES). 616 '\');}" onkeypress="if(document.all == null){alert(\''. 617 $this->_xmlEntities($lang['nosmblinks'],ENT_QUOTES).'\');}"'; 618 619 $link['name'] = $this->_getLinkTitle($name, $url, $isImage); 620 if ( !$isImage ) { 621 $link['class'] = 'windows'; 622 } else { 623 $link['class'] = 'media'; 624 } 625 626 627 $link['title'] = $this->_xmlEntities($url); 628 $url = str_replace('\\','/',$url); 629 $url = 'file:///'.$url; 630 $link['url'] = $url; 631 632 //output formatted 633 $this->doc .= $this->_formatLink($link); 634 } 635 636 function emaillink($address, $name = NULL) { 637 global $conf; 638 //simple setup 639 $link = array(); 640 $link['target'] = ''; 641 $link['pre'] = ''; 642 $link['suf'] = ''; 643 $link['style'] = ''; 644 $link['more'] = ''; 645 646 //we just test for image here - we need to encode the title our self 647 $this->_getLinkTitle($name, $address, $isImage); 648 if ( !$isImage ) { 649 $link['class']='mail'; 650 } else { 651 $link['class']='media'; 652 } 653 654 //shields up 655 if($conf['mailguard']=='visible'){ 656 //the mail name gets some visible encoding 657 $address = str_replace('@',' [at] ',$address); 658 $address = str_replace('.',' [dot] ',$address); 659 $address = str_replace('-',' [dash] ',$address); 660 661 $title = $this->_xmlEntities($address); 662 if(empty($name)){ 663 $name = $this->_xmlEntities($address); 664 }else{ 665 $name = $this->_xmlEntities($name); 666 } 667 }elseif($conf['mailguard']=='hex'){ 668 //encode every char to a hex entity 669 for ($x=0; $x < strlen($address); $x++) { 670 $encode .= '&#x' . bin2hex($address[$x]).';'; 671 } 672 $address = $encode; 673 $title = $encode; 674 if(empty($name)){ 675 $name = $encode; 676 }else{ 677 $name = $this->_xmlEntities($name); 678 } 679 }else{ 680 //keep address as is 681 $title = $this->_xmlEntities($address); 682 if(empty($name)){ 683 $name = $this->_xmlEntities($address); 684 }else{ 685 $name = $this->_xmlEntities($name); 686 } 687 } 688 689 $link['url'] = 'mailto:'.$address; 690 $link['name'] = $name; 691 $link['title'] = $title; 692 693 //output formatted 694 $this->doc .= $this->_formatLink($link); 695 } 696 697 /** 698 * @todo don't add link for flash 699 */ 700 function internalmedia ($src, $title=NULL, $align=NULL, $width=NULL, 701 $height=NULL, $cache=NULL) { 702 global $conf; 703 global $ID; 704 resolve_mediaid(getNS($ID),$src, $exists); 705 706 $link = array(); 707 $link['class'] = 'media'; 708 $link['style'] = ''; 709 $link['pre'] = ''; 710 $link['suf'] = ''; 711 $link['more'] = 'onclick="return svchk()" onkeypress="return svchk()"'; 712 $link['target'] = $conf['target']['media']; 713 714 $link['title'] = $this->_xmlEntities($src); 715 $link['url'] = DOKU_BASE.'lib/exe/fetch.php?cache='.$cache.'&media='.urlencode($src); 716 $link['name'] = $this->_media ($src, $title, $align, $width, $height, $cache); 717 718 719 //output formatted 720 $this->doc .= $this->_formatLink($link); 721 } 722 723 /** 724 * @todo don't add link for flash 725 */ 726 function externalmedia ($src, $title=NULL, $align=NULL, $width=NULL, 727 $height=NULL, $cache=NULL) { 728 global $conf; 729 730 $link = array(); 731 $link['class'] = 'media'; 732 $link['style'] = ''; 733 $link['pre'] = ''; 734 $link['suf'] = ''; 735 $link['more'] = 'onclick="return svchk()" onkeypress="return svchk()"'; 736 $link['target'] = $conf['target']['media']; 737 738 $link['title'] = $this->_xmlEntities($src); 739 $link['url'] = DOKU_BASE.'lib/exe/fetch.php?cache='.$cache.'&media='.urlencode($src); 740 $link['name'] = $this->_media ($src, $title, $align, $width, $height, $cache); 741 742 743 //output formatted 744 $this->doc .= $this->_formatLink($link); 745 } 746 747 /** 748 * Renders an RSS feed using Magpie 749 * 750 * @author Andreas Gohr <andi@splitbrain.org> 751 */ 752 function rss ($url){ 753 global $lang; 754 define('MAGPIE_CACHE_ON', false); //we do our own caching 755 define('MAGPIE_DIR', DOKU_INC.'inc/magpie/'); 756 define('MAGPIE_OUTPUT_ENCODING','UTF-8'); //return all feeds as UTF-8 757 require_once(MAGPIE_DIR.'/rss_fetch.inc'); 758 759 //disable warning while fetching 760 $elvl = error_reporting(E_ERROR); 761 $rss = fetch_rss($url); 762 error_reporting($elvl); 763 764 $this->doc .= '<ul class="rss">'; 765 if($rss){ 766 foreach ($rss->items as $item ) { 767 $this->doc .= '<li>'; 768 $this->externallink($item['link'],$item['title']); 769 $this->doc .= '</li>'; 770 } 771 }else{ 772 $this->doc .= '<li>'; 773 $this->doc .= '<em>'.$lang['rssfailed'].'</em>'; 774 $this->externallink($url); 775 $this->doc .= '</li>'; 776 } 777 $this->doc .= '</ul>'; 778 } 779 780 // $numrows not yet implemented 781 function table_open($maxcols = NULL, $numrows = NULL){ 782 $this->doc .= '<table class="inline">'.DOKU_LF; 783 } 784 785 function table_close(){ 786 $this->doc .= '</table>'.DOKU_LF.'<br />'.DOKU_LF; 787 } 788 789 function tablerow_open(){ 790 $this->doc .= DOKU_TAB . '<tr>' . DOKU_LF . DOKU_TAB . DOKU_TAB; 791 } 792 793 function tablerow_close(){ 794 $this->doc .= DOKU_LF . DOKU_TAB . '</tr>' . DOKU_LF; 795 } 796 797 function tableheader_open($colspan = 1, $align = NULL){ 798 $this->doc .= '<th'; 799 if ( !is_null($align) ) { 800 $this->doc .= ' class="'.$align.'align"'; 801 } 802 if ( $colspan > 1 ) { 803 $this->doc .= ' colspan="'.$colspan.'"'; 804 } 805 $this->doc .= '>'; 806 } 807 808 function tableheader_close(){ 809 $this->doc .= '</th>'; 810 } 811 812 function tablecell_open($colspan = 1, $align = NULL){ 813 $this->doc .= '<td'; 814 if ( !is_null($align) ) { 815 $this->doc .= ' class="'.$align.'align"'; 816 } 817 if ( $colspan > 1 ) { 818 $this->doc .= ' colspan="'.$colspan.'"'; 819 } 820 $this->doc .= '>'; 821 } 822 823 function tablecell_close(){ 824 $this->doc .= '</td>'; 825 } 826 827 //---------------------------------------------------------- 828 // Utils 829 830 /** 831 * Build a link 832 * 833 * Assembles all parts defined in $link returns HTML for the link 834 * 835 * @author Andreas Gohr <andi@splitbrain.org> 836 */ 837 function _formatLink($link){ 838 //make sure the url is XHTML compliant (skip mailto) 839 if(substr($link['url'],0,7) != 'mailto:'){ 840 $link['url'] = str_replace('&','&',$link['url']); 841 $link['url'] = str_replace('&amp;','&',$link['url']); 842 } 843 //remove double encodings in titles 844 $link['title'] = str_replace('&amp;','&',$link['title']); 845 846 $ret = ''; 847 $ret .= $link['pre']; 848 $ret .= '<a href="'.$link['url'].'"'; 849 if($link['class']) $ret .= ' class="'.$link['class'].'"'; 850 if($link['target']) $ret .= ' target="'.$link['target'].'"'; 851 if($link['title']) $ret .= ' title="'.$link['title'].'"'; 852 if($link['style']) $ret .= ' style="'.$link['style'].'"'; 853 if($link['more']) $ret .= ' '.$link['more']; 854 $ret .= '>'; 855 $ret .= $link['name']; 856 $ret .= '</a>'; 857 $ret .= $link['suf']; 858 return $ret; 859 } 860 861 /** 862 * Removes any Namespace from the given name but keeps 863 * casing and special chars 864 * 865 * @author Andreas Gohr <andi@splitbrain.org> 866 */ 867 function _simpleTitle($name){ 868 global $conf; 869 870 if($conf['useslash']){ 871 $nssep = '[:;/]'; 872 }else{ 873 $nssep = '[:;]'; 874 } 875 $name = preg_replace('!.*'.$nssep.'!','',$name); 876 //if there is a hash we use the ancor name only 877 $name = preg_replace('!.*#!','',$name); 878 return $name; 879 } 880 881 /** 882 * Renders internal and external media 883 * 884 * @author Andreas Gohr <andi@splitbrain.org> 885 */ 886 function _media ($src, $title=NULL, $align=NULL, $width=NULL, 887 $height=NULL, $cache=NULL) { 888 889 $ret = ''; 890 891 list($ext,$mime) = mimetype($src); 892 if(substr($mime,0,5) == 'image'){ 893 //add image tag 894 $ret .= '<img src="'.DOKU_BASE.'lib/exe/fetch.php?w='.$width.'&h='.$height. 895 '&cache='.$cache.'&media='.urlencode($src).'"'; 896 897 $ret .= ' class="media'.$align.'"'; 898 899 if (!is_null($title)) { 900 $ret .= ' title="'.$this->_xmlEntities($title).'"'; 901 $ret .= ' alt="'.$this->_xmlEntities($title).'"'; 902 }else{ 903 $ret .= ' alt=""'; 904 } 905 906 if ( !is_null($width) ) 907 $ret .= ' width="'.$this->_xmlEntities($width).'"'; 908 909 if ( !is_null($height) ) 910 $ret .= ' height="'.$this->_xmlEntities($height).'"'; 911 912 $ret .= ' />'; 913 914 }elseif($mime == 'application/x-shockwave-flash'){ 915 $ret .= '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'. 916 ' codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"'; 917 if ( !is_null($width) ) $ret .= ' width="'.$this->_xmlEntities($width).'"'; 918 if ( !is_null($height) ) $ret .= ' height="'.$this->_xmlEntities($height).'"'; 919 $ret .= '>'.DOKU_LF; 920 $ret .= '<param name="movie" value="'.DOKU_BASE.'lib/exe/fetch.php?media='.urlencode($src).'" />'.DOKU_LF; 921 $ret .= '<param name="quality" value="high" />'.DOKU_LF; 922 $ret .= '<embed src="'.DOKU_BASE.'lib/exe/fetch.php?media='.urlencode($src).'"'. 923 ' quality="high"'; 924 if ( !is_null($width) ) $ret .= ' width="'.$this->_xmlEntities($width).'"'; 925 if ( !is_null($height) ) $ret .= ' height="'.$this->_xmlEntities($height).'"'; 926 $ret .= ' type="application/x-shockwave-flash"'. 927 ' pluginspage="http://www.macromedia.com/go/getflashplayer"></embed>'.DOKU_LF; 928 $ret .= '</object>'.DOKU_LF; 929 930 }elseif(!is_null($title)){ 931 // well at least we have a title to display 932 $ret .= $this->_xmlEntities($title); 933 }else{ 934 // just show the source 935 $ret .= $this->_xmlEntities($src); 936 } 937 938 return $ret; 939 } 940 941 function _xmlEntities($string) { 942 return htmlspecialchars($string); 943 } 944 945 function _headerToLink($title) { 946 return str_replace(':','',cleanID($title)); 947 } 948 949 /** 950 * Adds code for section editing button 951 * 952 * This is just aplaceholder and gets replace by the button if 953 * section editing is allowed 954 * 955 * @author Andreas Gohr <andi@splitbrain.org> 956 */ 957 function _secedit($f, $t){ 958 $this->doc .= '<!-- SECTION ['.$f.'-'.$t.'] -->'; 959 } 960 961 /** 962 * Construct a title and handle images in titles 963 * 964 * @author Harry Fuecks <hfuecks@gmail.com> 965 */ 966 function _getLinkTitle($title, $default, & $isImage, $id=NULL) { 967 global $conf; 968 969 $isImage = FALSE; 970 if ( is_null($title) ) { 971 if ($conf['useheading'] && $id) { 972 $heading = p_get_first_heading($id); 973 if ($heading) { 974 return $this->_xmlEntities($heading); 975 } 976 } 977 return $this->_xmlEntities($default); 978 } else if ( is_string($title) ) { 979 return $this->_xmlEntities($title); 980 } else if ( is_array($title) ) { 981 $isImage = TRUE; 982 return $this->_imageTitle($title); 983 } 984 } 985 986 /** 987 * Returns an HTML code for images used in link titles 988 * 989 * @todo Resolve namespace on internal images 990 * @author Andreas Gohr <andi@splitbrain.org> 991 */ 992 function _imageTitle($img) { 993 return $this->_media($img['src'], 994 $img['title'], 995 $img['align'], 996 $img['width'], 997 $img['height'], 998 $img['cache']); 999 } 1000} 1001 1002//Setup VIM: ex: et ts=4 enc=utf-8 : 1003