[30114] | 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
|
---|
[30324] | 56 | $this->debug = true; //false;
|
---|
[30114] | 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');
|
---|
[30312] | 135 | // Hide comments always (sometimes used as separator)
|
---|
[30324] | 136 | $this->Lexer->addPattern('<!--.*?-->', 'plugin_simpletabs');
|
---|
[30114] | 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 | }
|
---|
[30324] | 234 | // note: we only handle comments inside tabbed area but outside of tabs
|
---|
| 235 | if (empty($this->current_tab) && preg_match('/<!--(.*)?-->/s', $match, $matches))
|
---|
[30312] | 236 | {
|
---|
| 237 | $this->_debugPrint("Handler=>comment: " . htmlspecialchars($matches[1]));
|
---|
| 238 | $params = array();
|
---|
| 239 | $params['action'] = 'comment';
|
---|
| 240 | $params['comment'] = $matches[1];
|
---|
| 241 | return array($state, $params);
|
---|
| 242 | }
|
---|
[30114] | 243 | if (preg_match('/(={2,6})\s*(.+?)\s*={2,6}/', $match, $matches))
|
---|
| 244 | {
|
---|
[30324] | 245 | $this->_debugPrint("Handler=>header: " . htmlspecialchars($match));
|
---|
[30114] | 246 | $params = array();
|
---|
| 247 | $params['action'] = 'heading';
|
---|
| 248 | $params['title'] = $matches[2];
|
---|
| 249 | $params['level'] = 7 - strlen($matches[1]);
|
---|
| 250 | return array($state, $params);
|
---|
| 251 | //$handler->_addCall('header',array($params['title'],$params['level'],$pos),$pos);
|
---|
| 252 | //return false;
|
---|
| 253 | }
|
---|
| 254 | break;
|
---|
| 255 |
|
---|
| 256 | case DOKU_LEXER_EXIT:
|
---|
| 257 | $this->_debugPrint("Handler=>exit tabbed area");
|
---|
| 258 | return array($state, array('action' => 'close tabbed area',
|
---|
| 259 | 'bytepos' => $pos + strlen($match)));
|
---|
| 260 | break;
|
---|
| 261 |
|
---|
| 262 | case DOKU_LEXER_UNMATCHED:
|
---|
[30312] | 263 | $this->_debugPrint("Handler=>unmatched: " . htmlspecialchars($match));
|
---|
[30114] | 264 | return array($state, $match);
|
---|
| 265 | break;
|
---|
| 266 | }
|
---|
| 267 |
|
---|
[30324] | 268 | $this->_debugPrint("Unhandled: " . htmlspecialchars($match));
|
---|
| 269 | return array(DOKU_LEXER_UNMATCHED, $match);
|
---|
[30114] | 270 | }
|
---|
| 271 |
|
---|
| 272 | /**
|
---|
| 273 | * Handle the actual output creation.
|
---|
| 274 | *
|
---|
| 275 | * <p>
|
---|
| 276 | * The method checks for the given <tt>$aFormat</tt> and returns
|
---|
| 277 | * <tt>FALSE</tt> when a format isn't supported. <tt>$aRenderer</tt>
|
---|
| 278 | * contains a reference to the renderer object which is currently
|
---|
| 279 | * handling the rendering. The contents of <tt>$aData</tt> is the
|
---|
| 280 | * return value of the <tt>handle()</tt> method.
|
---|
| 281 | * </p>
|
---|
| 282 | * @see handle()
|
---|
| 283 | */
|
---|
| 284 | function render($mode, &$renderer, $data)
|
---|
| 285 | {
|
---|
| 286 | if($mode == 'xhtml')
|
---|
| 287 | {
|
---|
| 288 | list($state, $params) = $data;
|
---|
| 289 |
|
---|
| 290 | switch ($state)
|
---|
| 291 | {
|
---|
| 292 | case DOKU_LEXER_ENTER:
|
---|
| 293 | if ($this->divide_edit_section && method_exists($renderer, 'finishSectionEdit'))
|
---|
| 294 | {
|
---|
| 295 | $renderer->finishSectionEdit($params['bytepos']);
|
---|
| 296 | }
|
---|
| 297 | $renderer->doc .= '</p>'; // Provide match for illegal wrapping <p>
|
---|
| 298 | $renderer->doc .= '
|
---|
| 299 |
|
---|
| 300 | <!-- Tabbed Area - Begin -->
|
---|
| 301 | <div class="tabbed_area" id="simpletabs-t' . $params['tab_area_id'] . '">
|
---|
| 302 | <ul class="tabs">
|
---|
| 303 | ';
|
---|
| 304 | $total_tab_count = count($params['tab_names']);
|
---|
| 305 | $tab_count = 0;
|
---|
| 306 | foreach ($params['tab_names'] as $tab_name)
|
---|
| 307 | {
|
---|
| 308 | $tab_count++;
|
---|
| 309 | $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 . '"';
|
---|
| 310 | if ($tab_name == $params['default_tab'])
|
---|
| 311 | {
|
---|
| 312 | $renderer->doc .= ' class="active"';
|
---|
| 313 | }
|
---|
| 314 | $renderer->doc .= '>' . $tab_name . '</a></li>
|
---|
| 315 | ';
|
---|
| 316 | }
|
---|
| 317 | $renderer->doc .= ' </ul>
|
---|
| 318 | ';
|
---|
| 319 | break;
|
---|
| 320 |
|
---|
| 321 | case DOKU_LEXER_MATCHED:
|
---|
| 322 | switch($params['action'])
|
---|
| 323 | {
|
---|
| 324 | case 'open tab':
|
---|
| 325 | $extra_classy = '';
|
---|
| 326 | if ($this->divide_edit_section && method_exists($renderer, 'startSectionEdit'))
|
---|
| 327 | {
|
---|
[30312] | 328 | $extra_classy = ' ' . $renderer->startSectionEdit($params['bytepos'], 'plugin_simpletabs');
|
---|
[30114] | 329 | }
|
---|
[30312] | 330 | $renderer->doc .= ' <div id="simpletabs-tc' . $params['tab_area_id'] . '_' . $params['tab_number'] . '" class="tab' . $extra_classy . '"';
|
---|
[30114] | 331 | if (!$params['active'])
|
---|
| 332 | {
|
---|
| 333 | $renderer->doc .= ' style="display:none;"';
|
---|
| 334 | }
|
---|
| 335 | $renderer->doc .= '>';
|
---|
| 336 | $renderer->doc .= '<p>'; // Open bogus </p>
|
---|
| 337 | break;
|
---|
| 338 | case 'close tab':
|
---|
| 339 | if ($this->divide_edit_section && method_exists($renderer, 'finishSectionEdit'))
|
---|
| 340 | {
|
---|
| 341 | $renderer->finishSectionEdit($params['bytepos']);
|
---|
| 342 | }
|
---|
| 343 | $renderer->doc .= '</p>'; // Close bogus <p>
|
---|
| 344 | $renderer->doc .= '<div style="clear:both;height:0px;"></div>';
|
---|
| 345 | $renderer->doc .= '</div>';
|
---|
| 346 | break;
|
---|
[30312] | 347 | case 'comment':
|
---|
| 348 | $renderer->doc .= '<!-- ' . $params['comment'] . ' -->';
|
---|
| 349 | break;
|
---|
[30114] | 350 | case 'heading':
|
---|
| 351 | $renderer->doc .= '<h' . $params['level'] . '>' . $params['title'] . '</h' . $params['level'] . '>';
|
---|
| 352 | break;
|
---|
| 353 | default:
|
---|
| 354 | $renderer->doc .= '<p>Error! Unknown action: ' . $params['action'] . '</p>';
|
---|
| 355 | }
|
---|
| 356 | break;
|
---|
| 357 |
|
---|
| 358 | case DOKU_LEXER_EXIT:
|
---|
| 359 | $renderer->doc .= '</div>
|
---|
| 360 | <!-- Tabbed Area - End -->
|
---|
| 361 | ';
|
---|
| 362 | $renderer->doc .= '<p>'; // Provide match for illegal wrapping <p>
|
---|
| 363 | if ($this->divide_edit_section && method_exists($renderer, 'startSectionEdit'))
|
---|
| 364 | {
|
---|
| 365 | $class = $renderer->startSectionEdit($params['bytepos'], 'plugin_simpletabs');
|
---|
| 366 | $renderer->doc .= '<div class="' . $class . '">';
|
---|
| 367 | }
|
---|
| 368 | break;
|
---|
| 369 |
|
---|
| 370 | case DOKU_LEXER_UNMATCHED:
|
---|
| 371 | $renderer->doc .= $params;
|
---|
| 372 | break;
|
---|
| 373 | }
|
---|
| 374 | return true;
|
---|
| 375 | }
|
---|
| 376 | return false;
|
---|
| 377 | }
|
---|
| 378 | }
|
---|
| 379 |
|
---|
| 380 | //Setup VIM: ex: et ts=4 enc=utf-8 :
|
---|
| 381 | ?> |
---|