1<?php
2
3if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
4if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
5require_once(DOKU_PLUGIN.'syntax.php');
6
7/**
8 * All DokuWiki plugins to extend the parser/rendering mechanism
9 * need to inherit from this class
10 */
11class syntax_plugin_revealjs_header extends DokuWiki_Syntax_Plugin {
12
13    public function getType() { return 'substition'; }
14    public function getSort() { return 32; }
15    public function getPType() { return 'block'; }
16
17
18    /**
19     * Connect lookup pattern to lexer.
20     *
21     * @param $aMode String The desired rendermode.
22     * @return none
23     * @public
24     * @see render()
25     */
26    public function connectTo($mode) {
27        /* We want to override the DokuWiki default header handling in cases, where
28        our Reveal.js plugin is active. Therefore we reuse the header pattern from
29        DokuWiki with a lower sort number to be able to fetch the headers before DokuWiki.
30        See also /inc/parser/parser.php around line 282 */
31        $this->Lexer->addSpecialPattern('[ \t]*={2,}[^\n]+={2,}[ \t]*(?=\n)', $mode, 'plugin_revealjs_header');
32    }
33
34
35    /**
36     * Handler to prepare matched data for the rendering process.
37     *
38     * @param $aMatch String The text matched by the patterns.
39     * @param $aState Integer The lexer state for the match.
40     * @param $aPos Integer The character position of the matched text.
41     * @param $aHandler Object Reference to the Doku_Handler object.
42     * @return Integer The current lexer state for the match.
43     * @public
44     * @see render()
45     * @static
46     */
47    public function handle($match, $state, $pos, Doku_Handler $handler) {
48        /* We reuse and adapt here the default DokuWiki header handler code. See also
49        /inc/parser/handler.php around line 97. */
50
51        // get level and title
52        $title = trim($match);
53        $level = 7 - strspn($title,'=');
54        if($level < 1) $level = 1;
55        $title = trim($title,'= ');
56        if ($this->getConf('revealjs_active') || $_GET['do']=='export_revealjs') {
57            /* We are now on a reveal.js activated page and we want to do our
58            own section handling to be able to get all relevant content from
59            one slide into one edit section. Since sections are header driven,
60            we have to do also our own header handling.
61            Because we are in a plugin context, we are automatically called and
62            do our work in the render function (see below).*/
63            // pass
64        }
65        else {
66            /* This is the DokuWiki default header/section handling, we use it on NON Reveal.js
67            activated pages (keyword ~~REVEAL~~ not found on page). See also theme.php */
68            $handler->_addCall('header', array($title, $level, $pos), $pos);
69        }
70        return array($title, $level, $pos);
71    }
72
73    /**
74     * Handle the actual output creation.
75     *
76     * @param $aFormat String The output format to generate.
77     * @param $aRenderer Object A reference to the renderer object.
78     * @param $aData Array The data created by the <tt>handle()</tt>
79     * method.
80     * @return Boolean <tt>TRUE</tt> if rendered successfully, or
81     * <tt>FALSE</tt> otherwise.
82     * @public
83     * @see handle()
84     */
85    public function render($mode, Doku_Renderer $renderer, $data) {
86        global $conf;
87        if($mode == 'xhtml') {
88            list($text, $level, $pos) = $data;
89            $horizontal_slide_level = $this->getConf('horizontal_slide_level');
90            /* examples:
91            horizontal_slide_level=2 ==> headers level 1-3 open slides, headers 4-6 are only content on slides
92            horizontal_slide_level=1 ==> headers level 1-2 open slides, headers 3-6 are only content on slides */
93
94            // rendering the slideshow
95            if (is_a($renderer, 'renderer_plugin_revealjs')){
96                // slide indicator: special horizontal rules ---- section orange zoom slow no-footer ---->
97                if (!$renderer->slide_indicator_headers) {
98                    $renderer->doc .= '<h'. $level .'>';
99                    $renderer->doc .= $renderer->_xmlEntities($text);
100                    $renderer->doc .= '</h'. $level .'>'.DOKU_LF;
101                }
102                // slide indicator: headers
103                else {
104                    // check, if we have to open a slide
105                    if ($level <= $horizontal_slide_level + 1) {
106                        // check, if we have to open a column
107                        if ($level <= $horizontal_slide_level) {
108                            $renderer->open_slide_container();
109                        }
110                        $renderer->open_slide();
111                    }
112                    $level_calculated = ($level > $horizontal_slide_level && $this->getConf('enlarge_vertical_slide_headers') ? $level - 1 : $level);
113                    $renderer->doc .= '<h'. $level_calculated .'>';
114                    $renderer->doc .= $renderer->_xmlEntities($text);
115                    $renderer->doc .= '</h'. $level_calculated .'>'.DOKU_LF;
116                }
117            }
118
119            // rendering the normal wiki page
120            else if ($this->getConf('revealjs_active') ) {
121                /* could be, that {{no-footer}} is used before and we need to align the
122                start position definition for the section editing */
123                if ($renderer->wikipage_next_slide_no_footer_position > 0) {
124                    $pos = $renderer->wikipage_next_slide_no_footer_position;
125                    $renderer->wikipage_next_slide_no_footer_position = 0;
126                }
127                /**
128                 * Render a heading (aligned copy of /inc/parser/xhtml.php around line 184)
129                 *
130                 * @param string $text  the text to display
131                 * @param int    $level header level
132                 * @param int    $pos   byte position in the original source
133                 */
134                if (!$text) return; //skip empty headlines
135                $hid = $this->_headerToLink($text, $renderer->wikipage_unique_headers);
136                //only add items within configured levels
137                $renderer->toc_additem($hid, $text, $level);
138                // handle section editing
139                if ($renderer->wikipage_slide_indicator_headers &&
140                    $level <= $horizontal_slide_level + 1 &&
141                    $renderer->wikipage_slide_edit_section_open &&
142                    !$renderer->wikipage_slide_background_defined) {
143                    $renderer->wikipage_slide_edit_section_open = false;
144                    $renderer->doc .= DOKU_LF.'</div>'.DOKU_LF;
145                    $renderer->finishSectionEdit($pos - 1);
146                }
147                if ($renderer->wikipage_slide_indicator_headers &&
148                    $level <= $horizontal_slide_level + 1 &&
149                    !$renderer->wikipage_slide_edit_section_open) {
150                    $renderer->wikipage_slide_number += 1;
151
152		    $sectionEditStartData = ['target' => 'section'];
153		    if (!defined('SEC_EDIT_PATTERN')) {
154			// backwards-compatibility for Frusterick Manners (2017-02-19)
155			$sectionEditStartData = 'section';
156		    }
157
158		    /* write slide details to page - we need to use a fake header (<h1 style="display:none...) here
159                    to force dokuwiki to show correct section edit highlighting by hoovering the edit button */
160                    $renderer->doc .= DOKU_LF.DOKU_LF.'<h2 style="display:none;" class="' .
161                        $renderer->startSectionEdit($pos, $sectionEditStartData, 'Slide '.$renderer->wikipage_slide_number).'"></h2>' . ($this->getConf('show_slide_details') ?
162                        '<div class="slide-details-hr'.($renderer->wikipage_slide_number == 1 ? ' first-slide' : '').'"></div>' .
163                        '<div class="slide-details-text">'.($level <= $horizontal_slide_level?'→':'↓') .
164                        ' Slide '.$renderer->wikipage_slide_number.($renderer->wikipage_next_slide_no_footer ? ' no-footer' : '').'</div>' : '');
165                    // open new edit section
166                    $renderer->wikipage_slide_edit_section_open = true;
167                    $renderer->doc .= DOKU_LF.'<div class="level2">'.DOKU_LF;
168                    $renderer->wikipage_next_slide_no_footer = false;
169                }
170                // write the header
171                $renderer->doc .= DOKU_LF.'<h'.$level.' id="'.$hid.'">'.$renderer->_xmlEntities($text)."</h$level>";
172                $renderer->wikipage_slide_background_defined = false;
173            }
174            return true;
175        }
176        return false;
177    }
178
179    /**
180     * Creates a linkid from a headline
181     *
182     * Local version for this plugin which can work on a string instead of an array.
183     * See also /inc/pageutils.php around line 222
184     *
185     * @author Andreas Gohr <andi@splitbrain.org>
186     * @author Ottmar Gobrecht <ottmar.gobrecht@gmail.com>
187     * @param string  $title The headline title
188     * @return string
189     */
190    function _headerToLink($title, &$check) {
191        $title = str_replace(array(':','.'),'',cleanID($title));
192        $new = ltrim($title,'0123456789_-');
193        if(empty($new)){
194            $title = 'section'.preg_replace('/[^0-9]+/','',$title); //keep numbers from headline
195        }
196        else {
197            $title = $new;
198        }
199        // make sure titles are unique
200        $count = 1;
201        while (strpos($check, '{'.$title.'}')) {
202            $title .= $count;
203            $count += 1;
204        }
205        $check .= '{'.$title.'}';
206        return $title;
207    }
208
209}
210