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