1<?php 2/** 3 * Allow creation of XHTML definition lists: 4 * <dl> 5 * <dt>term</dt> 6 * <dd>definition</dd> 7 * </dl> 8 * 9 * Syntax: 10 * ; term : definition 11 * ; term 12 * : definition 13 * 14 * As with other dokuwiki lists, each line must start with 2 spaces or a tab. 15 * Nested definition lists are not supported at this time. 16 * 17 * This plugin is heavily based on the definitions plugin by Pavel Vitis which 18 * in turn drew from the original definition list plugin by Stephane Chamberland. 19 * A huge thanks to both of them. 20 * 21 * Configuration: 22 * 23 * dt_fancy Whether to wrap DT content in <span class="term">Term</span>. 24 * Default true. 25 * classname The html class name to be given to the DL element. 26 * Default 'plugin_definitionlist'. This is the class used in the 27 * bundled CSS file. 28 * 29 * ODT support provided by Gabriel Birke and LarsDW223 30 * 31 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 32 * @author Chris Smith <chris [at] jalakai [dot] co [dot] uk> 33 * @author Gabriel Birke <birke@d-scribe.de> 34 */ 35 36if (!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); 37if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 38require_once(DOKU_PLUGIN.'syntax.php'); 39 40/** 41 * Settings: 42 * 43 * Define the trigger characters: 44 * ";" & ":" are the mediawiki settings. 45 * "=" & ":" are the settings for the original plugin by Pavel. 46 */ 47if (!defined('DL_DT')) define('DL_DT', ';'); // character to indicate a term (dt) 48if (!defined('DL_DD')) define('DL_DD', ':'); // character to indicate a definition (dd) 49 50/** 51 * 52 */ 53class syntax_plugin_definitionlist extends DokuWiki_Syntax_Plugin { 54 55 protected $stack = array(); // stack of currently open definition list items - used by handle() method 56 57 public function getType() { return 'container'; } 58 public function getAllowedTypes() { return array('container','substition','protected','disabled','formatting'); } 59 public function getPType() { return 'block'; } // block, so not surrounded by <p> tags 60 public function getSort() { return 10; } // before preformatted (20) 61 62 /** 63 * Connect pattern to lexer 64 */ 65 public function connectTo($mode) { 66 67 $this->Lexer->addEntryPattern('\n {2,}'.DL_DT, $mode, 'plugin_definitionlist'); 68 $this->Lexer->addEntryPattern('\n\t{1,}'.DL_DT, $mode, 'plugin_definitionlist'); 69 70 $this->Lexer->addPattern('(?: '.DL_DD.' )', 'plugin_definitionlist'); 71 $this->Lexer->addPattern('\n {2,}(?:'.DL_DT.'|'.DL_DD.')', 'plugin_definitionlist'); 72 $this->Lexer->addPattern('\n\t{1,}(?:'.DL_DT.'|'.DL_DD.')', 'plugin_definitionlist'); 73 } 74 75 public function postConnect() { 76 // we end the definition list when we encounter a blank line 77 $this->Lexer->addExitPattern('\n(?=[ \t]*\n)','plugin_definitionlist'); 78 } 79 80 /** 81 * Handle the match 82 */ 83 public function handle($match, $state, $pos, Doku_Handler $handler) { 84 switch ( $state ) { 85 case DOKU_LEXER_ENTER: 86 array_push($this->stack, 'dt'); 87 $this->_writeCall('dl',DOKU_LEXER_ENTER,$pos,$match,$handler); // open a new DL 88 $this->_writeCall('dt',DOKU_LEXER_ENTER,$pos,$match,$handler); // always start with a DT 89 break; 90 91 case DOKU_LEXER_MATCHED: 92 $oldtag = array_pop($this->stack); 93 $newtag = (substr(rtrim($match), -1) == DL_DT) ? 'dt' : 'dd'; 94 array_push($this->stack, $newtag); 95 96 $this->_writeCall($oldtag,DOKU_LEXER_EXIT,$pos,$match,$handler); // close the current definition list item... 97 $this->_writeCall($newtag,DOKU_LEXER_ENTER,$pos,$match,$handler); // ...and open the new dl item 98 break; 99 100 case DOKU_LEXER_EXIT: 101 // clean up & close any dl items on the stack 102 while ($tag = array_pop($this->stack)) { 103 $this->_writeCall($tag,DOKU_LEXER_EXIT,$pos,$match,$handler); 104 } 105 106 // and finally close the surrounding DL 107 $this->_writeCall('dl',DOKU_LEXER_EXIT,$pos,$match,$handler); 108 break; 109 110 case DOKU_LEXER_UNMATCHED: 111 $handler->base($match, $state, $pos); // cdata --- use base() as _writeCall() is prefixed for private/protected 112 break; 113 } 114 115 return false; 116 } 117 118 /** 119 * helper function to simplify writing plugin calls to the instruction list 120 * 121 * instruction params are of the format: 122 * 0 => tag (string) 'dl','dt','dd' 123 * 1 => state (int) DOKU_LEXER_??? state constant 124 * 2 => match (string) expected to be empty 125 */ 126 protected function _writeCall($tag, $state, $pos, $match, &$handler) { 127 $handler->addPluginCall('definitionlist', array($tag, $state, ''), $state, $pos, $match); 128 } 129 130 /** 131 * Create output 132 */ 133 public function render($format, Doku_Renderer $renderer, $data) { 134 if (empty($data)) return false; 135 136 switch ($format) { 137 case 'xhtml' : return $this->render_xhtml($renderer,$data); 138 case 'odt' : 139 if (!method_exists ($renderer, 'getODTPropertiesFromElement')) { 140 return $this->render_odt_old($renderer,$data); 141 } else { 142 return $this->render_odt_new($renderer,$data); 143 } 144 default : 145 // handle unknown formats generically - map both 'dt' & 'dd' to paragraphs; ingnore the 'dl' container 146 list ($tag, $state, $match) = $data; 147 switch ( $state ) { 148 case DOKU_LEXER_ENTER: 149 if ($tag != 'dl') $renderer->p_open(); 150 break; 151 case DOKU_LEXER_MATCHED: // fall-thru 152 case DOKU_LEXER_UNMATCHED: // defensive, shouldn't occur 153 $renderer->cdata($match); 154 break; 155 case DOKU_LEXER_EXIT: 156 if ($tag != 'dl') $renderer->p_close(); 157 break; 158 } 159 return true; 160 } 161 162 return false; 163 } 164 165 /** 166 * create output for the xhtml renderer 167 * 168 */ 169 protected function render_xhtml(Doku_Renderer $renderer, $data) { 170 list($tag,$state,$match) = $data; 171 172 switch ( $state ) { 173 case DOKU_LEXER_ENTER: 174 $renderer->doc .= $this->_open($tag); 175 break; 176 case DOKU_LEXER_MATCHED: 177 case DOKU_LEXER_UNMATCHED: // defensive, shouldn't occur 178 $renderer->cdata($tag); 179 break; 180 case DOKU_LEXER_EXIT: 181 $renderer->doc .= $this->_close($tag); 182 break; 183 } 184 return true; 185 } 186 187 /** 188 * create output for ODT renderer 189 * 190 * @author: Gabriel Birke <birke@d-scribe.de> 191 */ 192 protected function render_odt_old(Doku_Renderer $renderer, $data) { 193 static $param_styles = array('dd' => 'def_f5_list', 'dt' => 'def_f5_term'); 194 $this->_set_odt_styles_old($renderer); 195 196 list ($tag, $state, $match) = $data; 197 198 switch ( $state ) { 199 case DOKU_LEXER_ENTER: 200 if ($tag == 'dl') { 201 $renderer->p_close(); 202 } else { 203 $renderer->p_open($param_styles[$tag]); 204 } 205 break; 206 case DOKU_LEXER_MATCHED: 207 case DOKU_LEXER_UNMATCHED: // defensive, shouldn't occur 208 $renderer->cdata($match); 209 break; 210 case DOKU_LEXER_EXIT: 211 if ($tag != 'dl') { 212 $renderer->p_close(); 213 } else { 214 $renderer->p_open(); 215 } 216 break; 217 } 218 219 return true; 220 } 221 222 /** 223 * Create output for ODT renderer (newer version) 224 * @author: LarsDW223 225 */ 226 protected function render_odt_new(Doku_Renderer $renderer, $data) { 227 static $style_data = array(); 228 static $dl_properties = array(); 229 $this->_set_odt_styles_new($renderer, $style_data, $dl_properties); 230 231 list ($tag, $state, $match) = $data; 232 233 switch ( $state ) { 234 case DOKU_LEXER_ENTER: 235 if ($tag == 'dl') { 236 $properties = array(); 237 $renderer->_odtTableOpenUseProperties($dl_properties); 238 239 $properties ['width'] = $style_data ['margin-left']; 240 $renderer->_odtTableAddColumnUseProperties($properties); 241 } else { 242 if ($tag == 'dt') { 243 $renderer->tablerow_open(); 244 245 $properties = array(); 246 $properties ['border-left'] = 'none'; 247 $properties ['border-right'] = 'none'; 248 $properties ['border-top'] = 'none'; 249 $properties ['border-bottom'] = $style_data ['border-bottom']; 250 $renderer->_odtTableCellOpenUseProperties ($properties); 251 252 $renderer->_odtSpanOpen('Plugin_DefinitionList_Term'); 253 } else { 254 $properties = array(); 255 $properties ['border-left'] = 'none'; 256 $properties ['border-right'] = 'none'; 257 $properties ['border-top'] = 'none'; 258 $properties ['border-bottom'] = $style_data ['border-bottom']; 259 $renderer->_odtTableCellOpenUseProperties ($properties); 260 261 if (!empty($style_data ['image'])) { 262 $properties = array(); 263 $properties ['margin-right'] = $style_data ['padding-left']; 264 $renderer->_odtAddImageUseProperties($style_data ['image'], $properties); 265 } 266 267 $renderer->_odtSpanOpen('Plugin_DefinitionList_Description'); 268 } 269 } 270 break; 271 case DOKU_LEXER_MATCHED: 272 case DOKU_LEXER_UNMATCHED: // defensive, shouldn't occur 273 $renderer->cdata($match); 274 break; 275 case DOKU_LEXER_EXIT: 276 if ($tag != 'dl') { 277 $renderer->_odtSpanClose(); 278 $renderer->tablecell_close(); 279 if ($tag == 'dd') { 280 $renderer->p_close(); 281 $renderer->tablerow_close(); 282 } 283 } else { 284 $renderer->table_close(); 285 } 286 break; 287 } 288 289 return true; 290 } 291 292 /** 293 * set definition list styles, used by render_odt_old() 294 * 295 * add definition list styles to the renderer's autostyles property (once only) 296 * 297 * @param $renderer current (odt) renderer object 298 * @return void 299 */ 300 protected function _set_odt_styles_old(Doku_Renderer $renderer) { 301 static $do_once = true; 302 303 if ($do_once) { 304 $renderer->autostyles["def_f5_term"] = ' 305 <style:style style:name="def_f5_term" style:display-name="def_term" style:family="paragraph"> 306 <style:paragraph-properties fo:margin-top="0.18cm" fo:margin-bottom="0cm" fo:keep-together="always" style:page-number="auto" fo:keep-with-next="always"/> 307 <style:text-properties fo:font-weight="bold"/> 308 </style:style>'; 309 $renderer->autostyles["def_f5_list"] = ' 310 <style:style style:name="def_f5_list" style:display-name="def_list" style:family="paragraph"> 311 <style:paragraph-properties fo:margin-left="0.25cm" fo:margin-right="0cm" fo:text-indent="0cm" style:auto-text-indent="false"/> 312 </style:style>'; 313 314 $do_once = false; 315 } 316 } 317 318 /** 319 * Create definition list styles, used by render_odt_new(): 320 * Adds definition list styles to the ODT documents common styles (once only) 321 * 322 * @param Doku_renderer $renderer current (odt) renderer object 323 * @param Array $style_data Array for returning relevant properties to the caller 324 * @author: LarsDW223 325 */ 326 protected function _set_odt_styles_new(Doku_Renderer $renderer, &$style_data, &$dl_properties) { 327 static $do_once = true; 328 329 if ($do_once) { 330 // Create parent style to group the others beneath it 331 if (!$renderer->styleExists('Plugin_DivAlign2')) { 332 $parent_properties = array(); 333 $parent_properties ['style-parent'] = NULL; 334 $parent_properties ['style-class'] = 'Plugin_DefinitionList'; 335 $parent_properties ['style-name'] = 'Plugin_DefinitionList'; 336 $parent_properties ['style-display-name'] = 'Plugin DefinitionList'; 337 $renderer->createTextStyle($parent_properties); 338 } 339 340 // Get the current HTML stack from the ODT renderer 341 $stack = $renderer->getHTMLStack (); 342 343 // Save state to restore it later 344 $state = array(); 345 $stack->getState ($state); 346 // Only for debugging ==> see end of this function 347 //$renderer->dumpHTMLStack (); 348 349 $stack->open('dl', 'class="plugin_definitionlist"', NULL, NULL); 350 $renderer->getODTPropertiesFromElement ($dl_properties, $stack->getCurrentElement(), 'screen', true); 351 352 $stack->open('dd', NULL, NULL, NULL); 353 354 // Get CSS properties for ODT export. 355 $dd_properties = array (); 356 $renderer->getODTPropertiesFromElement ($dd_properties, $stack->getCurrentElement(), 'screen', true); 357 358 $stack->close('dd'); 359 $stack->open('dt', NULL, NULL, NULL); 360 361 // Get CSS properties for ODT export. 362 $dt_properties = array (); 363 $renderer->getODTPropertiesFromElement ($dt_properties, $stack->getCurrentElement(), 'screen', true); 364 365 // Set style data to be returned to caller 366 $style_data ['border-bottom'] = $dt_properties ['border-top']; 367 $style_data ['image'] = $dd_properties ['background-image']; 368 $style_data ['margin-left'] = $dd_properties ['margin-left']; 369 $style_data ['padding-left'] = $dd_properties ['padding-left']; 370 371 // Create text style for term 372 $dt_properties ['border-top'] = NULL; 373 $dt_properties ['style-class'] = NULL; 374 $dt_properties ['style-parent'] = 'Plugin_DefinitionList'; 375 $dt_properties ['style-name'] = 'Plugin_DefinitionList_Term'; 376 $dt_properties ['style-display-name'] = 'Term'; 377 $renderer->createTextStyle($dt_properties); 378 379 // Create text style for description 380 $dd_properties ['border-bottom'] = $dt_properties ['border-top']; 381 $dd_properties ['style-class'] = NULL; 382 $dd_properties ['style-parent'] = 'Plugin_DefinitionList'; 383 $dd_properties ['style-name'] = 'Plugin_DefinitionList_Description'; 384 $dd_properties ['style-display-name'] = 'Description'; 385 $dd_properties ['background'] = NULL; 386 $renderer->createTextStyle($dd_properties); 387 388 $stack->restoreState ($state); 389 // Only for debugging to check if the ODT plugins HTML stack 390 // is restored to the start state 391 //$renderer->dumpHTMLStack (); 392 393 $do_once = false; 394 } 395 } 396 397 /** 398 * open a definition list tag, used by render_xhtml() 399 * 400 * @param $tag (string) 'dl', 'dt' or 'dd' 401 * @return (string) html used to open the tag 402 */ 403 protected function _open($tag) { 404 if ($tag == 'dl') { 405 if ($this->getConf('classname')) { 406 $tag .= ' class="'.$this->getConf('classname').'"'; 407 } 408 $wrap = NL; 409 } else { 410 $wrap = ($tag == 'dt' && $this->getConf('dt_fancy')) ? '<span class="term">' : ''; 411 } 412 return "<$tag>$wrap"; 413 } 414 415 /** 416 * close a definition list tag, used by render_xhtml() 417 * 418 * @param $tag (string) 'dl', 'dt' or 'dd' 419 * @return (string) html used to close the tag 420 */ 421 protected function _close($tag) { 422 $wrap = ($tag == 'dt' && $this->getConf('dt_fancy')) ? '</span>' : ''; 423 return "$wrap</$tag>\n"; 424 } 425 426} 427 428//Setup VIM: ex: et ts=4 enc=utf-8 : 429