1<?php 2/** 3 * DokuWiki Syntax Plugin Canvas canvas 4 * 5 * html5 canvas functionality 6 * 7 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 8 * @author Sahara Satoshi <sahara.satoshi@gmail.com> 9 * 10 * REMARK: depends on InlineJS embedder (syntax component) 11 * SYNTAX: 12 * <canvas[:rgraph|:jqplot] chartid width,height> 13 * ... javascript ... 14 * </canvas> 15 */ 16// must be run within Dokuwiki 17if (!defined('DOKU_INC')) die(); 18if (!defined('NL')) define('NL',"\n"); 19 20/** 21 * All DokuWiki plugins to extend the parser/rendering mechanism 22 * need to inherit from this class 23 */ 24class syntax_plugin_canvas_canvas extends DokuWiki_Syntax_Plugin { 25 26 protected $mode; 27 protected $entry_pattern = '<canvas\b.*?>(?=.*?</canvas>)'; 28 protected $exit_pattern = '</canvas>'; 29 30 function __construct() { 31 $this->mode = substr(get_class($this), 7); 32 } 33 34 function getType() { return 'protected'; } 35 //function getPType() { return 'block'; } 36 function getSort() { return 160; } 37 function connectTo($mode) { 38 $this->Lexer->addEntryPattern($this->entry_pattern, $mode, $this->mode); 39 } 40 function postConnect() { 41 $this->Lexer->addExitPattern($this->exit_pattern, $this->mode); 42 } 43 44 /** 45 * handle syntax 46 */ 47 public function handle($match, $state, $pos, Doku_Handler $handler){ 48 49 global $conf; 50 // check whether inlinejs plugin exists 51 if (!plugin_isdisabled('inlinejs')) { 52 $inlinejs = plugin_load('syntax', 'inlinejs_embedder'); 53 if ($inlinejs->getConf('follow_htmlok') && !$conf['htmlok']) return false; 54 } else { 55 msg($this->getPluginName().': Plugin InlineJS disabled.',-1); 56 return false; 57 } 58 59 switch ($state) { 60 case DOKU_LEXER_ENTER: 61 // at least one delimiter required to have unique canvas id. 62 //if (strpos($match,' ') === false) { 63 // msg($this->getPluginName().': syntax wrong.',-1); 64 // return false; 65 //} 66 return array($state, $match); 67 68 case DOKU_LEXER_UNMATCHED: 69 // javascript code 70 return array($state, $match); 71 72 case DOKU_LEXER_EXIT: 73 return array($state, ''); 74 } 75 return false; 76 } 77 78 /** 79 * Render 80 */ 81 public function render($format, Doku_Renderer $renderer, $indata) { 82 83 if (empty($indata)) return false; 84 list($state, $data) = $indata; 85 86 if ($format != 'xhtml') return false; 87 88 switch ($state) { 89 case DOKU_LEXER_ENTER: 90 // get canvas type and id 91 if ( substr($data, 1, 7) == 'canvas:') { 92 $match = trim(substr($data, 8, -1)); 93 } else { 94 $match = trim(substr($data, 1, -1)); 95 } 96 list($ctype, $cid, $cparam) = explode(' ', $match, 3); 97 if (empty($cid)) { 98 msg($this->getPluginName().': syntax wrong -- '.hsc($data),-1); 99 } 100 $param = $this->getArguments($cparam, 'width'); 101 102 // prepare canvas 103 $renderer->doc.= $this->_htmlCanvas($cid, $ctype, $param); 104 // open script tag to output embedded javascript 105 $renderer->doc.= '<script type="text/javascript">'.NL.'/*<![CDATA[*/'; 106 break; 107 108 case DOKU_LEXER_UNMATCHED: 109 // output javascript 110 $renderer->doc.= $data; 111 break; 112 113 case DOKU_LEXER_EXIT: 114 // close script tag 115 $renderer->doc.= '/*!]]>*/'.NL.'</script>'.NL; 116 break; 117 } 118 return true; 119 } 120 121 122 /* --------------------------------------------------------- 123 * build html of canvas 124 * 125 * @param $cid (string) canvas id 126 * @param $ctype (string) canvas type 127 * @param $opts (array) canvas options (width, height, ...) 128 * --------------------------------------------------------- 129 */ 130 protected function _htmlCanvas($cid, $ctype, $opts) { 131 132 // check whether canvas id is given? 133 if (empty($cid)) return false; 134 135 // set default canvas size 136 if (!array_key_exists('width', $opts)) $opts['width'] = '300px'; 137 if (!array_key_exists('height',$opts)) $opts['height'] = '150px'; 138 139 // prepare plot container 140 switch ($ctype) { 141 case "jqplot": 142 // see its project page https://bitbucket.org/cleonello/jqplot/overview 143 // jqPlot is currently available for use in all personal or commercial projects 144 // under both the MIT and GPL version 2.0 licenses. This means that you can 145 // choose the license that best suits your project and use it accordingly. 146 $html.= '<div class="jqplot-target"'; 147 $html.= ' id="'.$cid.'"'; 148 $html.= ' style="width: '.$opts['width'].'; height: '.$opts['height'].';"> '; 149 $html.= '</div>'.NL; 150 $html.= '<div class="jqplot-license-note"'; 151 $html.= ' style="width: '.$opts['width'].'">'; 152 $html.= '<a href="http://www.jqplot.com/" title="Powered by jqPlot">Powered by jQplot</a>'; 153 $html.= '</div>'.NL; 154 break; 155 case "rgraph": 156 // see http://www.rgraph.net/license 157 // RGraph can be used free-of-charge by both commercial and non-commercial 158 // entities (eg business, personal, charity, educational etc) on either 159 // internal or external websites or in software that they make under the terms 160 // of the Creative Commons Attribution 3.0 This means that you may use RGraph 161 // for both commercial and non-commercial purposes as long as you link back to 162 // this website (eg underneath the chart). 163 $html.= '<canvas class="canvasbox"'; 164 $html.= ' id="'.$cid.'"'; 165 $html.= ' width="'.substr($opts['width'],0,-2).'"'; 166 $html.= ' height="'.substr($opts['height'],0,-2).'"'; 167 $html.= '>'.'[No canvas support]'.'</canvas>'.NL; 168 $html.= '<div class="rgraph-license-note"'; 169 $html.= ' style="width: '.$opts['width'].'">'; 170 $html.= '<a href="http://www.rgraph.net/" title="Powered by RGraph">Powered by RGraph</a>'; 171 $html.= '</div>'.NL; 172 break; 173 default: 174 $html.= '<canvas class="canvasbox"'; 175 $html.= ' id="'.$cid.'"'; 176 $html.= ' width="'.substr($opts['width'],0,-2).'"'; 177 $html.= ' height="'.substr($opts['height'],0,-2).'"'; 178 $html.= '>'.'[No canvas support]'.'</canvas>'.NL; 179 break; 180 } 181 return $html; 182 } 183 184 185 /* --------------------------------------------------------- 186 * get each named/non-named arguments as array variable 187 * 188 * Named arguments is to be given as key="value" (quoted). 189 * Non-named arguments is assumed as boolean. 190 * 191 * @param $args (string) arguments 192 * @param $singlekey (string) key name if single numeric value was given 193 * @return (array) parsed arguments in $arg['key']=value 194 * --------------------------------------------------------- 195 */ 196 protected function getArguments($args='', $singlekey='height') { 197 $arg = array(); 198 // get named arguments (key="value"), ex: width="100" 199 // value must be quoted in argument string. 200 $val = "([\"'`])(?:[^\\\\\"'`]|\\\\.)*\g{-1}"; 201 $pattern = "/(\w+)\s*=\s*($val)/"; 202 preg_match_all($pattern, $args, $matches, PREG_SET_ORDER); 203 foreach ($matches as $match) { 204 $arg[$match[1]] = substr($match[2], 1, -1); // drop quates from value string 205 $args = str_replace($match[0], '', $args); // remove parsed substring 206 } 207 208 // get named numeric value argument, ex width=100 209 // numeric value may not be quoted in argument string. 210 $val = '\d+'; 211 $pattern = "/(\w+)\s*=\s*($val)/"; 212 preg_match_all($pattern, $args, $matches, PREG_SET_ORDER); 213 foreach ($matches as $match) { 214 $arg[$match[1]] = (int)$match[2]; 215 $args = str_replace($match[0], '', $args); // remove parsed substring 216 } 217 218 // get width and/or height, specified as non-named arguments 219 $unit = 'px'; 220 $pattern = '/(?:^| )(\d+(%|em|pt|px)?)\s*([,xX]?(\d+(%|em|pt|px)?))?(?: |$)/'; 221 if (preg_match($pattern, $args, $matches)) { 222 if ($matches[4]) { 223 // width and height with unit was given 224 $arg['width'] = $matches[1]; 225 if (!$matches[2]) $arg['width'].= $unit; 226 $arg['height'] = $matches[4]; 227 if (!$matches[5]) $arg['height'].= $unit; 228 } elseif ($matches[2]) { 229 // width or height(=assumed as default) with unit was given 230 // preferred key name given as second parameter of this function 231 $arg[$singlekey] = $matches[1]; 232 if (!$matches[2]) $arg[$singlekey].= $unit; 233 } elseif ($matches[1]) { 234 // numeric token is assumed as width or height 235 $arg[$singlekey] = $matches[1].$unit; 236 } 237 $args = str_replace($matches[0], '', $args); // remove parsed substring 238 } 239 240 // get flags or non-named arguments, ex: showdate, noshowfooter 241 $tokens = preg_split('/\s+/', $args); 242 foreach ($tokens as $token) { 243 if (preg_match('/^(?:!|not?)(.+)/',$token, $matches)) { 244 // denyed/negative prefixed token 245 $arg[$matches[1]] = false; 246 } elseif (preg_match('/^[A-Za-z]/',$token)) { 247 $arg[$token] = true; 248 } 249 } 250 return $arg; 251 } 252 253} 254