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