1<?php 2/** 3 * Plugin hidden: Enable to hide details 4 * v2.4 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Guillaume Turri <guillaume.turri@gmail.com> 7 */ 8 9if(!defined('DOKU_INC')) die(); 10 11/** 12 * All DokuWiki plugins to extend the parser/rendering mechanism 13 * need to inherit from this class 14 */ 15class syntax_plugin_hidden extends DokuWiki_Syntax_Plugin { 16 17 function getType(){ return 'container'; } 18 function getPType(){ return 'stack'; } 19 function getAllowedTypes() { 20 return array('container', 'baseonly', 'substition','protected','disabled','formatting','paragraphs'); 21 } 22 function getSort(){ 23 //make sure it's greater than hiddenSwitch plugin's one in order to avoid a confusion between "<hidden.*" and "<hiddenSwitch.*" 24 return 189; 25 } 26 27 // override default accepts() method to allow nesting 28 // - ie, to get the plugin accepts its own entry syntax 29 function accepts($mode) { 30 if ($mode == substr(get_class($this), 7)) return true; 31 return parent::accepts($mode); 32 } 33 34 function connectTo($mode) { 35 $this->Lexer->addEntryPattern('<hidden\b.*?>(?=.*?</hidden>)', $mode,'plugin_hidden'); 36 $this->Lexer->addSpecialPattern('<hiddenSwitch[^>]*>', $mode,'plugin_hidden'); 37 } 38 function postConnect() { 39 $this->Lexer->addExitPattern('</hidden>','plugin_hidden'); 40 } 41 42 function handle($match, $state, $pos, Doku_Handler $handler) { 43 switch ($state) { 44 case DOKU_LEXER_SPECIAL: 45 //hiddenSwitch 46 $return = array('text' => $this->getLang('switch.default'), 'type' => 'switch'); 47 $match = trim(utf8_substr($match, 14, -1)); //14 = strlen("<hiddenSwitch ") 48 if ( $match !== '' ){ 49 $return['text'] = $match; 50 } 51 $return['text'] = htmlspecialchars($return['text']); 52 return $return; 53 54 case DOKU_LEXER_ENTER : 55 $return = array( 56 'active' => 'true', 57 'element'=>Array(), 58 'onHidden'=>'', 59 'onVisible'=>'', 60 'initialState'=>'hidden', 61 'state'=>$state, 62 'printHead' => true, 63 'bytepos_start' => $pos, 64 'edit' => false, 65 'editText' => $this->getLang('edit'), 66 'onExportPdf' => '' 67 ); 68 $match = substr($match, 7, -1); //7 = strlen("<hidden") 69 70 //Looking for the initial state 71 preg_match("/initialState *= *\"([^\"]*)\" ?/i", $match, $initialState); 72 if ( count($initialState) != 0) { 73 $match = str_replace($initialState[0], '', $match); 74 $initialState = strtolower(trim($initialState[1])); 75 if ( $initialState == 'visible' 76 || $initialState == 'true' 77 || $initialState == 'expand' ) { 78 $return['initialState'] = 'visible'; 79 } 80 } 81 82 //Looking for the -noPrint option 83 if ( preg_match('/-noprint/i', $match, $found) ){ 84 $return['printHead'] = false; 85 $match = str_replace($found[0], '', $match); 86 } 87 88 //Looking for the -editable option 89 if ( preg_match('/-edit(able)?( *= *"([^"]*)")?/i', $match, $found) ){ 90 if ( count($found) > 1 ){ 91 $return['editText'] = end($found); 92 } 93 $return['edit'] = true; 94 $match = str_replace($found[0], '', $match); 95 } 96 97 //Looking if this block is active 98 preg_match("/active *= *\"([^\"]*)\" ?/i", $match, $active); 99 if( count($active) != 0 ){ 100 $match = str_replace($active[0], '', $match); 101 $active = strtolower(trim($active[1])); 102 if($active=='false' || $active=='f' || $active=='0' || $active=='n'){ 103 $return['active'] = false; 104 } 105 } 106 107 //Looking for the element(s) of the block (ie: which switches may activate this element) 108 preg_match("/element *= *\"([^\"]*)\" ?/i", $match, $element); 109 if( count($element) != 0 ){ 110 $match = str_replace($element[0], '', $match); 111 $element[1] = htmlspecialchars($element[1]); 112 $return['element'] = explode(' ', $element[1]); 113 } 114 115 //Looking for the texts to display 116 $this->_grepOption($return, 'onHidden', $match); 117 $this->_grepOption($return, 'onVisible', $match); 118 $this->_grepOption($return, 'onExportPdf', $match); 119 120 //If there were neither onHidden nor onVisible, take what's left 121 if( $return['onHidden']=='' && $return['onVisible']=='' ){ 122 $text = trim($match); 123 if($text != ''){ 124 $return['onHidden'] = $text; 125 $return['onVisible'] = $text; 126 } else { //if there's nothing left, take the default texts 127 $return['onHidden'] = $this->getConf('default_text_when_hidden'); 128 $return['onVisible'] = $this->getConf('default_text_when_displayed'); 129 } 130 } else { //if one string is specified but not the other, take the defaul text 131 $return['onHidden'] = ($return['onHidden']!='') ? $return['onHidden'] : $this->getConf('default_text_when_hidden'); 132 $return['onVisible'] = ($return['onVisible']!='') ? $return['onVisible'] : $this->getConf('default_text_when_displayed'); 133 } 134 135 //If we don't have an exportPdf text, take the onVisible one, since the block will be exported unfolded 136 if ( $return['onExportPdf'] == '' ){ 137 $return['onExportPdf'] = $return['onVisible']; 138 } 139 140 return $return; 141 142 case DOKU_LEXER_UNMATCHED : 143 return array('state'=>$state, 'text'=>$match); 144 145 default: 146 return array('state'=>$state, 'bytepos_end' => $pos + strlen($match)); 147 } 148 } // handle() 149 150 private function _grepOption(&$options, $tag, &$match){ 151 preg_match("/$tag *= *\"([^\"]*)\" ?/i", $match, $text); 152 if ( count($text) != 0 ){ 153 $match = str_replace($text[0], '', $match); 154 $options[$tag] = $text[1]; 155 } 156 } 157 158 function render($mode, Doku_Renderer $renderer, $data) { 159 if ( $this->_exportingPDF() ){ 160 $data['onVisible'] = $data['onExportPdf']; 161 } 162 163 if($mode == 'xhtml' && array_key_exists('type', $data) && $data['type'] == 'switch') { 164 $renderer->doc .= '<button class="button hiddenSwitch">'.$data['text'].'</button>'; 165 return true; 166 } 167 if($mode == 'xhtml'){ 168 switch ($data['state']) { 169 case DOKU_LEXER_ENTER : 170 $this->editableBlocks[] = $data['edit']; 171 $classEdit = ($data['edit'] ? $renderer->startSectionEdit($data['bytepos_start'], 'section', $data['editText']) : ''); 172 $tab = array(); 173 $onVisible = p_render('xhtml', p_get_instructions($data['onVisible']), $tab); 174 $onHidden = p_render('xhtml', p_get_instructions($data['onHidden']), $tab); 175 176 // "\n" are inside tags to avoid whitespaces in the DOM with FF 177 $renderer->doc .= '<div class="hiddenGlobal '.$classEdit; 178 $renderer->doc .= $data['active'] ? ' hiddenActive' : ''; 179 $renderer->doc .= '">'; 180 181 182 183 $renderer->doc .= '<div class="hiddenElements">'; 184 foreach($data['element'] as $element){ 185 $renderer->doc .= ' '.$element; 186 } 187 $renderer->doc .= "</div>"; 188 189 $renderer->doc .= '<div class="hiddenHead '; 190 $renderer->doc .= $data['printHead'] ? '' : 'hiddenNoPrint'; 191 $renderer->doc .= ($data['initialState'] == 'hidden') ? ' hiddenSinceBeginning' : ''; 192 $renderer->doc .= '">'; 193 $renderer->doc .= '<div class="hiddenOnHidden">'.$onHidden."</div>"; //text displayed when hidden 194 $renderer->doc .= '<div class="hiddenOnVisible">'.$onVisible."</div>"; //text displayed when expanded 195 $renderer->doc .= '</div> <!-- .hiddenHead -->'; 196 197 $renderer->doc .= '<div class="hiddenBody">'; 198 break; 199 200 case DOKU_LEXER_UNMATCHED : 201 $text = $renderer->_xmlEntities($data['text']); 202 if ( preg_match("/^[ \t]*={2,}[^\n]+={2,}[ \t]*$/", $text, $match) ){ 203 $title = trim($match[0]); 204 $level = 7 - strspn($title,'='); 205 if($level < 1) $level = 1; 206 $title = trim($title,'='); 207 $title = trim($title); 208 $renderer->header($title, $level, 0); 209 } else { 210 $renderer->doc .= $text; 211 } 212 break; 213 214 case DOKU_LEXER_EXIT : 215 $renderer->doc .= "</div></div>"; //close hiddenBody and hiddenGlobal 216 if ( array_pop($this->editableBlocks) ){ 217 $renderer->finishSectionEdit($data['bytepos_end']); 218 } 219 break; 220 } 221 return true; 222 } 223 224 if ($mode == 'odt') { 225 if ($data['state'] == DOKU_LEXER_UNMATCHED && $data['type'] != 'switch') { 226 $renderer->doc .= $renderer->_xmlEntities($data['text']); 227 } 228 return true; 229 } 230 231 return false; 232 } // render() 233 234 private function _exportingPDF(){ 235 global $ACT; 236 return ($ACT == 'export_pdf' || $ACT == 'export_pdfbook' || $ACT == 'export_odt'); 237 } 238 239 var $editableBlocks = array(); 240 241} // class syntax_plugin_nspages 242