1 <?php
2 if (! 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>&lt;tip [c|l|r] [u|e|d] [h|i|n|w]&gt;some text&lt;/tip&gt;</tt>
19  * </p><pre>
20  *	Copyright (C) 2006, 2007  M.Watermann, D-10247 Berlin, FRG
21  *			All rights reserved
22  *		EMail : &lt;support@mwat.de&gt;
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  */
38 class 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('&#38;', '&#60;', '&#62;');
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