1<?php 2if (! class_exists('syntax_plugin_tip')) { 3 if (! defined('DOKU_PLUGIN')) { 4 if (! defined('DOKU_INC')) { 5 define('DOKU_INC', realpath(dirname(__FILE__) . '/../../') . '/'); 6 } // if 7 define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/'); 8 } // if 9 // Include parent class: 10 require_once(DOKU_PLUGIN . 'syntax.php'); 11 12/** 13 * <tt>syntax_plugin_tip.php </tt>- A PHP4 class that implements 14 * a <tt>DokuWiki</tt> plugin for <tt>small notes/tips fragments</tt>. 15 * 16 * <p> 17 * Usage:<br> 18 * <tt><tip [c|l|r] [u|e|d] [h|i|n|w]>some text</tip></tt> 19 * </p><pre> 20 * Copyright (C) 2006, 2007 M.Watermann, D-10247 Berlin, FRG 21 * All rights reserved 22 * EMail : <support@mwat.de> 23 * </pre><div class="disclaimer"> 24 * This program is free software; you can redistribute it and/or modify 25 * it under the terms of the GNU General Public License as published by 26 * the Free Software Foundation; either 27 * <a href="http://www.gnu.org/licenses/gpl.html">version 3</a> of the 28 * License, or (at your option) any later version.<br> 29 * This software is distributed in the hope that it will be useful, 30 * but WITHOUT ANY WARRANTY; without even the implied warranty of 31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 32 * General Public License for more details. 33 * </div> 34 * @author <a href="mailto:support@mwat.de">Matthias Watermann</a> 35 * @version <tt>$Id: syntax_plugin_tip.php,v 1.5 2007/08/15 12:36:19 matthias Exp $</tt> 36 * @since created 02-Dec-2006 37 */ 38class syntax_plugin_tip extends DokuWiki_Syntax_Plugin { 39 40 /** 41 * @privatesection 42 */ 43 //@{ 44 45 /** 46 * HTML special characters to replace in <tt>render()</tt>. 47 * 48 * @private 49 * @see render() 50 */ 51 var $_Chars = array('&', '<', '>'); 52 53 /** 54 * Entity replacements for HTML special characters. 55 * 56 * @private 57 * @see render() 58 */ 59 var $_Ents = array('&', '<', '>'); 60 61 //@} 62 /** 63 * @publicsection 64 */ 65 //@{ 66 67 /** 68 * Tell the parser whether the plugin accepts syntax mode 69 * <tt>$aMode</tt> within its own markup. 70 * 71 * @param $aMode String The requested syntaxmode. 72 * @return Boolean <tt>TRUE</tt> unless <tt>$aMode</tt> is 73 * <tt>'plugin_tip'</tt> (which would result in a <tt>FALSE</tt> 74 * method result). 75 * @public 76 * @see getAllowedTypes() 77 */ 78 function accepts($aMode) { 79 return ('plugin_tip' != $aMode); 80 } // accepts() 81 82 /** 83 * Connect lookup pattern to lexer. 84 * 85 * @param $aMode String The desired rendermode. 86 * @public 87 * @see render() 88 */ 89 function connectTo($aMode) { 90 $this->Lexer->addEntryPattern( 91 '\x3Ctip(?:\s+[a-z ]*\s*)*\x3E(?=.*?\x3C\x2Ftip\x3E)', 92 $aMode, 'plugin_tip'); 93 } // connectTo() 94 95 /** 96 * Get an array of mode types that may be nested within the 97 * plugin's own markup. 98 * 99 * @return Array Allowed nested types (i.e. all known types). 100 * @public 101 * @see accepts() 102 * @static 103 */ 104 function getAllowedTypes() { 105 return array( 106 'container', 107 'baseonly', 108 'formatting', 109 'substition', // sic! should be __substitution__ 110 'protected', 111 'disabled', 112 'paragraphs'); 113 } // getAllowedTypes() 114 115 /** 116 * Get an associative array with plugin info. 117 * 118 * <p> 119 * The returned array holds the following fields: 120 * <dl> 121 * <dt>author</dt><dd>Author of the plugin</dd> 122 * <dt>email</dt><dd>Email address to contact the author</dd> 123 * <dt>date</dt><dd>Last modified date of the plugin in 124 * <tt>YYYY-MM-DD</tt> format</dd> 125 * <dt>name</dt><dd>Name of the plugin</dd> 126 * <dt>desc</dt><dd>Short description of the plugin (Text only)</dd> 127 * <dt>url</dt><dd>Website with more information on the plugin 128 * (eg. syntax description)</dd> 129 * </dl> 130 * @return Array Information about this plugin class. 131 * @public 132 * @static 133 */ 134 function getInfo() { 135 return array( 136 'author' => 'Matthias Watermann', 137 'email' => 'support@mwat.de', 138 'date' => '2007-08-15', 139 'name' => 'Tip Syntax Plugin', 140 'desc' => 'Place hints &c. on a page ' 141 . '[<tip [c|l|r] [u|e|d] [h|i|n|w]> ... </tip>]', 142 'url' => 'http://wiki.splitbrain.org/plugin:tip'); 143 } // getInfo() 144 145 /** 146 * Define how this plugin is handled regarding paragraphs. 147 * 148 * <p> 149 * This method is important for correct XHTML nesting. 150 * It returns one of the following values: 151 * </p> 152 * <dl> 153 * <dt>normal</dt><dd>The plugin can be used inside paragraphs.</dd> 154 * <dt>block</dt><dd>Open paragraphs need to be closed before 155 * plugin output.</dd> 156 * <dt>stack</dt><dd>Special case: Plugin wraps other paragraphs.</dd> 157 * </dl> 158 * @return String <tt>"normal"</tt> instead of the (correct) "block" 159 * since otherwise the current DokuWiki parser wouldn't allow for 160 * nested paragraphs. 161 * @public 162 * @static 163 */ 164 function getPType() { 165 return 'normal'; 166 } // getPType() 167 168 /** 169 * Where to sort in? 170 * 171 * @return Integer <tt>415</tt> (not really important here). 172 * @public 173 * @static 174 */ 175 function getSort() { 176 return 415; 177 } // getSort() 178 179 /** 180 * Get the type of syntax this plugin defines. 181 * 182 * @return String <tt>"container"</tt>. 183 * @public 184 * @static 185 */ 186 function getType() { 187 return 'container'; 188 } // getType() 189 190 /** 191 * Handler to prepare matched data for the rendering process. 192 * 193 * <p> 194 * The <tt>$aState</tt> parameter gives the type of pattern 195 * which triggered the call to this method: 196 * </p> 197 * <dl> 198 * <dt>DOKU_LEXER_ENTER</dt> 199 * <dd>a pattern set by <tt>addEntryPattern()</tt></dd> 200 * <dt>DOKU_LEXER_EXIT</dt> 201 * <dd> a pattern set by <tt>addExitPattern()</tt></dd> 202 * <dt>DOKU_LEXER_UNMATCHED</dt> 203 * <dd>ordinary text encountered within the plugin's syntax mode 204 * which doesn't match any pattern.</dd> 205 * </dl> 206 * @param $aMatch String The text matched by the patterns. 207 * @param $aState Integer The lexer state for the match. 208 * @param $aPos Integer The character position of the matched text. 209 * @param $aHandler Object Reference to the Doku_Handler object. 210 * @return Array Index <tt>[0]</tt> holds the current 211 * <tt>$aState</tt>, index <tt>[1]</tt> the match prepared for 212 * the <tt>render()</tt> method. 213 * @public 214 * @see render() 215 * @static 216 */ 217 function handle($aMatch, $aState, $aPos, &$aHandler) { 218 switch ($aState) { 219 case DOKU_LEXER_ENTER: 220 $o = 'r'; // orientation, default: 'right' 221 $p = 'e'; // positioning, default: 'even' 222 $t = ''; // type, default: no image 223 $hits = array(); 224 $aMatch = preg_split('|\s+|', substr($aMatch, 4), 225 -1, PREG_SPLIT_NO_EMPTY); 226 while (list($i, $arg) = each($aMatch)) { 227 switch ($i = strtolower($arg{0})) { 228 case 'c': // center 229 case 'l': // left 230 case 'r': // right 231 $o = $i; 232 break; 233 case 'd': // down 234 case 'e': // even 235 case 'u': // up 236 $p = $i; 237 break; 238 case 'h': // hint 239 case 'i': // info 240 case 'n': // note 241 case 'w': // warn 242 $t = $i; 243 default: 244 break; 245 } // switch 246 } // while 247 return array(DOKU_LEXER_ENTER, $o . $p . $t); 248 case DOKU_LEXER_UNMATCHED: 249 return array(DOKU_LEXER_UNMATCHED, $aMatch); 250 case DOKU_LEXER_EXIT: 251 return array(DOKU_LEXER_EXIT, ''); 252 default: 253 // This state isn't used by 'render()' 254 // and causes it to do nothing at all: 255 return array(DOKU_LEXER_SPECIAL, ''); 256 } // switch 257 } // handle() 258 259 /** 260 * Add exit pattern to lexer. 261 * 262 * @public 263 */ 264 function postConnect() { 265 $this->Lexer->addExitPattern('\x3C\x2Ftip\x3E', 'plugin_tip'); 266 } // postConnect() 267 268 /** 269 * Handle the actual output creation. 270 * 271 * <p> 272 * The method checks for the given <tt>$aFormat</tt> and returns 273 * <tt>FALSE</tt> when a format isn't supported. <tt>$aRenderer</tt> 274 * is a reference to the renderer object which is currently 275 * handling the rendering. The contents of <tt>$aData</tt> is 276 * the return value of the <tt>handle()</tt> method. 277 * </p> 278 * @param $aFormat String The output format to generate. 279 * @param $aRenderer Object A reference to the renderer object. 280 * @param $aData Array The data created/returned by the 281 * <tt>handle()</tt> method. 282 * @return Boolean <tt>TRUE</tt> if rendered successfully, 283 * or <tt>FALSE</tt> otherwise. 284 * @public 285 * @see handle() 286 */ 287 function render($aFormat, &$aRenderer, &$aData) { 288 if ('xhtml' != $aFormat) { 289 return FALSE; 290 } // if 291 // XXX: All those "</p>" and "<p>" tags handled here are just kind 292 // of workaround problems with the current DokuWiki renderer; it's 293 // needed because we've to lie (in "getPType()") about the created 294 // markup's type. 295 // Basically they are __wrong__ here (since they are outside the 296 // plugin's domain of responsibility) but, alas, without them 297 // invalid HTML would be generated :-( 298 // If and when DokuWiki becomes more stateful 299 // the superflous tags should be removed. 300 switch ($aData[0]) { 301 case DOKU_LEXER_ENTER: 302 // Since we have to use PType 'normal' we must close the 303 // current paragraph and open a new one for our own contents. 304 $hits = array(); 305 if (preg_match('|\s*<p>\s*$|i', $aRenderer->doc, $hits)) { 306 $aRenderer->doc = substr($aRenderer->doc, 307 0, -strlen($hits[0])) . '<div class="tipAll tip' 308 . $aData[1] . '"><p>'; 309 } else { 310 $aRenderer->doc .= '</p><div class="tipAll tip' 311 . $aData[1] . '"><p>'; 312 } // if 313 // The "tipAll" class is meant to make CSS easier. 314 return TRUE; 315 case DOKU_LEXER_UNMATCHED: 316 $aRenderer->doc .= 317 str_replace($this->_Chars, $this->_Ents, $aData[1]); 318 return TRUE; 319 case DOKU_LEXER_EXIT: 320 // Since we have to use PType 'normal' we must open 321 // a new paragraph for the following text. 322 $aRenderer->doc = preg_replace('|\s*<p>\s*</p>\s*|', '', 323 $aRenderer->doc) . '</p></div><p>'; 324 default: 325 return TRUE; 326 } // switch 327 } // render() 328 329 //@} 330} // class syntax_plugin_tip 331} // if 332?> 333