root/documentation/trunk/wiki/plugins/simpletabs/syntax.php @ 30325

Revision 30325, 12.7 KB (checked in by jmt12, 5 years ago)

And this time I'll turn off debugging

Line 
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
16if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
17if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
18require_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 */
24class syntax_plugin_simpletabs
25extends 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('<!--.*?-->', '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      // note: we only handle comments inside tabbed area but outside of tabs
235      if (empty($this->current_tab) && preg_match('/<!--(.*)?-->/s', $match, $matches))
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      }
243      if (preg_match('/(={2,6})\s*(.+?)\s*={2,6}/', $match, $matches))
244      {
245          $this->_debugPrint("Handler=>header: " . htmlspecialchars($match));
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:
263        $this->_debugPrint("Handler=>unmatched: " . htmlspecialchars($match));
264      return array($state, $match);
265      break;
266    }
267
268    $this->_debugPrint("Unhandled: " . htmlspecialchars($match));
269    return array(DOKU_LEXER_UNMATCHED, $match);
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              {
328                  $extra_classy = ' ' . $renderer->startSectionEdit($params['bytepos'], 'plugin_simpletabs');
329              }
330              $renderer->doc .= '  <div id="simpletabs-tc' . $params['tab_area_id'] . '_' . $params['tab_number'] . '" class="tab' . $extra_classy . '"';
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;
347          case 'comment':
348              $renderer->doc .= '<!-- ' . $params['comment'] . ' -->';
349              break;
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?>
Note: See TracBrowser for help on using the browser.