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 .= ' </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