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_instruction_reference {
105
106    private $list;
107    private $data;
108    private $index;
109    private $name;
110
111    /**
112     * Constructor
113     */
114    public function __construct($list, &$data, $index) {
115        $this->list = $list;
116        $this->data =& $data;
117        $this->index = $index;
118        $this->name = ($data[0] == 'plugin') ? 'plugin_' . $data[1][0] : $data[0];
119    }
120
121    /**
122     *
123     */
124    public function getIndex() {
125        return $this->index;
126    }
127
128    /**
129     *
130     */
131    public function getName() {
132        return $this->name;
133    }
134
135    /**
136     *
137     */
138    public function getData($index) {
139        return $this->data[1][$index];
140    }
141
142    /**
143     *
144     */
145    public function getPluginData($index) {
146        return $this->data[1][1][$index];
147    }
148
149    /**
150     *
151     */
152    public function setPluginData($index, $data) {
153        $this->data[1][1][$index] = $data;
154    }
155
156    /**
157     *
158     */
159    public function unsetPluginData($index) {
160        unset($this->data[1][1][$index]);
161    }
162
163    /**
164     *
165     */
166    public function getRefnotesAttribute($name) {
167        return array_key_exists($name, $this->data[1][1][1]) ? $this->data[1][1][1][$name] : '';
168    }
169
170    /**
171     *
172     */
173    public function setRefnotesAttribute($name, $value) {
174        $this->data[1][1][1][$name] = $value;
175    }
176
177    /**
178     *
179     */
180    public function unsetRefnotesAttribute($name) {
181        unset($this->data[1][1][1][$name]);
182    }
183
184    /**
185     *
186     */
187    public function getPrevious() {
188        return $this->list->getAt($this->index - 1);
189    }
190
191    /**
192     *
193     */
194    public function insertBefore($call) {
195        return $this->list->insert($this->index, $call);
196    }
197}
198
199////////////////////////////////////////////////////////////////////////////////////////////////////
200class refnotes_instruction_list implements Iterator {
201
202    private $event;
203    private $index;
204    private $extraCalls;
205
206    /**
207    * Constructor
208    */
209    public function __construct($event) {
210        $this->event = $event;
211        $this->index = 0;
212        $this->extraCalls = array();
213    }
214
215    /**
216     * Implementation of Iterator interface
217     */
218    public function rewind() {
219        $this->index = 0;
220    }
221
222    /**
223     * Implementation of Iterator interface
224     */
225    public function current() {
226        return new refnotes_instruction_reference($this, $this->event->data->calls[$this->index], $this->index);
227    }
228
229    /**
230     * Implementation of Iterator interface
231     */
232    public function key() {
233        return $this->index;
234    }
235
236    /**
237     * Implementation of Iterator interface
238     */
239    public function next() {
240        ++$this->index;
241    }
242
243    /**
244     * Implementation of Iterator interface
245     */
246    public function valid() {
247        return array_key_exists($this->index, $this->event->data->calls);
248    }
249
250    /**
251     *
252     */
253    public function getAt($index) {
254        return new refnotes_instruction_reference($this, $this->event->data->calls[$index], $index);
255    }
256
257    /**
258     *
259     */
260    public function insert($index, $call) {
261        $this->extraCalls[$index][] = $call;
262    }
263
264    /**
265     *
266     */
267    public function append($call) {
268        $this->extraCalls[count($this->event->data->calls)][] = $call;
269    }
270
271    /**
272     *
273     */
274    public function applyChanges() {
275        if (empty($this->extraCalls)) {
276            return;
277        }
278
279        ksort($this->extraCalls);
280
281        $calls = array();
282        $prevIndex = 0;
283
284        foreach ($this->extraCalls as $index => $extraCalls) {
285            if ($prevIndex < $index) {
286                $slice = array_slice($this->event->data->calls, $prevIndex, $index - $prevIndex);
287                $calls = array_merge($calls, $slice);
288            }
289
290            foreach ($extraCalls as $call) {
291                $calls[] = $call->getData();
292            }
293
294            $prevIndex = $index;
295        }
296
297        $callCount = count($this->event->data->calls);
298
299        if ($prevIndex < $callCount) {
300            $slice = array_slice($this->event->data->calls, $prevIndex, $callCount - $prevIndex);
301            $calls = array_merge($calls, $slice);
302        }
303
304        $offset = $this->event->data->calls[$callCount - 1][2];
305
306        for ($i = count($calls) - 1; $i >= 0; --$i) {
307            if ($calls[$i][2] == -1) {
308                $calls[$i][2] = $offset;
309            }
310            else {
311                $offset = $calls[$i][2];
312            }
313        }
314
315        $this->event->data->calls = $calls;
316        $this->extraCalls = array();
317    }
318}
319