1 | <?php
|
---|
2 | /**
|
---|
3 | * A simple tabbed area component, developed from the tutorial given here:
|
---|
4 | * http://net.tutsplus.com/tutorials/html-css-techniques/how-to-create-a-slick-tabbed-content-area/
|
---|
5 | *
|
---|
6 | * Syntax:
|
---|
7 | * <TABAREA tabs="comma,separated,list">
|
---|
8 | * <TAB>Content of 'comma' including [[:syntax|wiki syntax]]</TAB>
|
---|
9 | * <TAB>Content of 'separated' and here **is** //some// ''formatting''</TAB>
|
---|
10 | * <TAB>Content of 'list'</TAB>
|
---|
11 | * </TABAREA>
|
---|
12 | *
|
---|
13 | * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
|
---|
14 | */
|
---|
15 |
|
---|
16 | if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
|
---|
17 | if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
|
---|
18 | require_once(DOKU_PLUGIN.'syntax.php');
|
---|
19 |
|
---|
20 | /**
|
---|
21 | * All DokuWiki plugins to extend the parser/rendering mechanism
|
---|
22 | * need to inherit from this class
|
---|
23 | */
|
---|
24 | class syntax_plugin_simpletabs
|
---|
25 | extends DokuWiki_Syntax_Plugin
|
---|
26 | {
|
---|
27 | // The name of currently rendering tab - useful for ensuring syntax (no
|
---|
28 | // close tab before open, for example)
|
---|
29 | var $current_tab;
|
---|
30 |
|
---|
31 | var $current_tab_number;
|
---|
32 |
|
---|
33 | // Print debug messages
|
---|
34 | var $debug;
|
---|
35 |
|
---|
36 | // The count of tabbed areas encountered on this page (so we can ensure a
|
---|
37 | // unique identifier is assigned)
|
---|
38 | var $tab_area_counter;
|
---|
39 |
|
---|
40 | var $tab_counter;
|
---|
41 |
|
---|
42 | var $tabs;
|
---|
43 |
|
---|
44 | var $divide_edit_section;
|
---|
45 |
|
---|
46 | function __construct()
|
---|
47 | {
|
---|
48 | global $PARSER_MODES;
|
---|
49 | $this->allowedModes = array_merge($PARSER_MODES['container'],
|
---|
50 | $PARSER_MODES['formatting'],
|
---|
51 | $PARSER_MODES['substition'],
|
---|
52 | $PARSER_MODES['protected'],
|
---|
53 | $PARSER_MODES['disabled'],
|
---|
54 | $PARSER_MODES['paragraphs']);
|
---|
55 | // Init
|
---|
56 | $this->debug = false;
|
---|
57 | $this->divide_edit_section = false;
|
---|
58 | $this->tab_area_counter = 0;
|
---|
59 | $this->tab_counter = 0;
|
---|
60 | }
|
---|
61 | /** __construct() **/
|
---|
62 |
|
---|
63 |
|
---|
64 | /** @function
|
---|
65 | *
|
---|
66 | */
|
---|
67 | function _debugPrint($msg)
|
---|
68 | {
|
---|
69 | if ($this->debug)
|
---|
70 | {
|
---|
71 | echo "<p><b>[DEBUG]</b> " . $msg . "</p>\n";
|
---|
72 | }
|
---|
73 | }
|
---|
74 | /** _debugPrint() **/
|
---|
75 |
|
---|
76 |
|
---|
77 | /**
|
---|
78 | * Should return the type of syntax this plugin defines.
|
---|
79 | */
|
---|
80 | function getType()
|
---|
81 | {
|
---|
82 | return 'container';
|
---|
83 | }
|
---|
84 | /** getType() **/
|
---|
85 |
|
---|
86 |
|
---|
87 | /**
|
---|
88 | * Returns a number used to determine in which order modes are added.
|
---|
89 | */
|
---|
90 | function getSort()
|
---|
91 | {
|
---|
92 | return 45;
|
---|
93 | }
|
---|
94 |
|
---|
95 |
|
---|
96 | /**
|
---|
97 | * What kind of syntax do we allow (optional)
|
---|
98 | */
|
---|
99 | function getAllowedTypes()
|
---|
100 | {
|
---|
101 | return array('container','formatting','substition','protected','disabled','paragraphs');
|
---|
102 | }
|
---|
103 | /** getAllowedTypes() **/
|
---|
104 |
|
---|
105 |
|
---|
106 | /**
|
---|
107 | * Define how this plugin is handled regarding paragraphs.
|
---|
108 | *
|
---|
109 | * <p>
|
---|
110 | * This method is important for correct XHTML nesting. It returns
|
---|
111 | * one of the following values:
|
---|
112 | * </p>
|
---|
113 | * <dl>
|
---|
114 | * <dt>normal</dt><dd>The plugin can be used inside paragraphs.</dd>
|
---|
115 | * <dt>block</dt><dd>Open paragraphs need to be closed before
|
---|
116 | * plugin output.</dd>
|
---|
117 | * <dt>stack</dt><dd>Special case: Plugin wraps other paragraphs.</dd>
|
---|
118 | * </dl>
|
---|
119 | */
|
---|
120 | function getPType()
|
---|
121 | {
|
---|
122 | return 'normal';
|
---|
123 | }
|
---|
124 |
|
---|
125 |
|
---|
126 | /**
|
---|
127 | * Connect lookup pattern to lexer.
|
---|
128 | */
|
---|
129 | function connectTo($mode)
|
---|
130 | {
|
---|
131 | $this->Lexer->addEntryPattern('<TABAREA tabs=".*?">(?=.*?</TABAREA>)$',$mode,'plugin_simpletabs');
|
---|
132 | // The tabs
|
---|
133 | $this->Lexer->addPattern('<TAB>', 'plugin_simpletabs');
|
---|
134 | $this->Lexer->addPattern('</TAB>', 'plugin_simpletabs');
|
---|
135 | // Hide comments always (sometimes used as separator)
|
---|
136 | $this->Lexer->addPattern('^<!--.*?-->(?=\n)', 'plugin_simpletabs');
|
---|
137 | // Special things inside the content we want to manually handle.
|
---|
138 | $this->Lexer->addPattern( '^[ \t]*={2,6}\s?[^\n]+={2,6}[ \t]*(?=\n)', 'plugin_simpletabs');
|
---|
139 | }
|
---|
140 |
|
---|
141 | function postConnect()
|
---|
142 | {
|
---|
143 | $this->Lexer->addExitPattern('</TABAREA>','plugin_simpletabs');
|
---|
144 | }
|
---|
145 |
|
---|
146 |
|
---|
147 | /**
|
---|
148 | * Handler to prepare matched data for the rendering process.
|
---|
149 | *
|
---|
150 | * <p>
|
---|
151 | * The <tt>$aState</tt> parameter gives the type of pattern
|
---|
152 | * which triggered the call to this method:
|
---|
153 | * </p>
|
---|
154 | * <dl>
|
---|
155 | * <dt>DOKU_LEXER_ENTER</dt>
|
---|
156 | * <dd>a pattern set by <tt>addEntryPattern()</tt></dd>
|
---|
157 | * <dt>DOKU_LEXER_MATCHED</dt>
|
---|
158 | * <dd>a pattern set by <tt>addPattern()</tt></dd>
|
---|
159 | * <dt>DOKU_LEXER_EXIT</dt>
|
---|
160 | * <dd> a pattern set by <tt>addExitPattern()</tt></dd>
|
---|
161 | * <dt>DOKU_LEXER_SPECIAL</dt>
|
---|
162 | * <dd>a pattern set by <tt>addSpecialPattern()</tt></dd>
|
---|
163 | * <dt>DOKU_LEXER_UNMATCHED</dt>
|
---|
164 | * <dd>ordinary text encountered within the plugin's syntax mode
|
---|
165 | * which doesn't match any pattern.</dd>
|
---|
166 | * </dl>
|
---|
167 | * @param $aMatch String The text matched by the patterns.
|
---|
168 | * @param $aState Integer The lexer state for the match.
|
---|
169 | * @param $aPos Integer The character position of the matched text.
|
---|
170 | * @param $aHandler Object Reference to the Doku_Handler object.
|
---|
171 | * @return Integer The current lexer state for the match.
|
---|
172 | * @public
|
---|
173 | * @see render()
|
---|
174 | * @static
|
---|
175 | */
|
---|
176 | function handle($match, $state, $pos, &$handler)
|
---|
177 | {
|
---|
178 | switch ($state)
|
---|
179 | {
|
---|
180 | case DOKU_LEXER_ENTER:
|
---|
181 | // Increment the counter for tabbed area identifiers
|
---|
182 | $this->tab_area_counter++;
|
---|
183 | // Reset these two variables used to track progress through tabbed area
|
---|
184 | $this->current_tab = '';
|
---|
185 | $this->tab_counter = 0;
|
---|
186 | // Separate the tab names - hopefully there are the same number as tabbed
|
---|
187 | // areas otherwise things will go strange
|
---|
188 | $this->tab_names = array();
|
---|
189 | if (preg_match('/tabs="(.+)"/', $match, $matches))
|
---|
190 | {
|
---|
191 | $this->tab_names = explode(',', $matches[1]);
|
---|
192 | }
|
---|
193 | // When rendered this command will create the unordered list that acts as
|
---|
194 | // the tabs themselves as well as the precursor for the tabbed content.
|
---|
195 | $this->_debugPrint("Handler=>enter tabbed area: " . $matches[1]);
|
---|
196 | return array($state, array('action' => 'open tabbed area',
|
---|
197 | 'tab_area_id' => $this->tab_area_counter,
|
---|
198 | 'tab_names' => $this->tab_names,
|
---|
199 | 'default_tab' => $this->getConf('defaulttab'),
|
---|
200 | 'bytepos' => $pos));
|
---|
201 | break;
|
---|
202 |
|
---|
203 | case DOKU_LEXER_MATCHED:
|
---|
204 | // Open tab area
|
---|
205 | if ($match == '<TAB>' && $this->current_tab == '')
|
---|
206 | {
|
---|
207 | $this->current_tab = $this->tab_names[$this->tab_counter];
|
---|
208 | $this->tab_counter++;
|
---|
209 | $this->current_tab_number = $this->tab_counter;
|
---|
210 | $active = false;
|
---|
211 | if ($this->current_tab == $this->getConf('defaulttab'))
|
---|
212 | {
|
---|
213 | $active = true;
|
---|
214 | }
|
---|
215 | // When rendered this command will open the div serving as a tabbed
|
---|
216 | // area's content
|
---|
217 | $this->_debugPrint("Handler=>open tab: " . $this->current_tab);
|
---|
218 | return array($state, array('action' => 'open tab',
|
---|
219 | 'tab_area_id' => $this->tab_area_counter,
|
---|
220 | 'tab_number' => $this->tab_counter,
|
---|
221 | 'active' => $active,
|
---|
222 | 'bytepos' => $pos));
|
---|
223 | }
|
---|
224 | if ($match == '</TAB>')
|
---|
225 | {
|
---|
226 | $this->_debugPrint("Handler=>close tab: " . $this->current_tab);
|
---|
227 | // Housekeeping
|
---|
228 | $this->current_tab = '';
|
---|
229 | $this->current_tab_number = 0;
|
---|
230 | // When rendered this command closes the tabbed area content div
|
---|
231 | return array($state, array('action' => 'close tab',
|
---|
232 | 'bytepos' => $pos + strlen($match)));
|
---|
233 | }
|
---|
234 | if (preg_match('/^<!--(.*)?-->(?=\n)/s', $match, $matches))
|
---|
235 | {
|
---|
236 | $this->_debugPrint("Handler=>comment: " . htmlspecialchars($matches[1]));
|
---|
237 | $params = array();
|
---|
238 | $params['action'] = 'comment';
|
---|
239 | $params['comment'] = $matches[1];
|
---|
240 | return array($state, $params);
|
---|
241 | }
|
---|
242 | if (preg_match('/(={2,6})\s*(.+?)\s*={2,6}/', $match, $matches))
|
---|
243 | {
|
---|
244 | $params = array();
|
---|
245 | $params['action'] = 'heading';
|
---|
246 | $params['title'] = $matches[2];
|
---|
247 | $params['level'] = 7 - strlen($matches[1]);
|
---|
248 | return array($state, $params);
|
---|
249 | //$handler->_addCall('header',array($params['title'],$params['level'],$pos),$pos);
|
---|
250 | //return false;
|
---|
251 | }
|
---|
252 | break;
|
---|
253 |
|
---|
254 | case DOKU_LEXER_EXIT:
|
---|
255 | $this->_debugPrint("Handler=>exit tabbed area");
|
---|
256 | return array($state, array('action' => 'close tabbed area',
|
---|
257 | 'bytepos' => $pos + strlen($match)));
|
---|
258 | break;
|
---|
259 |
|
---|
260 | case DOKU_LEXER_UNMATCHED:
|
---|
261 | $this->_debugPrint("Handler=>unmatched: " . htmlspecialchars($match));
|
---|
262 | return array($state, $match);
|
---|
263 | break;
|
---|
264 | }
|
---|
265 |
|
---|
266 | return array();
|
---|
267 | }
|
---|
268 |
|
---|
269 | /**
|
---|
270 | * Handle the actual output creation.
|
---|
271 | *
|
---|
272 | * <p>
|
---|
273 | * The method checks for the given <tt>$aFormat</tt> and returns
|
---|
274 | * <tt>FALSE</tt> when a format isn't supported. <tt>$aRenderer</tt>
|
---|
275 | * contains a reference to the renderer object which is currently
|
---|
276 | * handling the rendering. The contents of <tt>$aData</tt> is the
|
---|
277 | * return value of the <tt>handle()</tt> method.
|
---|
278 | * </p>
|
---|
279 | * @see handle()
|
---|
280 | */
|
---|
281 | function render($mode, &$renderer, $data)
|
---|
282 | {
|
---|
283 | if($mode == 'xhtml')
|
---|
284 | {
|
---|
285 | list($state, $params) = $data;
|
---|
286 |
|
---|
287 | switch ($state)
|
---|
288 | {
|
---|
289 | case DOKU_LEXER_ENTER:
|
---|
290 | if ($this->divide_edit_section && method_exists($renderer, 'finishSectionEdit'))
|
---|
291 | {
|
---|
292 | $renderer->finishSectionEdit($params['bytepos']);
|
---|
293 | }
|
---|
294 | $renderer->doc .= '</p>'; // Provide match for illegal wrapping <p>
|
---|
295 | $renderer->doc .= '
|
---|
296 |
|
---|
297 | <!-- Tabbed Area - Begin -->
|
---|
298 | <div class="tabbed_area" id="simpletabs-t' . $params['tab_area_id'] . '">
|
---|
299 | <ul class="tabs">
|
---|
300 | ';
|
---|
301 | $total_tab_count = count($params['tab_names']);
|
---|
302 | $tab_count = 0;
|
---|
303 | foreach ($params['tab_names'] as $tab_name)
|
---|
304 | {
|
---|
305 | $tab_count++;
|
---|
306 | $renderer->doc .= ' <li><a href="javascript:tabSwitch(' . $params['tab_area_id'] . ',' . $total_tab_count . ',' . $tab_count . ');" id="simpletabs-t' . $params['tab_area_id'] . '_' . $tab_count . '"';
|
---|
307 | if ($tab_name == $params['default_tab'])
|
---|
308 | {
|
---|
309 | $renderer->doc .= ' class="active"';
|
---|
310 | }
|
---|
311 | $renderer->doc .= '>' . $tab_name . '</a></li>
|
---|
312 | ';
|
---|
313 | }
|
---|
314 | $renderer->doc .= ' </ul>
|
---|
315 | ';
|
---|
316 | break;
|
---|
317 |
|
---|
318 | case DOKU_LEXER_MATCHED:
|
---|
319 | switch($params['action'])
|
---|
320 | {
|
---|
321 | case 'open tab':
|
---|
322 | $extra_classy = '';
|
---|
323 | if ($this->divide_edit_section && method_exists($renderer, 'startSectionEdit'))
|
---|
324 | {
|
---|
325 | $extra_classy = ' ' . $renderer->startSectionEdit($params['bytepos'], 'plugin_simpletabs');
|
---|
326 | }
|
---|
327 | $renderer->doc .= ' <div id="simpletabs-tc' . $params['tab_area_id'] . '_' . $params['tab_number'] . '" class="tab' . $extra_classy . '"';
|
---|
328 | if (!$params['active'])
|
---|
329 | {
|
---|
330 | $renderer->doc .= ' style="display:none;"';
|
---|
331 | }
|
---|
332 | $renderer->doc .= '>';
|
---|
333 | $renderer->doc .= '<p>'; // Open bogus </p>
|
---|
334 | break;
|
---|
335 | case 'close tab':
|
---|
336 | if ($this->divide_edit_section && method_exists($renderer, 'finishSectionEdit'))
|
---|
337 | {
|
---|
338 | $renderer->finishSectionEdit($params['bytepos']);
|
---|
339 | }
|
---|
340 | $renderer->doc .= '</p>'; // Close bogus <p>
|
---|
341 | $renderer->doc .= '<div style="clear:both;height:0px;"></div>';
|
---|
342 | $renderer->doc .= '</div>';
|
---|
343 | break;
|
---|
344 | case 'comment':
|
---|
345 | $renderer->doc .= '<!-- ' . $params['comment'] . ' -->';
|
---|
346 | break;
|
---|
347 | case 'heading':
|
---|
348 | $renderer->doc .= '<h' . $params['level'] . '>' . $params['title'] . '</h' . $params['level'] . '>';
|
---|
349 | break;
|
---|
350 | default:
|
---|
351 | $renderer->doc .= '<p>Error! Unknown action: ' . $params['action'] . '</p>';
|
---|
352 | }
|
---|
353 | break;
|
---|
354 |
|
---|
355 | case DOKU_LEXER_EXIT:
|
---|
356 | $renderer->doc .= '</div>
|
---|
357 | <!-- Tabbed Area - End -->
|
---|
358 | ';
|
---|
359 | $renderer->doc .= '<p>'; // Provide match for illegal wrapping <p>
|
---|
360 | if ($this->divide_edit_section && method_exists($renderer, 'startSectionEdit'))
|
---|
361 | {
|
---|
362 | $class = $renderer->startSectionEdit($params['bytepos'], 'plugin_simpletabs');
|
---|
363 | $renderer->doc .= '<div class="' . $class . '">';
|
---|
364 | }
|
---|
365 | break;
|
---|
366 |
|
---|
367 | case DOKU_LEXER_UNMATCHED:
|
---|
368 | $renderer->doc .= $params;
|
---|
369 | break;
|
---|
370 | }
|
---|
371 | return true;
|
---|
372 | }
|
---|
373 | return false;
|
---|
374 | }
|
---|
375 | }
|
---|
376 |
|
---|
377 | //Setup VIM: ex: et ts=4 enc=utf-8 :
|
---|
378 | ?> |
---|