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('&lt;', '&gt;'), $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