1<?php
2/**
3 * DokuWiki Plugin tabbox (Syntax Component)
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  Andreas Gohr <dokuwiki@cosmocode.de>
7 */
8
9// must be run within Dokuwiki
10if(!defined('DOKU_INC')) die();
11
12class syntax_plugin_tabbox extends DokuWiki_Syntax_Plugin {
13
14    protected $tabs         = array();
15    protected $tabids       = array();
16    protected $intab        = false;
17    protected $original_doc = '';
18
19    function accepts($mode) {
20        if($mode == 'plugin_tabbox') return true;
21        return parent::accepts($mode);
22    }
23
24    /**
25     * @return string Syntax mode type
26     */
27    public function getType() {
28        return 'formatting';
29    }
30
31    /**
32     * @return string Paragraph type
33     */
34    public function getPType() {
35        return 'stack';
36    }
37
38    /**
39     * @return int Sort order - Low numbers go before high numbers
40     */
41    public function getSort() {
42        return 190;
43    }
44
45    /**
46     * @return array Things that may be inside the syntax
47     */
48    function getAllowedTypes() {
49        return array('container', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs');
50    }
51
52    /**
53     * Connect lookup pattern to lexer.
54     *
55     * @param string $mode Parser mode
56     */
57    public function connectTo($mode) {
58        $this->Lexer->addEntryPattern('<tabbox.*?>(?=.*?</tabbox>)', $mode, 'plugin_tabbox');
59        $this->Lexer->addSpecialPattern('<tabbox.*?>(?=.*?</tabbox>)', 'plugin_tabbox', 'plugin_tabbox');
60    }
61
62    public function postConnect() {
63
64        $this->Lexer->addExitPattern('</tabbox>', 'plugin_tabbox');
65    }
66
67    /**
68     * Handle matches of the tabbox syntax
69     *
70     * @param string $match The match of the syntax
71     * @param int $state The state of the handler
72     * @param int $pos The position in the document
73     * @param Doku_Handler $handler The handler
74     * @return array Data for the renderer
75     */
76    public function handle($match, $state, $pos, Doku_Handler $handler) {
77        // we treat intermediate matches like entries in rendering
78        if($state == DOKU_LEXER_SPECIAL) $state = DOKU_LEXER_ENTER;
79
80        return array($state, $match, $pos);
81    }
82
83    /**
84     * Render xhtml output or metadata
85     *
86     * @param string $mode Renderer mode (supported modes: xhtml)
87     * @param Doku_Renderer $renderer The renderer
88     * @param array $data The data from the handler() function
89     * @return bool If rendering was successful.
90     */
91    public function render($mode, Doku_Renderer $renderer, $data) {
92        if($mode != 'xhtml') return false;
93
94        list($state, $match, $pos) = $data;
95
96        switch($state) {
97            case DOKU_LEXER_ENTER:
98                if(!$this->intab) {
99                    // this is the first tab
100                    $this->_openBox($renderer);
101                } else {
102                    // close last tab
103                    $this->_closeTab($renderer, $pos - 1);
104                }
105                // open new tab
106                $this->_openTab($renderer, substr($match, 7, -1), $pos + strlen($match) + 1 );
107                break;
108            case DOKU_LEXER_EXIT:
109                if($this->intab) {
110                    // close last tab
111                    $this->_closeTab($renderer, $pos - 1);
112                }
113                $this->_closeBox($renderer);
114                break;
115            default:
116                // just render as is
117                $renderer->cdata($match);
118        }
119
120        return true;
121    }
122
123
124
125    /**
126     * Open a new tab with the given name
127     *
128     * @param Doku_Renderer_xhtml $R
129     * @param string $name
130     * @param int $pos Byte position of start of tab content
131     */
132    protected function _openTab(Doku_Renderer_xhtml $R, $name, $pos) {
133        $name  = trim($name);
134        $tabid = 'tab__' . sectionID($name, $this->tabids);
135        // use one smaller headline than current section for the tabs
136        $level = $this->_getProtected($R, 'lastlevel') + 1;
137        if($level > 5) $level = 5;
138
139        // write the header
140        $R->doc .= '<div class="tabboxtab" id="tab_'.$tabid.'">'.DOKU_LF;
141        if (defined('SEC_EDIT_PATTERN')) {
142            // for DokuWiki Greebo and more recent versions
143            $R->doc .= DOKU_LF . '<h' . $level . ' class="hl '.  $R->startSectionEdit($pos, array('target' => 'section', 'name' => $name)) . '" id="' . $tabid . '">';
144        } else {
145            $R->doc .= DOKU_LF . '<h' . $level . ' class="hl '.  $R->startSectionEdit($pos, 'section', $name)  . '" id="' . $tabid . '">';
146        }
147        $R->doc .= $R->_xmlEntities($name);
148        $R->doc .= "</h$level>" . DOKU_LF;
149
150        $this->intab = true;
151    }
152
153    /**
154     * Close the current tab
155     *
156     * @param Doku_Renderer_xhtml $R
157     * @param int $pos Byte position of end of tab content
158     */
159    protected function _closeTab(Doku_Renderer_xhtml $R, $pos) {
160        $R->finishSectionEdit($pos);
161        $R->doc .= DOKU_LF.'</div>'.DOKU_LF;
162        $this->intab = false;
163    }
164
165    /**
166     * Open a new tab box
167     *
168     * @param Doku_Renderer_xhtml $R
169     */
170    protected function _openBox(Doku_Renderer_xhtml $R) {
171        $R->doc .= '<div class="plugin_tabbox">' . DOKU_LF;
172
173    }
174
175    /**
176     * Close the tab box
177     *
178     * @param Doku_Renderer_xhtml $R
179     */
180    protected function _closeBox(Doku_Renderer_xhtml $R) {
181        $R->doc .= '</div>' . DOKU_LF;
182    }
183
184    /**
185     * Get the value of a protected member
186     *
187     * @author Jan Turoň
188     * @link http://stackoverflow.com/a/27754169/172068
189     * @param $obj
190     * @param $name
191     * @return mixed
192     */
193    protected function _getProtected($obj, $name) {
194        $array  = (array) $obj;
195        $prefix = chr(0) . '*' . chr(0);
196        return $array[$prefix . $name];
197    }
198
199}
200
201// vim:ts=4:sw=4:et:
202