1 | var expressions = require('derby-templates').expressions;
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 | module.exports = EventModel;
|
29 |
|
30 |
|
31 |
|
32 | var nextId = 1;
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 | function ModelRef(model, item, segments, outside) {
|
46 | this.id = nextId++;
|
47 |
|
48 |
|
49 |
|
50 | this.model = model;
|
51 | this.segments = segments;
|
52 |
|
53 |
|
54 | this.item = item;
|
55 |
|
56 |
|
57 |
|
58 | this.outside = outside;
|
59 |
|
60 |
|
61 |
|
62 | this.result = outside.child(item).refChild(this);
|
63 | }
|
64 |
|
65 | ModelRef.prototype.update = function() {
|
66 | var segments = expressions.pathSegments(this.segments);
|
67 | var newItem = expressions.lookup(segments, this.model.data);
|
68 | if (this.item === newItem) return;
|
69 |
|
70 |
|
71 | delete this.outside.child(this.item).refChildren[this.id];
|
72 |
|
73 | this.item = newItem;
|
74 |
|
75 | var container = this.outside.child(this.item);
|
76 |
|
77 |
|
78 | if (!container.refChildren) container.refChildren = new RefChildrenMap();
|
79 | container.refChildren[this.id] = this.result;
|
80 |
|
81 |
|
82 | this.result.update();
|
83 | };
|
84 |
|
85 |
|
86 | function RefOutMap() {}
|
87 | function RefChildrenMap() {}
|
88 | function BindingsMap() {}
|
89 | function ItemContextsMap() {}
|
90 | function EventModelsMap() {}
|
91 |
|
92 | function EventModel() {
|
93 | this.id = nextId++;
|
94 |
|
95 |
|
96 |
|
97 |
|
98 | this.object = null;
|
99 | this.array = null;
|
100 |
|
101 |
|
102 | this.arrayByReference = null;
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 | this.refOut = null;
|
109 |
|
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 | this.refChildren = null;
|
116 |
|
117 | this.bindings = null;
|
118 |
|
119 |
|
120 |
|
121 | this.itemContexts = null;
|
122 | }
|
123 |
|
124 | EventModel.prototype.refChild = function(ref) {
|
125 | if (!this.refChildren) this.refChildren = new RefChildrenMap();
|
126 | var id = ref.id;
|
127 |
|
128 | if (!this.refChildren[id]) {
|
129 | this.refChildren[id] = new EventModel();
|
130 | }
|
131 | return this.refChildren[id];
|
132 | };
|
133 |
|
134 | EventModel.prototype.arrayLookup = function(model, segmentsBefore, segmentsInside) {
|
135 | var segments = expressions.pathSegments(segmentsInside);
|
136 | var item = expressions.lookup(segments, model.data);
|
137 |
|
138 | var source = this.at(segmentsInside);
|
139 |
|
140 |
|
141 |
|
142 | var container = this.at(segmentsBefore);
|
143 |
|
144 | if (!source.refOut) source.refOut = new RefOutMap();
|
145 |
|
146 | var ref = source.refOut[container.id];
|
147 | if (ref == null) {
|
148 | ref = new ModelRef(model, item, segmentsInside, container);
|
149 | source.refOut[container.id] = ref;
|
150 | }
|
151 |
|
152 | return ref;
|
153 | };
|
154 |
|
155 |
|
156 | EventModel.prototype.child = function(segment) {
|
157 | var container;
|
158 | if (typeof segment === 'string') {
|
159 |
|
160 | if (!this.object) this.object = {};
|
161 | container = this.object;
|
162 |
|
163 | } else if (typeof segment === 'number') {
|
164 |
|
165 | if (!this.array) this.array = [];
|
166 | container = this.array;
|
167 |
|
168 | } else if (segment instanceof ModelRef) {
|
169 |
|
170 |
|
171 |
|
172 |
|
173 |
|
174 |
|
175 |
|
176 |
|
177 | return segment.result;
|
178 |
|
179 | } else {
|
180 |
|
181 | if (!this.arrayByReference) this.arrayByReference = [];
|
182 | container = this.arrayByReference;
|
183 | segment = segment.item;
|
184 | }
|
185 |
|
186 | return container[segment] || (container[segment] = new EventModel());
|
187 | };
|
188 |
|
189 |
|
190 |
|
191 |
|
192 |
|
193 |
|
194 |
|
195 |
|
196 |
|
197 | EventModel.prototype.at = function(segments) {
|
198 |
|
199 | if (segments == null) return this;
|
200 |
|
201 | var eventModel = this;
|
202 |
|
203 | for (var i = 0; i < segments.length; i++) {
|
204 | eventModel = eventModel.child(segments[i]);
|
205 | }
|
206 |
|
207 | return eventModel;
|
208 | };
|
209 |
|
210 | EventModel.prototype.isEmpty = function() {
|
211 | if (hasKeys(this.dependancies)) return false;
|
212 | if (hasKeys(this.itemContexts)) return false;
|
213 |
|
214 | if (this.object) {
|
215 | if (hasKeys(this.object)) return false;
|
216 | this.object = null;
|
217 | }
|
218 |
|
219 | if (this.arrayByReference) {
|
220 | for (var i = 0; i < this.arrayByReference.length; i++) {
|
221 | if (this.arrayByReference[i] != null) return false;
|
222 | }
|
223 | this.arrayByReference = null;
|
224 | }
|
225 |
|
226 | if (this.array) {
|
227 | for (var i = 0; i < this.array.length; i++) {
|
228 | if (this.array[i] != null) return false;
|
229 | }
|
230 | this.array = null;
|
231 | }
|
232 |
|
233 | return true;
|
234 | };
|
235 |
|
236 | function hasKeys(object) {
|
237 | for (var key in object) {
|
238 | return true;
|
239 | }
|
240 | return false;
|
241 | }
|
242 |
|
243 |
|
244 |
|
245 |
|
246 | EventModel.prototype._addItemContext = function(context) {
|
247 | if (!context._id) context._id = nextId++;
|
248 | if (!this.itemContexts) this.itemContexts = new ItemContextsMap();
|
249 | this.itemContexts[context._id] = context;
|
250 | };
|
251 |
|
252 | EventModel.prototype._removeItemContext = function(context) {
|
253 | if (this.itemContexts) {
|
254 | delete this.itemContexts[context._id];
|
255 | }
|
256 | };
|
257 |
|
258 | EventModel.prototype._addBinding = function(binding) {
|
259 | var bindings = this.bindings || (this.bindings = new BindingsMap());
|
260 | binding.eventModels || (binding.eventModels = new EventModelsMap());
|
261 | bindings[binding.id] = binding;
|
262 | binding.eventModels[this.id] = this;
|
263 | };
|
264 |
|
265 |
|
266 |
|
267 | EventModel.prototype.addBinding = function(segments, binding) {
|
268 | this.at(segments)._addBinding(binding);
|
269 | };
|
270 |
|
271 |
|
272 |
|
273 | EventModel.prototype.addItemContext = function(segments, context) {
|
274 | this.at(segments)._addItemContext(context);
|
275 | };
|
276 |
|
277 | EventModel.prototype.removeBinding = function(binding) {
|
278 | if (!binding.eventModels) return;
|
279 | for (var id in binding.eventModels) {
|
280 | var eventModel = binding.eventModels[id];
|
281 | if (eventModel.bindings) delete eventModel.bindings[binding.id];
|
282 | }
|
283 | binding.eventModels = null;
|
284 | };
|
285 |
|
286 | EventModel.prototype._each = function(segments, pos, fn) {
|
287 |
|
288 | if (this.refChildren) {
|
289 | for (var id in this.refChildren) {
|
290 | var refChild = this.refChildren[id];
|
291 | if (refChild) refChild._each(segments, pos, fn);
|
292 | }
|
293 | }
|
294 |
|
295 | if (segments.length === pos) {
|
296 | fn(this);
|
297 | return;
|
298 | }
|
299 |
|
300 | var segment = segments[pos];
|
301 | var child;
|
302 | if (typeof segment === 'string') {
|
303 |
|
304 |
|
305 |
|
306 | child = this.object && this.object[segment];
|
307 | if (child) child._each(segments, pos + 1, fn);
|
308 |
|
309 | } else {
|
310 |
|
311 | child = this.array && this.array[segment];
|
312 | if (child) child._each(segments, pos + 1, fn);
|
313 |
|
314 | child = this.arrayByReference && this.arrayByReference[segment];
|
315 | if (child) child._each(segments, pos + 1, fn);
|
316 | }
|
317 | };
|
318 |
|
319 |
|
320 |
|
321 |
|
322 | EventModel.prototype.localUpdate = function(previous, pass) {
|
323 | if (this.bindings) {
|
324 | for (var id in this.bindings) {
|
325 | var binding = this.bindings[id];
|
326 | if (binding) binding.update(previous, pass);
|
327 | }
|
328 | }
|
329 |
|
330 |
|
331 |
|
332 | if (this.refOut) {
|
333 | for (var id in this.refOut) {
|
334 | var ref = this.refOut[id];
|
335 | if (ref) ref.update();
|
336 | }
|
337 | }
|
338 | };
|
339 |
|
340 |
|
341 | EventModel.prototype.update = function(previous, pass) {
|
342 | this.localUpdate(previous, pass);
|
343 |
|
344 | if (this.object) {
|
345 | for (var key in this.object) {
|
346 | var binding = this.object[key];
|
347 | if (binding) binding.update();
|
348 | }
|
349 | }
|
350 |
|
351 | if (this.array) {
|
352 | for (var i = 0; i < this.array.length; i++) {
|
353 | var binding = this.array[i];
|
354 | if (binding) binding.update();
|
355 | }
|
356 | }
|
357 |
|
358 | if (this.arrayByReference) {
|
359 | for (var i = 0; i < this.arrayByReference.length; i++) {
|
360 | var binding = this.arrayByReference[i];
|
361 | if (binding) binding.update();
|
362 | }
|
363 | }
|
364 | };
|
365 |
|
366 |
|
367 |
|
368 | EventModel.prototype._updateChildItemContexts = function(from, to) {
|
369 | if (!this.arrayByReference) return;
|
370 |
|
371 | if (from == null) from = 0;
|
372 | if (to == null) to = this.arrayByReference.length;
|
373 |
|
374 | for (var i = from; i < to; i++) {
|
375 | var contexts = this.arrayByReference[i] &&
|
376 | this.arrayByReference[i].itemContexts;
|
377 | if (contexts) {
|
378 | for (var key in contexts) {
|
379 | contexts[key].item = i;
|
380 | }
|
381 | }
|
382 | }
|
383 | };
|
384 |
|
385 |
|
386 |
|
387 | EventModel.prototype._updateArray = function(from, to) {
|
388 | if (!this.array) return;
|
389 |
|
390 | if (from == null) from = 0;
|
391 | if (to == null) to = this.array.length;
|
392 |
|
393 | for (var i = from; i < to; i++) {
|
394 | var binding = this.array[i];
|
395 | if (binding) binding.update();
|
396 | }
|
397 | };
|
398 |
|
399 | EventModel.prototype._updateObject = function() {
|
400 | if (this.object) {
|
401 | for (var key in this.object) {
|
402 | var binding = this.object[key];
|
403 | if (binding) binding.update();
|
404 | }
|
405 | }
|
406 | };
|
407 |
|
408 | EventModel.prototype._set = function(previous, pass) {
|
409 |
|
410 |
|
411 |
|
412 |
|
413 |
|
414 | this.update(previous, pass);
|
415 | };
|
416 |
|
417 |
|
418 | EventModel.prototype._insert = function(index, howMany) {
|
419 |
|
420 | this._updateArray(index);
|
421 |
|
422 |
|
423 | if (this.arrayByReference && this.arrayByReference.length > index) {
|
424 |
|
425 |
|
426 |
|
427 |
|
428 |
|
429 |
|
430 | for (var i = 0; i < howMany; i++) {
|
431 | this.arrayByReference.splice(index, 0, null);
|
432 | }
|
433 |
|
434 |
|
435 | this._updateChildItemContexts(index + howMany);
|
436 | }
|
437 |
|
438 |
|
439 | if (this.bindings) {
|
440 | for (var id in this.bindings) {
|
441 | var binding = this.bindings[id];
|
442 | if (binding) binding.insert(index, howMany);
|
443 | }
|
444 | }
|
445 | this._updateObject();
|
446 | };
|
447 |
|
448 |
|
449 | EventModel.prototype._remove = function(index, howMany) {
|
450 |
|
451 | this._updateArray(index);
|
452 |
|
453 | if (this.arrayByReference) {
|
454 |
|
455 | this.arrayByReference.splice(index, howMany);
|
456 |
|
457 | this._updateChildItemContexts(index);
|
458 | }
|
459 |
|
460 |
|
461 | if (this.bindings) {
|
462 | for (var id in this.bindings) {
|
463 | var binding = this.bindings[id];
|
464 | if (binding) binding.remove(index, howMany);
|
465 | }
|
466 | }
|
467 | this._updateObject();
|
468 | };
|
469 |
|
470 |
|
471 | EventModel.prototype._move = function(from, to, howMany) {
|
472 |
|
473 |
|
474 | var first, end;
|
475 | if (from < to) {
|
476 | first = from;
|
477 | end = to + howMany;
|
478 | } else {
|
479 | first = to;
|
480 | end = from + howMany;
|
481 | }
|
482 |
|
483 |
|
484 | this._updateArray(first, end);
|
485 |
|
486 |
|
487 | var arr = this.arrayByReference;
|
488 | if (arr && arr.length > first) {
|
489 |
|
490 | var values = arr.splice(from, howMany);
|
491 |
|
492 |
|
493 | arr.splice.apply(arr, [to, 0].concat(values));
|
494 |
|
495 |
|
496 | this._updateChildItemContexts(first, end);
|
497 | }
|
498 |
|
499 |
|
500 | if (this.bindings) {
|
501 | for (var id in this.bindings) {
|
502 | var binding = this.bindings[id];
|
503 | if (binding) binding.move(from, to, howMany);
|
504 | }
|
505 | }
|
506 | this._updateObject();
|
507 | };
|
508 |
|
509 |
|
510 |
|
511 |
|
512 | EventModel.prototype.mutate = function(segments, fn) {
|
513 |
|
514 |
|
515 |
|
516 |
|
517 | this._each(segments, 0, fn);
|
518 |
|
519 |
|
520 |
|
521 |
|
522 | for (var i = 0, len = segments.length; i++ < len;) {
|
523 | var wildcardSegments = segments.slice(0, i);
|
524 | wildcardSegments.push('*');
|
525 | this._each(wildcardSegments, 0, childSetWildcard);
|
526 | }
|
527 | };
|
528 |
|
529 | function childSetWildcard(child) {
|
530 | child._set();
|
531 | }
|
532 |
|
533 | EventModel.prototype.set = function(segments, previous, pass) {
|
534 | this.mutate(segments, function childSet(child) {
|
535 | child._set(previous, pass);
|
536 | });
|
537 | };
|
538 |
|
539 | EventModel.prototype.insert = function(segments, index, howMany) {
|
540 | this.mutate(segments, function childInsert(child) {
|
541 | child._insert(index, howMany);
|
542 | });
|
543 | };
|
544 |
|
545 | EventModel.prototype.remove = function(segments, index, howMany) {
|
546 | this.mutate(segments, function childRemove(child) {
|
547 | child._remove(index, howMany);
|
548 | });
|
549 | };
|
550 |
|
551 | EventModel.prototype.move = function(segments, from, to, howMany) {
|
552 | this.mutate(segments, function childMove(child) {
|
553 | child._move(from, to, howMany);
|
554 | });
|
555 | };
|
556 |
|
\ | No newline at end of file |