1<?php 2/** 3 * Render Plugin for XHTML output with preserved linebreaks and markup in headers 4 * 5 * @author Max Westen <max@dlmax.org> 6 * @author Chris Smith <chris@jalakai.co.uk> 7 * @author Danny Lin <danny0838[at]pchome[dot]com[dot]tw> 8 */ 9 10 11// must be run within Dokuwiki 12if(!defined('DOKU_INC')) die(); 13 14/** 15 * All DokuWiki plugins to extend the parser/rendering mechanism 16 * need to inherit from this class 17 */ 18class syntax_plugin_header3 extends DokuWiki_Syntax_Plugin { 19 20 function getType() { return 'baseonly';} 21 function getPType() { return 'block';} 22 function getAllowedTypes() { return array('formatting', 'substition', 'disabled'); } 23 function getSort() { return 49; } 24 25 /** 26 * Connect pattern to lexer 27 */ 28 function connectTo($mode) { 29 $this->Lexer->addEntryPattern("[ \t]*={2,}(?=[^\n]+={2,}[ \t]*\n)", $mode, 'plugin_header3'); 30 } 31 32 function postConnect() { 33 $this->Lexer->addExitPattern('={2,}[ \t]*(?=\n)', 'plugin_header3'); 34 } 35 36 /** 37 * Handle the match 38 */ 39 function handle($match, $state, $pos, Doku_Handler $handler){ 40 switch ($state) { 41 case DOKU_LEXER_ENTER : 42 $this->h_level = 7 - strspn($match,"="); 43 $this->h_pos = $pos; 44 if ($handler->status['section']) $handler->_addCall('section_close',array(),$pos); 45 $handler->addPluginCall('header3',array($state),$state,$pos,$match); 46 $handler->CallWriter = & new Doku_Handler_Nest($handler->CallWriter,'nest_close'); 47 return false; 48 case DOKU_LEXER_UNMATCHED : 49 $handler->_addCall('cdata', array($match), $pos); 50 return false; 51 case DOKU_LEXER_EXIT : 52 $handler->_addCall('nest_close', array(), $pos); 53 $handler->CallWriter->process(); 54 $handler->CallWriter = & $handler->CallWriter->CallWriter; 55 $handler->addPluginCall('header3',array($state,$this->h_level,$this->h_pos),$state,$pos,$match); 56 $handler->_addCall('section_open',array($this->h_level),$pos); 57 $handler->status['section'] = true; 58 return false; 59 } 60 return false; 61 } 62 63 /** 64 * Create output 65 */ 66 function render($format, Doku_Renderer $renderer, $data) { 67 list($state,$level,$pos) = $data; 68 switch ($state) { 69 case DOKU_LEXER_ENTER : 70 // store current parsed content 71 $this->store = $renderer->doc; 72 $renderer->doc = ''; 73 // metadata renderer should always parse content in the header 74 if ($format=='metadata') { 75 $this->capture = $renderer->capture; 76 $renderer->capture = true; 77 } 78 break; 79 case DOKU_LEXER_EXIT : 80 // retrieve content parsed by nest parser (i.e. in the header) 81 $title = trim($renderer->doc); 82 $renderer->doc = $this->store; 83 $this->store = ''; 84 // restore variable 85 if ($format=='metadata') { 86 $renderer->capture = $this->capture; 87 } 88 // create header 89 if($level < 1) $level = 1; 90 $method = '_' . $format . '_header'; 91 if (method_exists($this,$method)) { 92 // we have special procedure for the renderer 93 $this->$method($title, $level, $pos, $renderer); 94 } else { 95 // fall back to default renderer behavior 96 $renderer->header($title,$level,$pos); 97 } 98 break; 99 } 100 return true; 101 } 102 103 // simple function to strip all html tags 104 function _remove_html_tags($text) { 105 return preg_replace( "#<[^>]*?>#", "" , $text); 106 } 107 108 /** 109 * Revised procedures for renderers 110 * 111 * Basically adds &$renderer argument and replaces $this to $renderer, 112 * and our procedures of course. 113 */ 114 115 function _xhtml_header($text, $level, $pos, &$renderer) { 116 global $conf; 117 118 $displaytext = $text; // <= added 119 $text = htmlspecialchars_decode($this->_remove_html_tags($text),ENT_QUOTES); // <= added 120 if(!$text) return; //skip empty headlines 121 122 $hid = $renderer->_headerToLink($text,true); 123 124 //only add items within configured levels 125 $renderer->toc_additem($hid, $text, $level); 126 127 // adjust $node to reflect hierarchy of levels 128 $renderer->node[$level-1]++; 129 if ($level < $renderer->lastlevel) { 130 for ($i = 0; $i < $renderer->lastlevel-$level; $i++) { 131 $renderer->node[$renderer->lastlevel-$i-1] = 0; 132 } 133 } 134 $renderer->lastlevel = $level; 135 136 if ($level <= $conf['maxseclevel'] && 137 count($renderer->sectionedits) > 0 && 138 $renderer->sectionedits[count($renderer->sectionedits) - 1][2] === 'section') { 139 $renderer->finishSectionEdit($pos - 1); 140 } 141 142 // write the header 143 $renderer->doc .= DOKU_LF.'<h'.$level; 144 if ($level <= $conf['maxseclevel']) { 145 $renderer->doc .= ' class="' . $renderer->startSectionEdit($pos, 'section', $text) . '"'; 146 } 147 $renderer->doc .= '><a name="'.$hid.'" id="'.$hid.'">'; 148 $renderer->doc .= $displaytext; // <= revised 149 $renderer->doc .= "</a></h$level>".DOKU_LF; 150 } 151 152 function _odt_header($text, $level, $pos, &$renderer){ 153 $displaytext = $text; // <= added 154 $text = $this->_remove_html_tags($text); // <= added 155 $hid = $renderer->_headerToLink($text,true); 156 $renderer->doc .= '<text:h text:style-name="Heading_20_'.$level.'" text:outline-level="'.$level.'">'; 157 $renderer->doc .= '<text:bookmark-start text:name="'.$hid.'"/>'; 158 $renderer->doc .= $displaytext; // <= revised 159 $renderer->doc .= '<text:bookmark-end text:name="'.$hid.'"/>'; 160 $renderer->doc .= '</text:h>'; 161 } 162 163 function _xml_header($text, $level, $pos, &$renderer){ 164 if (!$text) return; //skip empty headlines 165 $renderer->nextHeader = '<header level="' . $level . '" pos="' . $pos . '">'. 166 $renderer->nextHeader .= $text; // <= revised 167 $renderer->nextHeader .= '</header>'.DOKU_LF; 168 } 169} 170