lib/plugins/header/syntax.php
<?php /** * Plugin Header: Header Replacement Plugin - EXPERIMENTAL * * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) * @author Christopher Smith <chris@jalakai.co.uk> */ if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); require_once(DOKU_PLUGIN.'syntax.php'); /** * All DokuWiki plugins to extend the parser/rendering mechanism * need to inherit from this class */ class syntax_plugin_header extends DokuWiki_Syntax_Plugin { var $idx = false; var $placeholder = '<h# id="@">'; var $counters = array(NULL,0,0,0,0,0,0); var $in_use = false; // settings (move to plugin config) FIXME! var $dflt_depth = 3; // max header depth at which counters will apply var $depth = 0; var $dflt_pattern = '#.'; // pattern used for counter substitution var $pattern = '#.'; function getInfo(){ return array( 'author' => 'Christopher Smith', 'email' => 'chris@jalakai.co.uk', 'date' => '2006-04-05', 'name' => 'Header Replacement Plugin', 'desc' => 'Replace/Upgrade Heading Syntax', 'url' => 'http://wiki.splitbrain.org/plugin:header', ); } function getSort() { return 45; } /* same as header */ function getType() { return 'baseonly'; } function getAllowedTypes() { return array('formatting'); } function getPType(){ return 'normal';} function connectTo($mode) { $this->Lexer->addSpecialPattern('~~OH[^\n]*~~',$mode,'plugin_header'); $this->Lexer->addEntryPattern('[ \t]*={2,6}(?=[^\n]+={2,}[ \t]*\n)',$mode,'plugin_header'); } function postConnect() { $this->Lexer->addExitPattern('={2,}[ \t]*(?=\n)', 'plugin_header'); } function handle($match, $state, $pos, &$handler){ static $level; switch ($state) { case DOKU_LEXER_ENTER: $level = 7 - strlen(trim($match)); if ($handler->status['section']) { $handler->_addCall('section_close', array(), $pos); } else { $handler->_addCall('p_close', array(), $pos); } return array('header_open',$level,$pos); // case DOKU_LEXER_MATCHED: case DOKU_LEXER_SPECIAL: $data = trim(substr($match, 4, -2)); $settings = array(); if (preg_match('/^(?::(\d))?(?:\|(.*))?$/',$data,$settings)) { array_shift($settings); } else { $settings = array($this->dflt_depth,$this->dflt_pattern); } $data = array('header_settings', $settings, $pos); // put the settings instruction at the front of the instruction stack array_unshift($handler->calls, array('plugin', array('header', $data, $pos), $pos)); return array('header_none',NULL,NULL); case DOKU_LEXER_UNMATCHED: return array('header_cdata', $match, $pos); case DOKU_LEXER_EXIT: // hack! add our plugin call and after that a section open call $data = array('header_close', $level, $pos); $handler->_addCall('plugin', array('header', $data, $pos), $pos); $handler->_addCall('section_open', array($level), $pos); $handler->status['section'] = true; return array('header_none',NULL,NULL); } return NULL; } function render($mode, &$renderer, $indata) { list($instr, $data, $pos) = $indata; if($mode == 'xhtml'){ switch ($instr) { case 'header_settings' : if (count($data) >= 2) { list($this->depth, $this->pattern) = $data; } $this->in_use = true; break; case 'header_open' : # $renderer->doc .= "</p>"; $this->xhtml_header_open($renderer, $data, $pos); break; case 'header_cdata' : $renderer->doc .= $renderer->_xmlEntities($data); break; case 'header_close' : $this->xhtml_header_close($renderer, $data); # $renderer->doc .= "<p>"; break; } return true; } return false; } function xhtml_header_open(&$renderer, $level, $pos) { global $conf; //copied from parser/xhtml.php //handle section editing if($level <= $conf['maxseclevel']){ // add button for last section if any if($renderer->lastsec) $renderer->_secedit($renderer->lastsec,$pos-1); // remember current position $renderer->lastsec = $pos; } $this->idx = strlen($renderer->doc); $renderer->doc .= $this->placeholder; } function xhtml_header_close(&$renderer, $level) { global $conf; // grab header title text // 1: locate our header text // for efficiency, look first at the idx into renderer->doc saved in xhtml_header_open // if we don't find anything, then try looking from the start of the document if (($this->idx = strpos($renderer->doc, $this->placeholder, $this->idx)) === false) { $this->idx = strpos($renderer->doc, $this->placeholder); } // rendered header data is from located position to the end of the string $header = substr($renderer->doc,$this->idx+strlen($this->placeholder)); // remove any xhtml markup, leaving the CDATA $title = preg_replace('/<.*?>/','',$header); // create an id for the header $id = $renderer->_headerToLink($title,'true'); //handle TOC if($level >= $conf['toptoclevel'] && $level <= $conf['maxtoclevel']){ if ($title) { // the TOC is one of our standard ul list arrays ;-) $renderer->toc[] = array('hid' => $id, 'title' => $title, 'type' => 'ul', 'level' => $level-$conf['toptoclevel']+1); } } // rebuild the rendered doc // contents up to header_open + header_open + counter + rendered header data + header_close $renderer->doc = substr($renderer->doc,0,$this->idx); $renderer->doc .= '<h'.$level.' id="'.$id.'">'; if ($this->in_use && ($level <= $this->depth)) $renderer->doc .= '<span class="counter">'.$this->xhtml_header_counter($level).'</span>'; $renderer->doc .= $header; $renderer->doc .= '</h'.$level.'>'.DOKU_LF; // reset status variables $this->idx = false; } function xhtml_header_counter($level) { // sanity check if ($level > $this->depth) return ''; // increment the counter for this level $this->counter[$level]++; // reset counters at deeper levels for ($i=$level+1; $i<=$this->depth; $i++) $this->counter[$i] = 0; // generate the counter string, performing conversions/substitutions as requested $out = ''; for ($i=1; $i<=$level; $i++) { $x = $this->counter[$i]; $r = $this->roman($x); $values = array($x,strtolower($r),$r, chr(ord('A')+$x-1), chr(ord('a')+$x-1)); $out .= str_replace(array('#','i','I','A','a'),$values,$this->pattern); } return $out; } // generate roman numerals function roman($n) { $ones = array('','I','II','III','IV','V','VI','VII','VIII','IX'); $tens = array('','X','XX','XXX','XL','L','LX','LXX','LXXX','XC'); $huns = array('','C','CC','CCC','CD','D','DC','DCC','DCCC','CM'); $thou = array('','M','MM','MMM'); $idx = str_pad(strrev((string)($n)),4,'0'); $out = $thou[$idx{3}].$huns[$idx{2}].$tens[$idx{1}].$ones[$idx{0}]; return $out; } } ?>