Replacement Header Plugin

<?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;
    }
}
?>
 
tutorials/header.txt · Last modified: 2010/05/31 16:21 (external edit)
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki