1<?php 2/** 3 * indexnumber-Plugin: Create independent, referencable counters on a page 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Gabriel Birke <gb@birke-software.de> 7 */ 8 9 10if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); 11if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 12require_once(DOKU_PLUGIN.'syntax.php'); 13 14class syntax_plugin_indexnumber extends DokuWiki_Syntax_Plugin { 15 16 /** 17 * Current counter value for each counter 18 * @var type array 19 */ 20 protected $idxnumbers = array(); 21 22 /** 23 * Stack for avoiding nested tags 24 * @var type SplStack 25 */ 26 protected $tag_stack; 27 28 /** 29 * Separator between counter and description 30 * @var type string 31 */ 32 protected $separator; 33 34 public function __construct(){ 35 $this->tag_stack = new SplStack(); 36 $this->separator = $this->getConf('separator'); 37 } 38 39 /** 40 * What about paragraphs? 41 */ 42 function getPType(){ 43 return 'block'; 44 } 45 46 /** 47 * What kind of syntax are we? 48 */ 49 function getType(){ 50 return 'container'; 51 } 52 53 /** 54 * Where to sort in? 55 */ 56 function getSort(){ 57 return 200; 58 } 59 60 function getAllowedTypes() { return array('container', 'substition', 'protected', 'disabled', 'formatting', 'paragraphs'); } 61 62 /** 63 * Connect pattern to lexer 64 */ 65 function connectTo($mode) { 66 $this->Lexer->addEntryPattern('<idxnum .*?>',$mode,'plugin_indexnumber'); 67 } 68 69 function postConnect() { 70 $this->Lexer->addExitPattern('</idxnum>', 'plugin_indexnumber'); 71 } 72 73 /** 74 * Handle the match 75 */ 76 function handle($match, $state, $pos, Doku_Handler $handler) { 77 if($state == DOKU_LEXER_ENTER && preg_match('/<idxnum ([^#]+)(?:#(\d+)(.*))?>/', $match, $matches)) { 78 $idxId = trim($matches[1]); 79 if(empty($this->idxnumbers[$idxId])) { 80 $this->idxnumbers[$idxId] = 1; 81 } 82 else { 83 $this->idxnumbers[$idxId]++; 84 } 85 $description = trim($matches[3]); 86 $description = str_replace(array('<', '>'), array('<', '>'), $description); // Replace angle brackets 87 $tagData = array($state, $idxId, $this->idxnumbers[$idxId], $matches[2], $description); 88 if($this->tag_stack->isEmpty()) { 89 $this->tag_stack->push($tagData); 90 return $tagData; 91 } 92 } 93 elseif($state == DOKU_LEXER_EXIT) { 94 if(!$this->tag_stack->isEmpty()) { 95 $tagData = $this->tag_stack->pop(); 96 $tagData[0] = $state; 97 return $tagData; 98 } 99 } 100 elseif($state == DOKU_LEXER_UNMATCHED) { 101 return array($state, $match); 102 } 103 104 // Ignore errors 105 return array(); 106 107 } 108 109 /** 110 * Create output 111 * 112 * The data is an array with with following keys: 113 * 114 * 0 - state (DOKU_LEXER_ENTER, DOKU_LEXER_EXIT, DOKU_LEXER_UNMATCHED) 115 * 1 - Counter name 116 * 2 - Counter value 117 * 3 - Counter reference id, without # 118 * 4 - Description text 119 */ 120 function render($format, Doku_Renderer $R, $data) { 121 if($format != 'xhtml'){ 122 return false; 123 } 124 if($data[0] == DOKU_LEXER_ENTER) { 125 $anchor = preg_replace('/[^a-z]/i', '_', $data[1]).'_'.$data[2]; 126 $R->doc .= '<div id="'.$anchor.'" class="idxnum_container">'; 127 return true; 128 } 129 elseif($data[0] == DOKU_LEXER_EXIT) { 130 $R->doc .= '<p class="idxnum"><span class="counter">'.$data[1].' '.$data[2].$this->separator.'</span>'; 131 $R->doc .= $data[4].'</p></div>'; 132 return true; 133 } 134 elseif($data[0] == DOKU_LEXER_UNMATCHED) { 135 $R->doc .= $R->_xmlEntities($data[1]); 136 } 137 return false; 138 } 139 140} 141 142 143 144