102b64928SChristopher Smith<?php 202b64928SChristopher Smith/** 302b64928SChristopher Smith * Box Plugin: Draw highlighting boxes around wiki markup 402b64928SChristopher Smith * 502b64928SChristopher Smith * Syntax: <box width% classes|title> 602b64928SChristopher Smith * width% width of the box, must use % unit 702b64928SChristopher Smith * classes one or more classes used to style the box, several predefined styles included in style.css 802b64928SChristopher Smith * title (optional) all text after '|' will be rendered above the main code text with a 902b64928SChristopher Smith * different style. 1002b64928SChristopher Smith * 1102b64928SChristopher Smith * Acknowledgements: 1202b64928SChristopher Smith * Rounded corners based on snazzy borders by Stu Nicholls (http://www.cssplay.co.uk/boxes/snazzy) 1302b64928SChristopher Smith * which is in turn based on nifty corners by Alessandro Fulciniti (http://pro.html.it/esempio/nifty/) 1402b64928SChristopher Smith * 1502b64928SChristopher Smith * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 1602b64928SChristopher Smith * @author Christopher Smith <chris@jalakai.co.uk> 17*bd01c8ecSGerry Weißbach * @author i-net software <tools@inetsoftware.de> 1802b64928SChristopher Smith */ 1902b64928SChristopher Smith 2002b64928SChristopher Smithif(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); 2102b64928SChristopher Smithif(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 2202b64928SChristopher Smithrequire_once(DOKU_PLUGIN.'syntax.php'); 2302b64928SChristopher Smith 2402b64928SChristopher Smith/** 2502b64928SChristopher Smith * All DokuWiki plugins to extend the parser/rendering mechanism 2602b64928SChristopher Smith * need to inherit from this class 2702b64928SChristopher Smith */ 2802b64928SChristopher Smithclass syntax_plugin_box extends DokuWiki_Syntax_Plugin { 2902b64928SChristopher Smith 3002b64928SChristopher Smith var $title_mode = false; 31*bd01c8ecSGerry Weißbach var $title_pos = array(); 32*bd01c8ecSGerry Weißbach var $title_name = array(); 3302b64928SChristopher Smith 3402b64928SChristopher Smith // the following are used in rendering and are set by _xhtml_boxopen() 35*bd01c8ecSGerry Weißbach var $_xb_colours = array(); 3602b64928SChristopher Smith var $_content_colours = ''; 3702b64928SChristopher Smith var $_title_colours = ''; 3802b64928SChristopher Smith 39*bd01c8ecSGerry Weißbach /** 40*bd01c8ecSGerry Weißbach * return some info 41*bd01c8ecSGerry Weißbach */ 42*bd01c8ecSGerry Weißbach function getInfo(){ 43*bd01c8ecSGerry Weißbach return array( 44*bd01c8ecSGerry Weißbach 'author' => 'Christopher Smith / i-net software', 45*bd01c8ecSGerry Weißbach 'email' => 'tools@inetsoftware.de', 46*bd01c8ecSGerry Weißbach 'date' => '2010-04-21 (original 2008-11-11)', 47*bd01c8ecSGerry Weißbach 'name' => 'Box Plugin', 48*bd01c8ecSGerry Weißbach 'desc' => 'Boxes with titles, colour and rounded corners. 49*bd01c8ecSGerry Weißbach Syntax: <box width class colours|title> ... </box|caption> 50*bd01c8ecSGerry Weißbach width, class, colours title & caption are optional. 51*bd01c8ecSGerry Weißbach The title can include some wiki markup, the box 52*bd01c8ecSGerry Weißbach contents can include almost any wiki markup.', 53*bd01c8ecSGerry Weißbach 'url' => 'http://www.inetsoftware.de/other-products/dokuwiki-plugins/boxes', 54*bd01c8ecSGerry Weißbach ); 55*bd01c8ecSGerry Weißbach } 56*bd01c8ecSGerry Weißbach 5702b64928SChristopher Smith function getType(){ return 'protected';} 5802b64928SChristopher Smith function getAllowedTypes() { return array('container','substition','protected','disabled','formatting','paragraphs'); } 5902b64928SChristopher Smith function getPType(){ return 'block';} 6002b64928SChristopher Smith 6102b64928SChristopher Smith // must return a number lower than returned by native 'code' mode (200) 6202b64928SChristopher Smith function getSort(){ return 195; } 6302b64928SChristopher Smith 6402b64928SChristopher Smith // override default accepts() method to allow nesting 6502b64928SChristopher Smith // - ie, to get the plugin accepts its own entry syntax 6602b64928SChristopher Smith function accepts($mode) { 6702b64928SChristopher Smith if ($mode == substr(get_class($this), 7)) return true; 6802b64928SChristopher Smith 6902b64928SChristopher Smith return parent::accepts($mode); 7002b64928SChristopher Smith } 7102b64928SChristopher Smith 7202b64928SChristopher Smith /** 7302b64928SChristopher Smith * Connect pattern to lexer 7402b64928SChristopher Smith */ 7502b64928SChristopher Smith function connectTo($mode) { 7602b64928SChristopher Smith $this->Lexer->addEntryPattern('<box>(?=.*?</box.*?>)',$mode,'plugin_box'); 7702b64928SChristopher Smith $this->Lexer->addEntryPattern('<box\s[^\r\n\|]*?>(?=.*?</box.*?>)',$mode,'plugin_box'); 7802b64928SChristopher Smith $this->Lexer->addEntryPattern('<box\|(?=[^\r\n]*?\>.*?</box.*?\>)',$mode,'plugin_box'); 7902b64928SChristopher Smith $this->Lexer->addEntryPattern('<box\s[^\r\n\|]*?\|(?=[^\r\n]*?>.*?</box.*?>)',$mode,'plugin_box'); 8002b64928SChristopher Smith } 8102b64928SChristopher Smith 8202b64928SChristopher Smith function postConnect() { 8302b64928SChristopher Smith $this->Lexer->addPattern('>', 'plugin_box'); 8402b64928SChristopher Smith $this->Lexer->addExitPattern('</box.*?>', 'plugin_box'); 8502b64928SChristopher Smith } 8602b64928SChristopher Smith 8702b64928SChristopher Smith /** 8802b64928SChristopher Smith * Handle the match 8902b64928SChristopher Smith */ 9002b64928SChristopher Smith function handle($match, $state, $pos, &$handler){ 9102b64928SChristopher Smith 9202b64928SChristopher Smith switch ($state) { 9302b64928SChristopher Smith case DOKU_LEXER_ENTER: 9402b64928SChristopher Smith $data = $this->_boxstyle(trim(substr($match, 4, -1))); 9502b64928SChristopher Smith if (substr($match, -1) == '|') { 9602b64928SChristopher Smith $this->title_mode = true; 97*bd01c8ecSGerry Weißbach return array('title_open',$data, $pos); 9802b64928SChristopher Smith } else { 99*bd01c8ecSGerry Weißbach return array('box_open',$data, $pos); 10002b64928SChristopher Smith } 10102b64928SChristopher Smith 10202b64928SChristopher Smith case DOKU_LEXER_MATCHED: 10302b64928SChristopher Smith if ($this->title_mode) { 10402b64928SChristopher Smith $this->title_mode = false; 105*bd01c8ecSGerry Weißbach return array('box_open','', $pos); 10602b64928SChristopher Smith } else { 107*bd01c8ecSGerry Weißbach return array('data', $match, $pos); 10802b64928SChristopher Smith } 10902b64928SChristopher Smith 11002b64928SChristopher Smith case DOKU_LEXER_UNMATCHED: 111*bd01c8ecSGerry Weißbach if ($this->title_mode) { 112*bd01c8ecSGerry Weißbach return array('data', $match, $pos); 113*bd01c8ecSGerry Weißbach } 114*bd01c8ecSGerry Weißbach 11502b64928SChristopher Smith $handler->_addCall('cdata',array($match), $pos); 11602b64928SChristopher Smith return false; 11702b64928SChristopher Smith case DOKU_LEXER_EXIT: 118*bd01c8ecSGerry Weißbach $pos += strlen($match); // has to be done becvause the ending tag comes after $pos 11902b64928SChristopher Smith $data = trim(substr($match, 5, -1)); 12002b64928SChristopher Smith $title = ($data && $data{0} == "|") ? substr($data,1) : ''; 12102b64928SChristopher Smith 122*bd01c8ecSGerry Weißbach return array('box_close', $title, $pos); 12302b64928SChristopher Smith 12402b64928SChristopher Smith } 12502b64928SChristopher Smith return false; 12602b64928SChristopher Smith } 12702b64928SChristopher Smith 12802b64928SChristopher Smith /** 12902b64928SChristopher Smith * Create output 13002b64928SChristopher Smith */ 13102b64928SChristopher Smith function render($mode, &$renderer, $indata) { 132*bd01c8ecSGerry Weißbach global $ID, $ACT; 13302b64928SChristopher Smith 134*bd01c8ecSGerry Weißbach // $pos is for the current position in the wiki page 13502b64928SChristopher Smith if (empty($indata)) return false; 136*bd01c8ecSGerry Weißbach list($instr, $data, $pos) = $indata; 13702b64928SChristopher Smith 13802b64928SChristopher Smith if($mode == 'xhtml'){ 13902b64928SChristopher Smith switch ($instr) { 14002b64928SChristopher Smith case 'title_open' : 14102b64928SChristopher Smith $this->title_mode = true; 142*bd01c8ecSGerry Weißbach $this->title_pos[] = $pos; // Start Position for Section Editing 143*bd01c8ecSGerry Weißbach $renderer->doc .= $this->_xhtml_boxopen($data); 144*bd01c8ecSGerry Weißbach $renderer->doc .= "<h2 class='box_title " . (method_exists($renderer, "finishSectionEdit") ? $renderer->startSectionEdit($pos, 'section', 'box') : "") . " '{$this->_title_colours}>"; 14502b64928SChristopher Smith break; 14602b64928SChristopher Smith 14702b64928SChristopher Smith case 'box_open' : 14802b64928SChristopher Smith if ($this->title_mode) { 14902b64928SChristopher Smith $this->title_mode = false; 150*bd01c8ecSGerry Weißbach $renderer->doc .= "</h2>\n<div class='box_content'{$this->_content_colours}>"; 15102b64928SChristopher Smith } else { 152*bd01c8ecSGerry Weißbach $this->title_pos[] = $pos; // Start Position for Section Editing 153*bd01c8ecSGerry Weißbach $this->title_name[] = 'box_' . 'no-title' . '_' . md5(time()); 15402b64928SChristopher Smith $renderer->doc .= $this->_xhtml_boxopen($data)."<div class='box_content'{$this->_content_colours}>"; 15502b64928SChristopher Smith } 15602b64928SChristopher Smith break; 15702b64928SChristopher Smith 15802b64928SChristopher Smith case 'data' : 159*bd01c8ecSGerry Weißbach $output = $renderer->_xmlEntities($data); 160*bd01c8ecSGerry Weißbach 161*bd01c8ecSGerry Weißbach if ( $this->title_mode ) { 162*bd01c8ecSGerry Weißbach $this->title_name[] = 'box_' . cleanID($output) . '_' . md5($output); 163*bd01c8ecSGerry Weißbach $hid = $renderer->_headerToLink($output,true); 164*bd01c8ecSGerry Weißbach $renderer->doc .= '<a id="' . $hid . '" name="' . $hid . '">' . $output . '</a>'; 165*bd01c8ecSGerry Weißbach break; 166*bd01c8ecSGerry Weißbach } 167*bd01c8ecSGerry Weißbach 168*bd01c8ecSGerry Weißbach $renderer->doc .= $output; 16902b64928SChristopher Smith break; 17002b64928SChristopher Smith 17102b64928SChristopher Smith case 'box_close' : 17202b64928SChristopher Smith $renderer->doc .= "</div>\n"; 17302b64928SChristopher Smith 17402b64928SChristopher Smith if ($data) { 17502b64928SChristopher Smith $renderer->doc .= "<p class='box_caption'{$this->_title_colours}>".$renderer->_xmlEntities($data)."</p>\n"; 17602b64928SChristopher Smith } 177*bd01c8ecSGerry Weißbach 178*bd01c8ecSGerry Weißbach // insert the section edit button befor the box is closed - array_pop makes sure we take the last box 179*bd01c8ecSGerry Weißbach if ( $this->getConf('allowSectionEdit') && $ACT != 'preview' ) { 180*bd01c8ecSGerry Weißbach $renderer->nocache(); 181*bd01c8ecSGerry Weißbach 182*bd01c8ecSGerry Weißbach 183*bd01c8ecSGerry Weißbach if ( auth_quickaclcheck($ID) > AUTH_READ ) { 184*bd01c8ecSGerry Weißbach $title = array_pop($this->title_name); // Clean up 185*bd01c8ecSGerry Weißbach if ( method_exists($renderer, "finishSectionEdit") ) { 186*bd01c8ecSGerry Weißbach $renderer->finishSectionEdit($pos); 187*bd01c8ecSGerry Weißbach } 188*bd01c8ecSGerry Weißbach } 189*bd01c8ecSGerry Weißbach } 190*bd01c8ecSGerry Weißbach 19102b64928SChristopher Smith $renderer->doc .= $this->_xhtml_boxclose(); 192*bd01c8ecSGerry Weißbach 19302b64928SChristopher Smith break; 19402b64928SChristopher Smith } 19502b64928SChristopher Smith 19602b64928SChristopher Smith return true; 19702b64928SChristopher Smith } 19802b64928SChristopher Smith return false; 19902b64928SChristopher Smith } 20002b64928SChristopher Smith 20102b64928SChristopher Smith function _boxstyle($str) { 20202b64928SChristopher Smith if (!strlen($str)) return array(); 20302b64928SChristopher Smith 20402b64928SChristopher Smith $styles = array(); 20502b64928SChristopher Smith 20602b64928SChristopher Smith $tokens = preg_split('/\s+/', $str, 9); // limit is defensive 20702b64928SChristopher Smith foreach ($tokens as $token) { 20802b64928SChristopher Smith if (preg_match('/^\d*\.?\d+(%|px|em|ex|pt|cm|mm|pi|in)$/', $token)) { 20902b64928SChristopher Smith $styles['width'] = $token; 21002b64928SChristopher Smith continue; 21102b64928SChristopher Smith } 21202b64928SChristopher Smith 21302b64928SChristopher Smith if (preg_match('/^( 21402b64928SChristopher Smith (\#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}))| #colorvalue 21502b64928SChristopher Smith (rgb\(([0-9]{1,3}%?,){2}[0-9]{1,3}%?\)) #rgb triplet 21602b64928SChristopher Smith )$/x', $token)) { 21702b64928SChristopher Smith $styles['colour'][] = $token; 21802b64928SChristopher Smith continue; 21902b64928SChristopher Smith } 22002b64928SChristopher Smith 22102b64928SChristopher Smith // restrict token (class names) characters to prevent any malicious data 22202b64928SChristopher Smith if (preg_match('/[^A-Za-z0-9_-]/',$token)) continue; 22302b64928SChristopher Smith $styles['class'] = (isset($styles['class']) ? $styles['class'].' ' : '').$token; 22402b64928SChristopher Smith } 22502b64928SChristopher Smith if (!empty($styles['colour'])) { 22602b64928SChristopher Smith $styles['colour'] = $this->_box_colours($styles['colour']); 22702b64928SChristopher Smith } 22802b64928SChristopher Smith 22902b64928SChristopher Smith return $styles; 23002b64928SChristopher Smith } 23102b64928SChristopher Smith 23202b64928SChristopher Smith function _box_colours($colours) { 23302b64928SChristopher Smith $triplets = array(); 23402b64928SChristopher Smith 23502b64928SChristopher Smith // only need the first four colours 23602b64928SChristopher Smith if (count($colours) > 4) $colours = array_slice($colours,0,4); 23702b64928SChristopher Smith foreach ($colours as $colour) { 23802b64928SChristopher Smith $triplet[] = $this->_colourToTriplet($colour); 23902b64928SChristopher Smith } 24002b64928SChristopher Smith 24102b64928SChristopher Smith // there must be one colour to get here - the primary background 24202b64928SChristopher Smith // calculate title background colour if not present 24302b64928SChristopher Smith if (empty($triplet[1])) { 24402b64928SChristopher Smith $triplet[1] = $triplet[0]; 24502b64928SChristopher Smith } 24602b64928SChristopher Smith 24702b64928SChristopher Smith // calculate outer background colour if not present 24802b64928SChristopher Smith if (empty($triplet[2])) { 24902b64928SChristopher Smith $triplet[2] = $triplet[0]; 25002b64928SChristopher Smith } 25102b64928SChristopher Smith 25202b64928SChristopher Smith // calculate border colour if not present 25302b64928SChristopher Smith if (empty($triplet[3])) { 25402b64928SChristopher Smith $triplet[3] = $triplet[0]; 25502b64928SChristopher Smith } 25602b64928SChristopher Smith 25702b64928SChristopher Smith // convert triplets back to style sheet colours 25802b64928SChristopher Smith $style_colours['content_background'] = 'rgb('.join(',',$triplet[0]).')'; 25902b64928SChristopher Smith $style_colours['title_background'] = 'rgb('.join(',',$triplet[1]).')'; 26002b64928SChristopher Smith $style_colours['outer_background'] = 'rgb('.join(',',$triplet[2]).')'; 26102b64928SChristopher Smith $style_colours['borders'] = 'rgb('.join(',',$triplet[3]).')'; 26202b64928SChristopher Smith 26302b64928SChristopher Smith return $style_colours; 26402b64928SChristopher Smith } 26502b64928SChristopher Smith 26602b64928SChristopher Smith function _colourToTriplet($colour) { 26702b64928SChristopher Smith if ($colour{0} == '#') { 26802b64928SChristopher Smith if (strlen($colour) == 4) { 26902b64928SChristopher Smith // format #FFF 27002b64928SChristopher Smith return array(hexdec($colour{1}.$colour{1}),hexdec($colour{2}.$colour{2}),hexdec($colour{3}.$colour{3})); 27102b64928SChristopher Smith } else { 27202b64928SChristopher Smith // format #FFFFFF 27302b64928SChristopher Smith return array(hexdec(substr($colour,1,2)),hexdec(substr($colour,3,2)), hexdec(substr($colour,5,2))); 27402b64928SChristopher Smith } 27502b64928SChristopher Smith } else { 27602b64928SChristopher Smith // format rgb(x,y,z) 27702b64928SChristopher Smith return explode(',',substr($colour,4,-1)); 27802b64928SChristopher Smith } 27902b64928SChristopher Smith } 28002b64928SChristopher Smith 28102b64928SChristopher Smith function _xhtml_boxopen($styles) { 28202b64928SChristopher Smith $class = 'class="box' . (isset($styles['class']) ? ' '.$styles['class'] : '') . '"'; 28302b64928SChristopher Smith $style = isset($styles['width']) ? "width: {$styles['width']};" : ''; 28402b64928SChristopher Smith 28502b64928SChristopher Smith if (isset($styles['colour'])) { 28602b64928SChristopher Smith $colours = 'background-color: '.$styles['colour']['outer_background'].'; '; 28702b64928SChristopher Smith $colours .= 'border-color: '.$styles['colour']['borders'].';'; 28802b64928SChristopher Smith 28902b64928SChristopher Smith $this->_content_colours = 'style="background-color: '.$styles['colour']['content_background'].'; border-color: '.$styles['colour']['borders'].'"'; 29002b64928SChristopher Smith $this->_title_colours = 'style="background-color: '.$styles['colour']['title_background'].';"'; 29102b64928SChristopher Smith 29202b64928SChristopher Smith } else { 29302b64928SChristopher Smith $colours = ''; 29402b64928SChristopher Smith 29502b64928SChristopher Smith $this->_content_colours = ''; 29602b64928SChristopher Smith $this->_title_colours = ''; 29702b64928SChristopher Smith } 29802b64928SChristopher Smith 29902b64928SChristopher Smith if ($style || $colours) $style = ' style="'.$style.' '.$colours.'"'; 30002b64928SChristopher Smith if ($colours) $colours = ' style="'.$colours.'"'; 30102b64928SChristopher Smith 302*bd01c8ecSGerry Weißbach $this->_xb_colours[] = $colours; 30302b64928SChristopher Smith 30402b64928SChristopher Smith $html = "<div $class$style>\n"; 305*bd01c8ecSGerry Weißbach 306*bd01c8ecSGerry Weißbach // Don't do box extras if there is no style for them 307*bd01c8ecSGerry Weißbach if ( !empty($colours) ) { 308*bd01c8ecSGerry Weißbach $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"; 30902b64928SChristopher Smith $html .=" <div class='xbox'$colours>\n"; 310*bd01c8ecSGerry Weißbach } 31102b64928SChristopher Smith 31202b64928SChristopher Smith return $html; 31302b64928SChristopher Smith } 31402b64928SChristopher Smith 31502b64928SChristopher Smith function _xhtml_boxclose() { 31602b64928SChristopher Smith 317*bd01c8ecSGerry Weißbach $colours = array_pop($this->_xb_colours); 31802b64928SChristopher Smith 319*bd01c8ecSGerry Weißbach // Don't do box extras if there is no style for them 320*bd01c8ecSGerry Weißbach if ( !empty($colours) ) { 32102b64928SChristopher Smith $html = " </div>\n"; 322*bd01c8ecSGerry Weißbach $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"; 323*bd01c8ecSGerry Weißbach } 324*bd01c8ecSGerry Weißbach $html .= "</div> <!-- Extras -->\n"; 32502b64928SChristopher Smith 32602b64928SChristopher Smith return $html; 32702b64928SChristopher Smith } 32802b64928SChristopher Smith 32902b64928SChristopher Smith} 33002b64928SChristopher Smith 33102b64928SChristopher Smith//Setup VIM: ex: et ts=4 enc=utf-8 :