1<?php 2/** 3 * Box Plugin: Draw highlighting boxes around wiki markup 4 * 5 * Syntax: <box width% classes|title> 6 * width% width of the box, must use % unit 7 * classes one or more classes used to style the box, several predefined styles included in style.css 8 * title (optional) all text after '|' will be rendered above the main code text with a 9 * different style. 10 * 11 * Acknowledgements: 12 * Rounded corners based on snazzy borders by Stu Nicholls (http://www.cssplay.co.uk/boxes/snazzy) 13 * which is in turn based on nifty corners by Alessandro Fulciniti (http://pro.html.it/esempio/nifty/) 14 * 15 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 16 * @author Christopher Smith <chris@jalakai.co.uk> 17 */ 18 19if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); 20if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 21require_once(DOKU_PLUGIN.'syntax.php'); 22 23/** 24 * All DokuWiki plugins to extend the parser/rendering mechanism 25 * need to inherit from this class 26 */ 27class syntax_plugin_box extends DokuWiki_Syntax_Plugin { 28 29 var $title_mode = false; 30 31 // the following are used in rendering and are set by _xhtml_boxopen() 32 var $_xb_colours = ''; 33 var $_content_colours = ''; 34 var $_title_colours = ''; 35 36 function getType(){ return 'protected';} 37 function getAllowedTypes() { return array('container','substition','protected','disabled','formatting','paragraphs'); } 38 function getPType(){ return 'block';} 39 40 // must return a number lower than returned by native 'code' mode (200) 41 function getSort(){ return 195; } 42 43 // override default accepts() method to allow nesting 44 // - ie, to get the plugin accepts its own entry syntax 45 function accepts($mode) { 46 if ($mode == substr(get_class($this), 7)) return true; 47 48 return parent::accepts($mode); 49 } 50 51 /** 52 * Connect pattern to lexer 53 */ 54 function connectTo($mode) { 55 $this->Lexer->addEntryPattern('<box>(?=.*?</box.*?>)',$mode,'plugin_box'); 56 $this->Lexer->addEntryPattern('<box\s[^\r\n\|]*?>(?=.*?</box.*?>)',$mode,'plugin_box'); 57 $this->Lexer->addEntryPattern('<box\|(?=[^\r\n]*?\>.*?</box.*?\>)',$mode,'plugin_box'); 58 $this->Lexer->addEntryPattern('<box\s[^\r\n\|]*?\|(?=[^\r\n]*?>.*?</box.*?>)',$mode,'plugin_box'); 59 } 60 61 function postConnect() { 62 $this->Lexer->addPattern('>', 'plugin_box'); 63 $this->Lexer->addExitPattern('</box.*?>', 'plugin_box'); 64 } 65 66 /** 67 * Handle the match 68 */ 69 function handle($match, $state, $pos, &$handler){ 70 71 switch ($state) { 72 case DOKU_LEXER_ENTER: 73 $data = $this->_boxstyle(trim(substr($match, 4, -1))); 74 if (substr($match, -1) == '|') { 75 $this->title_mode = true; 76 return array('title_open',$data); 77 } else { 78 return array('box_open',$data); 79 } 80 81 case DOKU_LEXER_MATCHED: 82 if ($this->title_mode) { 83 $this->title_mode = false; 84 return array('box_open',''); 85 } else { 86 return array('data', $match); 87 } 88 89 case DOKU_LEXER_UNMATCHED: 90 $handler->_addCall('cdata',array($match), $pos); 91 return false; 92 93 case DOKU_LEXER_EXIT: 94 $data = trim(substr($match, 5, -1)); 95 $title = ($data && $data{0} == "|") ? substr($data,1) : ''; 96 97 return array('box_close', $title); 98 99 } 100 return false; 101 } 102 103 /** 104 * Create output 105 */ 106 function render($mode, &$renderer, $indata) { 107 108 if (empty($indata)) return false; 109 list($instr, $data) = $indata; 110 111 if($mode == 'xhtml'){ 112 switch ($instr) { 113 case 'title_open' : 114 $this->title_mode = true; 115 $renderer->doc .= $this->_xhtml_boxopen($data)."<p class='box_title' {$this->_title_colours}>"; 116 break; 117 118 case 'box_open' : 119 if ($this->title_mode) { 120 $this->title_mode = false; 121 $renderer->doc .= "</p>\n<div class='box_content' {$this->_content_colours}>"; 122 } else { 123 $renderer->doc .= $this->_xhtml_boxopen($data)."<div class='box_content' {$this->_content_colours}>"; 124 } 125 break; 126 127 case 'data' : 128 $renderer->doc .= $renderer->_xmlEntities($data); 129 break; 130 131 case 'box_close' : 132 $renderer->doc .= "</div>\n"; 133 134 if ($data) { 135 $renderer->doc .= "<p class='box_caption' {$this->_title_colours}>".$renderer->_xmlEntities($data)."</p>\n"; 136 } 137 $renderer->doc .= $this->_xhtml_boxclose(); 138 break; 139 } 140 141 return true; 142 } 143 return false; 144 } 145 146 function _boxstyle($str) { 147 if (!strlen($str)) return array(); 148 149 $styles = array(); 150 151 $tokens = preg_split('/\s+/', $str, 9); // limit is defensive 152 foreach ($tokens as $token) { 153 if (preg_match('/^\d*\.?\d+(%|px|em|ex|pt|cm|mm|pi|in)$/', $token)) { 154 $styles['width'] = $token; 155 continue; 156 } 157 158 if (preg_match('/^( 159 (\#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}))| #colorvalue 160 (rgb\(([0-9]{1,3}%?,){2}[0-9]{1,3}%?\)) #rgb triplet 161 )$/x', $token)) { 162 $styles['colour'][] = $token; 163 continue; 164 } 165 166 // restrict token (class names) characters to prevent any malicious data 167 if (preg_match('/[^A-Za-z0-9_-]/',$token)) continue; 168 $styles['class'] = (isset($styles['class']) ? $styles['class'].' ' : '').$token; 169 } 170 if (!empty($styles['colour'])) { 171 $styles['colour'] = $this->_box_colours($styles['colour']); 172 } 173 174 return $styles; 175 } 176 177 function _box_colours($colours) { 178 $triplets = array(); 179 180 // only need the first four colours 181 if (count($colours) > 4) $colours = array_slice($colours,0,4); 182 foreach ($colours as $colour) { 183 $triplet[] = $this->_colourToTriplet($colour); 184 } 185 186 // there must be one colour to get here - the primary background 187 // calculate title background colour if not present 188 if (empty($triplet[1])) { 189 $triplet[1] = $triplet[0]; 190 } 191 192 // calculate outer background colour if not present 193 if (empty($triplet[2])) { 194 $triplet[2] = $triplet[0]; 195 } 196 197 // calculate border colour if not present 198 if (empty($triplet[3])) { 199 $triplet[3] = $triplet[0]; 200 } 201 202 // convert triplets back to style sheet colours 203 $style_colours['content_background'] = 'rgb('.join(',',$triplet[0]).')'; 204 $style_colours['title_background'] = 'rgb('.join(',',$triplet[1]).')'; 205 $style_colours['outer_background'] = 'rgb('.join(',',$triplet[2]).')'; 206 $style_colours['borders'] = 'rgb('.join(',',$triplet[3]).')'; 207 208 return $style_colours; 209 } 210 211 function _colourToTriplet($colour) { 212 if ($colour{0} == '#') { 213 if (strlen($colour) == 4) { 214 // format #FFF 215 return array(hexdec($colour{1}.$colour{1}),hexdec($colour{2}.$colour{2}),hexdec($colour{3}.$colour{3})); 216 } else { 217 // format #FFFFFF 218 return array(hexdec(substr($colour,1,2)),hexdec(substr($colour,3,2)), hexdec(substr($colour,5,2))); 219 } 220 } else { 221 // format rgb(x,y,z) 222 return explode(',',substr($colour,4,-1)); 223 } 224 } 225 226 function _xhtml_boxopen($styles) { 227 $class = 'class="box' . (isset($styles['class']) ? ' '.$styles['class'] : '') . '"'; 228 $style = isset($styles['width']) ? "width: {$styles['width']};" : ''; 229 230 if (isset($styles['colour'])) { 231 $colours = 'background-color: '.$styles['colour']['outer_background'].'; '; 232 $colours .= 'border-color: '.$styles['colour']['borders'].';'; 233 234 $this->_content_colours = 'style="background-color: '.$styles['colour']['content_background'].'; border-color: '.$styles['colour']['borders'].'"'; 235 $this->_title_colours = 'style="background-color: '.$styles['colour']['title_background'].';"'; 236 237 } else { 238 $colours = ''; 239 240 $this->_content_colours = ''; 241 $this->_title_colours = ''; 242 } 243 244 if ($style || $colours) $style = ' style="'.$style.' '.$colours.'"'; 245 if ($colours) $colours = ' style="'.$colours.'"'; 246 247 $this->_xb_colours = $colours; 248 249 $html = "<div $class$style>\n"; 250 $html .=" <b class='xtop'><b class='xb1'$colours></b><b class='xb2'$colours></b><b class='xb3'$colours></b><b class='xb4'$colours></b></b>\n"; 251 $html .=" <div class='xbox'$colours>\n"; 252 253 return $html; 254 } 255 256 function _xhtml_boxclose() { 257 258 $colours = $this->_xb_colours; 259 260 $html = " </div>\n"; 261 $html .= " <b class='xbottom'><b class='xb4'$colours></b><b class='xb3'$colours></b><b class='xb2'$colours></b><b class='xb1'$colours></b></b>\n"; 262 $html .= "</div>\n"; 263 264 return $html; 265 } 266 267} 268 269//Setup VIM: ex: et ts=4 enc=utf-8 :