1<?php
2/**
3 * DokuWiki Plugin variants (Syntax Component)
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  Michael Hamann <michael@content-space.de>
7 */
8
9// must be run within Dokuwiki
10if (!defined('DOKU_INC')) die();
11
12/**
13 * The variants syntax class, defines the if/else syntax
14 */
15class syntax_plugin_variants_variants extends DokuWiki_Syntax_Plugin {
16    /**
17     * @return string The plugin type
18     */
19    public function getType() {
20        return 'container';
21    }
22
23    /**
24     * @return string The paragraph type
25     */
26    public function getPType() {
27        return 'block';
28    }
29
30    /**
31     * @return int The sort number
32     */
33    public function getSort() {
34        return 200;
35    }
36
37    /**
38     * @return array The allowed types that may be used inside the plugin
39     */
40    public function getAllowedTypes() {
41        return array('container', 'baseonly', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs');
42    }
43
44    /**
45     * @param string $mode The mode that shall be tested
46     * @return bool If the mode is accepted inside the plugin
47     */
48    public function accepts($mode) {
49        // allow nesting!
50        if ($mode == substr(get_class($this), 7)) return true;
51        return parent::accepts($mode);
52    }
53
54    /**
55     * Add the patterns
56     *
57     * @param int $mode The current mode
58     */
59    public function connectTo($mode) {
60        $this->Lexer->addEntryPattern('<ifvar [^=>]+=[^>]+>',$mode,'plugin_variants_variants');
61        $this->Lexer->addPattern('<else>', 'plugin_variants_variants');
62    }
63
64    /**
65     * Add the exit patterns
66     */
67    public function postConnect() {
68        $this->Lexer->addExitPattern('</ifvar>','plugin_variants_variants');
69    }
70
71    /**
72     * handle a syntax match
73     *
74     * @param string       $match The match
75     * @param int          $state The current handler state
76     * @param int          $pos   The position in the page
77     * @param Doku_Handler $handler The handler object
78     * @return bool False, this plugin doesn't need to be added by the handler
79     */
80    public function handle($match, $state, $pos, Doku_Handler $handler){
81        switch ($state) {
82            case DOKU_LEXER_ENTER:
83                // setup call writer
84                $condition = substr($match, 7, -1);
85                $CallWriter = new syntax_plugin_variants_callwriter($handler->CallWriter, $condition, $pos);
86                $handler->CallWriter =& $CallWriter;
87
88                break;
89            case DOKU_LEXER_MATCHED:
90                // start else branch
91                $handler->CallWriter->startElse();
92                // else
93                break;
94            case DOKU_LEXER_UNMATCHED:
95                // store cdata
96                $handler->_addCall('cdata', array($match), $pos);
97                break;
98            case DOKU_LEXER_EXIT:
99                // end call writer
100                $handler->CallWriter->process();
101                $ReWriter = & $handler->CallWriter;
102                $handler->CallWriter = & $ReWriter->CallWriter;
103                break;
104        }
105        return false;
106    }
107
108    /**
109     * Render the output of the variants plugin
110     *
111     * @param string        $mode      The output mode
112     * @param Doku_Renderer $renderer  The renderer
113     * @param array         $data      The data from the handler
114     * @return bool If anything has been rendered
115     */
116    public function render($mode, Doku_Renderer $renderer, $data) {
117        $renderer->nocache();
118        /** @var Input $INPUT */
119        global $INPUT;
120        list($condition, $ifcalls, $elsecalls) = $data;
121
122        list($key, $value) = explode('=', $condition, 2);
123
124        $condresult = true;
125        if (substr($key, -1) == '!') {
126            $key = substr($key, 0, -1);
127            $condresult = false;
128        }
129
130        trim($key); trim($value);
131
132        if ($INPUT->has($key) && ($INPUT->str($key) == $value) == $condresult) {
133            $renderer->nest($ifcalls);
134        } else {
135            $renderer->nest($elsecalls);
136        }
137
138        return true;
139    }
140}
141
142/**
143 * Call writer for the variants plugins
144 */
145class syntax_plugin_variants_callwriter {
146    private $ifcalls = array();
147    private $elsecalls = array();
148    private $condition;
149    private $in_else = false;
150    private $startpos;
151    /** @var Doku_Handler_CallWriter $CallWriter */
152    var $CallWriter;
153
154    /**
155     * Construct a new syntax plugin variants callwriter
156     *
157     * @param Doku_Handler_CallWriter $CallWriter The parent handler
158     * @param string                  $condition The condition
159     * @param int                     $startpos The position of the start of the syntax
160     */
161    function __construct(& $CallWriter, $condition, $startpos) {
162        $this->CallWriter = & $CallWriter;
163        $this->condition = $condition;
164        $this->startpos = $startpos;
165    }
166
167    function startElse() {
168        $this->in_else = true;
169    }
170
171    /**
172     * Adds a single call
173     *
174     * @param array $call The call that shall be written
175     */
176    function writeCall($call) {
177        if ($this->in_else) {
178            $this->elsecalls[] = $call;
179        } else {
180            $this->ifcalls[] = $call;
181        }
182    }
183
184    /**
185     * Adds an array of calls
186     *
187     * @param array $calls The calls that shall be written
188     */
189    function writeCalls($calls) {
190        if ($this->in_else) {
191            $this->elsecalls = array_merge($this->elsecalls, $calls);
192        } else {
193            $this->ifcalls = array_merge($this->ifcalls, $calls);
194        }
195    }
196
197    function finalise() {
198        $this->process();
199        $this->CallWriter->finalise();
200        unset($this->CallWriter);
201    }
202
203    function process() {
204        // process blocks
205        $B = new Doku_Handler_Block();
206        $this->ifcalls = $B->process($this->ifcalls);
207        $B = new Doku_Handler_Block();
208        $this->elsecalls = $B->process($this->elsecalls);
209
210        $this->CallWriter->writeCall(array('plugin',array('variants_variants', array($this->condition, $this->ifcalls, $this->elsecalls), DOKU_LEXER_SPECIAL, ''), $this->startpos));
211        $this->ifcalls = array();
212        $this->elsecalls = array();
213    }
214}
215
216// vim:ts=4:sw=4:et:
217