1<?php
2/**
3 * @file       progrecss/syntax.php
4 * @brief      DokuWiki Progress bars using CSS.
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author     Luis Machuca Bezzaza <luis [dot] machuca [at] gulix [dot] cl>
7 * @version    1.9 (Mar 2018)
8 * @date       2018-03-27
9 *
10 *   For a live demo check the instructions on the plugin's wiki page.
11 *
12 *  Greetings.
13 *        - Luis Machuca Bezzaza.
14 */
15
16if(!defined('DW_LF')) define('DW_LF',"\n");
17
18if(!defined('DOKU_INC'))
19define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
20if(!defined('DOKU_PLUGIN'))
21define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
22require_once(DOKU_PLUGIN.'syntax.php');
23
24/**
25 * All DokuWiki plugins to extend the parser/rendering mechanism
26 * need to inherit from this class
27 */
28class syntax_plugin_progrecss extends DokuWiki_Syntax_Plugin {
29
30    function getType(){
31        return 'formatting';
32    }
33
34    function getAllowedTypes() {
35        return array('substition', 'formatting', 'disabled');
36    }
37
38    function getSort(){
39        return 550;
40    }
41
42    function getPType(){
43        return 'normal';
44    }
45
46    function connectTo($mode) {
47         $p_param= ".*?";
48         // option 1: a percentage (eg.: "45%")
49         $this->Lexer->addSpecialPattern (
50                "<progrecss (?:[0-9]{1,2}|100)% $p_param/>",
51                $mode, 'plugin_progrecss');
52         // option 2: a fraction (eg.: "13/57")
53         $this->Lexer->addSpecialPattern (
54                "<progrecss (?:\d+/\d+) $p_param/>",
55                $mode, 'plugin_progrecss');
56
57    }
58
59    /**
60     * Handle the match
61     */
62	function handle($match, $state, $pos, Doku_Handler $handler) {
63    // function handle($match, $state, $pos, &$handler){
64
65        /* The syntax is expected as follows:
66          ppp% [param1=value1; [param2=value2; [...]]]
67         */
68        $data= array();
69		static $progrecss_count= 0;
70        static $expected_params= array(
71          "caption",    /* caption: can contain formatting wikitext, or be empty. */
72          "style",      /* style: must be empty or a value defined as a user style. */
73          "width",      /* width: must be a valid CSS expression for width, except in pt. */
74          "order",      /* order: the percentage value appears before, inside (default) or after the bar */
75          "marker",     /* reserved for future use */
76          "pdisplay"    /* pdisplay: sprintf() format for the percentage value */
77          );
78        /* OK, so let's strip the markup, taking first the precentage%
79           value which is _mandatory_. */
80        $match = substr($match,11,-2); // Strip markup
81        $match= explode(' ', $match, 2);
82
83        /* That already done, let's split all other elements, which are
84           expected to be in the 'param=value;' format. We must
85           take into consideration 'escaped' semicolons. */
86        $elems= preg_split('/(?<!\\\\);/', $match[1]);
87
88        foreach ($elems as $pair) {
89            list($key,$value)= explode('=', $pair, 2);
90            trim($key); trim($value);
91            /* we check if the param is valid (exists in 'expected_params'),
92              otherwise we just choose to ignore it.
93              (yes, something better could've been done) */
94            if  (in_array($key, $expected_params) ) { //accept parameter
95                $data[$key]= $value;
96            } else { //refuse parameter
97            }
98        }
99
100        /* Sanitize the 'order' parameter */
101        if (!preg_match('/(before|inside|after)/', $data['order'] ) )
102            $data['order'] = 'inside';
103
104        /* At this point, if any of the available parameters was
105           not found, let's apply some sane default. */
106        //if (empty($data['caption']) )   $data['caption']= "";
107        if (empty($data['style'])   )   $data['style']= "default";
108        if (empty($data['width'])   )   $data['width']= "100px";
109        if (empty($data['pdisplay']))   $data['pdisplay']= $this->getConf('percent_format');
110        $data['style']= "progrecss_". $data['style'];
111
112        /* If 'p' is of the form 'x/y', convert it to an approximate percentage
113        */
114        $p= $match[0];
115        if (preg_match('/\d+\/\d+/', $p) ) {
116            $plist= explode('/', $p, 2);
117            // if fraction is too large, we just consider it 100%
118            $p[0] = min($p[0], $p[1]);
119            $p    = intval($plist[0]*100/$plist[1]);
120            $data['f'] = $plist[0]. $this->getConf('fraction_divisor'). $plist[1];
121        }
122        else {
123            $data['f'] = sprintf($data['pdisplay'].'%%', intval($p));
124        }
125        $data['p']= intval($p);
126        // Else p should be in the form '\d+' as required by the regex
127
128        /* In order to somewhat uniquely IDfy each p-bar,
129           let's use a counter...
130           Why didn't I think of that before? */
131        $IDnum= sprintf("progrecss_id_%03d",
132                $progrecss_count);
133        /* Did I mention that this generates correlative IDs */
134
135        $data['id']= $IDnum;
136		$progrecss_count= $progrecss_count+1;
137
138        /* Are we ready yet? */
139        return $data;
140    }
141
142    /**
143     * Create output
144     */
145    function render($mode, Doku_Renderer $renderer, $data) {
146    //function render($mode, &$renderer, $data) {
147        static $counter= 0;
148        $percentage=  intval($data['p']);
149        $fmted_p = $data['f'];
150        $id= $data['id'];
151        $fullwidth=   $data['width'];
152        $caption= $data['caption'];
153        $style= $data['style'];
154        $where= $data['order'];
155
156        if($mode == 'xhtml'){
157        /* each progrecssbar is enclosed in a SPAN package,
158           classed according to "style" parameter,
159           and IDed in a somewhat-unique manner using both caption
160           and random binary toughts. */
161            $renderer->doc .= $this->_create_block_header(
162                              $id, $style, $fullwidth);
163            $renderer->doc .= '<span class="border" style="width: '.
164                              $fullwidth. ';">';
165        /* The next function contains the actual "intelligence" behind
166           the plugin. The rest is simply "ability".*/
167            $renderer->doc .= $this->_place_percentage($percentage, $fmted_p, $where);
168            $renderer->doc .= DW_LF;
169        /* See? A PHP+CSS is as powerful as... */
170            $renderer->doc .= '&nbsp;</span>'. DW_LF;
171            if (!empty($caption)) $renderer->doc .= $this->_render_caption($caption);
172            $renderer->doc .= $this->_create_block_footer($id);
173            return true;
174        }
175        if($mode == 'text'){
176        // simply output the percentage in a text renderer
177            $renderer->doc .= ' ['. $fmted_p;
178            if (!empty ($caption) ) {
179                $renderer->doc .= ' | '. $caption;
180            }
181            $renderer->doc.= ' ]'. DW_LF;
182            return true;
183        } // done with the text renderer
184        return false;
185    }
186
187    /*
188     * From this point on, all are local functions
189     */
190
191    function _create_block_header($id, $style, $fullwidth) {
192        $wt  = DW_LF. '<!-- begin: progrecss bar \''. $id. '\'. -->'. DW_LF;
193        $wt  = '<span id="'. $id. '" class="'. $style. '" >';
194        return $wt;
195    }
196
197    function _create_block_footer($id) {
198        $wt  = '</span>';
199        $wt .= DW_LF. '<!-- end of progrecss bar \''. $id. '\'. -->'. DW_LF;
200        return $wt;
201    }
202
203    function _render_caption($caption) {
204        $wt  = '<span class="caption">';
205        $wt .= p_render('xhtml', p_get_instructions($caption), $info);
206        $wt  = str_replace('<p>', '', $wt);
207        $wt  = str_replace('</p>', '', $wt);
208        $wt  = trim($wt);
209        $wt .= '</span>';
210        return $wt;
211    }
212
213    /*
214    not used yet.
215    percentage position:
216    before --> [20%    |         ]
217    inside --> [  25%    |       ]
218    after  --> [          |   30%]
219    */
220    function _place_percentage ($pv, $p, $where) {
221        if ($where==='before') $wt.= $p;
222        $wt.= '  <span class="bar '. $where. '" style="width: '.$pv. '%;">';
223        if ($where==='inside') $wt.= $p;
224        $wt.= '</span> ';
225        if ($where==='after') $wt.= $p;
226        return $wt;
227    }
228}
229// end, we are happy now
230
231