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