1<?php
2
3/**
4 * Plugin RefNotes: Handling of instruction array
5 *
6 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
7 * @author     Mykola Ostrovskyy <dwpforge@gmail.com>
8 */
9
10//////////////////////////////////////////////////////////////////////////////////////////////////
11class refnotes_instruction {
12
13    protected $data;
14
15    /**
16     * Constructor
17     */
18    public function __construct($name, $data, $offset = -1) {
19        $this->data = array($name, $data, $offset);
20    }
21
22    /**
23     *
24     */
25    public function getData() {
26        return $this->data;
27    }
28}
29
30////////////////////////////////////////////////////////////////////////////////////////////////////
31class refnotes_nest_instruction extends refnotes_instruction {
32
33    /**
34     * Constructor
35     */
36    public function __construct($data) {
37        parent::__construct('nest', array($data));
38    }
39}
40
41////////////////////////////////////////////////////////////////////////////////////////////////////
42class refnotes_plugin_instruction extends refnotes_instruction {
43
44    /**
45     * Constructor
46     */
47    public function __construct($name, $data, $type, $text, $offset = -1) {
48        parent::__construct('plugin', array($name, $data, $type, $text), $offset);
49    }
50}
51
52////////////////////////////////////////////////////////////////////////////////////////////////////
53class refnotes_notes_instruction extends refnotes_plugin_instruction {
54
55    /**
56     * Constructor
57     */
58    public function __construct($type, $attributes, $data = NULL) {
59        $pluginData[0] = $type;
60        $pluginData[1] = $attributes;
61
62        if (!empty($data)) {
63            $pluginData[2] = $data;
64        }
65
66        parent::__construct('refnotes_notes', $pluginData, DOKU_LEXER_SPECIAL, '');
67    }
68}
69
70////////////////////////////////////////////////////////////////////////////////////////////////////
71class refnotes_notes_style_instruction extends refnotes_notes_instruction {
72
73    /**
74     * Constructor
75     */
76    public function __construct($namespace, $data) {
77        parent::__construct('style', array('ns' => $namespace), $data);
78    }
79}
80
81////////////////////////////////////////////////////////////////////////////////////////////////////
82class refnotes_notes_map_instruction extends refnotes_notes_instruction {
83
84    /**
85     * Constructor
86     */
87    public function __construct($namespace, $data) {
88        parent::__construct('map', array('ns' => $namespace), $data);
89    }
90}
91
92////////////////////////////////////////////////////////////////////////////////////////////////////
93class refnotes_notes_render_instruction extends refnotes_notes_instruction {
94
95    /**
96     * Constructor
97     */
98    public function __construct($namespace) {
99        parent::__construct('render', array('ns' => $namespace));
100    }
101}
102
103////////////////////////////////////////////////////////////////////////////////////////////////////
104class refnotes_notes_render_block_instruction extends refnotes_notes_instruction {
105
106    /**
107     * Constructor
108     */
109    public function __construct($block) {
110        parent::__construct('block', $block);
111    }
112}
113
114////////////////////////////////////////////////////////////////////////////////////////////////////
115class refnotes_instruction_reference {
116
117    private $list;
118    private $data;
119    private $index;
120    private $name;
121
122    /**
123     * Constructor
124     */
125    public function __construct($list, &$data, $index) {
126        $this->list = $list;
127        $this->data =& $data;
128        $this->index = $index;
129        $this->name = ($data[0] == 'plugin') ? 'plugin_' . $data[1][0] : $data[0];
130    }
131
132    /**
133     *
134     */
135    public function getIndex() {
136        return $this->index;
137    }
138
139    /**
140     *
141     */
142    public function getName() {
143        return $this->name;
144    }
145
146    /**
147     *
148     */
149    public function getData($index) {
150        return $this->data[1][$index];
151    }
152
153    /**
154     *
155     */
156    public function getPluginData($index) {
157        return $this->data[1][1][$index];
158    }
159
160    /**
161     *
162     */
163    public function setPluginData($index, $data) {
164        $this->data[1][1][$index] = $data;
165    }
166
167    /**
168     *
169     */
170    public function unsetPluginData($index) {
171        unset($this->data[1][1][$index]);
172    }
173
174    /**
175     *
176     */
177    public function getRefnotesAttribute($name) {
178        return array_key_exists($name, $this->data[1][1][1]) ? $this->data[1][1][1][$name] : '';
179    }
180
181    /**
182     *
183     */
184    public function setRefnotesAttribute($name, $value) {
185        $this->data[1][1][1][$name] = $value;
186    }
187
188    /**
189     *
190     */
191    public function unsetRefnotesAttribute($name) {
192        unset($this->data[1][1][1][$name]);
193    }
194
195    /**
196     *
197     */
198    public function getPrevious() {
199        return $this->list->getAt($this->index - 1);
200    }
201
202    /**
203     *
204     */
205    public function insertBefore($call) {
206        return $this->list->insert($this->index, $call);
207    }
208
209    /**
210     *
211     */
212    public function insertAfter($call) {
213        return $this->list->insert($this->index + 1, $call);
214    }
215}
216
217////////////////////////////////////////////////////////////////////////////////////////////////////
218class refnotes_instruction_list implements Iterator {
219
220    private $event;
221    private $index;
222    private $extraCalls;
223
224    /**
225    * Constructor
226    */
227    public function __construct($event) {
228        $this->event = $event;
229        $this->index = 0;
230        $this->extraCalls = array();
231    }
232
233    /**
234     * Implementation of Iterator interface
235     */
236    public function rewind() {
237        $this->index = 0;
238    }
239
240    /**
241     * Implementation of Iterator interface
242     */
243    public function current() {
244        return new refnotes_instruction_reference($this, $this->event->data->calls[$this->index], $this->index);
245    }
246
247    /**
248     * Implementation of Iterator interface
249     */
250    public function key() {
251        return $this->index;
252    }
253
254    /**
255     * Implementation of Iterator interface
256     */
257    public function next() {
258        ++$this->index;
259    }
260
261    /**
262     * Implementation of Iterator interface
263     */
264    public function valid() {
265        return array_key_exists($this->index, $this->event->data->calls);
266    }
267
268    /**
269     *
270     */
271    public function getAt($index) {
272        return new refnotes_instruction_reference($this, $this->event->data->calls[$index], $index);
273    }
274
275    /**
276     *
277     */
278    public function insert($index, $call) {
279        $this->extraCalls[$index][] = $call;
280    }
281
282    /**
283     *
284     */
285    public function append($call) {
286        $this->extraCalls[count($this->event->data->calls)][] = $call;
287    }
288
289    /**
290     *
291     */
292    public function applyChanges() {
293        if (empty($this->extraCalls)) {
294            return;
295        }
296
297        ksort($this->extraCalls);
298
299        $calls = array();
300        $prevIndex = 0;
301
302        foreach ($this->extraCalls as $index => $extraCalls) {
303            if ($prevIndex < $index) {
304                $slice = array_slice($this->event->data->calls, $prevIndex, $index - $prevIndex);
305                $calls = array_merge($calls, $slice);
306            }
307
308            foreach ($extraCalls as $call) {
309                $calls[] = $call->getData();
310            }
311
312            $prevIndex = $index;
313        }
314
315        $callCount = count($this->event->data->calls);
316
317        if ($prevIndex < $callCount) {
318            $slice = array_slice($this->event->data->calls, $prevIndex, $callCount - $prevIndex);
319            $calls = array_merge($calls, $slice);
320        }
321
322        $offset = $this->event->data->calls[$callCount - 1][2];
323
324        for ($i = count($calls) - 1; $i >= 0; --$i) {
325            if ($calls[$i][2] == -1) {
326                $calls[$i][2] = $offset;
327            }
328            else {
329                $offset = $calls[$i][2];
330            }
331        }
332
333        $this->event->data->calls = $calls;
334        $this->extraCalls = array();
335    }
336}
337