vasille 2.2.2 → 2.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +34 -28
  2. package/cdn/es2015.js +18 -724
  3. package/cdn/es5.js +687 -957
  4. package/flow-typed/vasille.js +14 -59
  5. package/lib/core/core.js +3 -3
  6. package/lib/index.js +5 -4
  7. package/lib/models/array-model.js +6 -9
  8. package/lib/models/object-model.js +3 -17
  9. package/lib/node/node.js +6 -3
  10. package/lib/views/object-view.js +1 -1
  11. package/lib-node/binding/attribute.js +35 -0
  12. package/lib-node/binding/binding.js +33 -0
  13. package/lib-node/binding/class.js +48 -0
  14. package/lib-node/binding/style.js +27 -0
  15. package/lib-node/core/core.js +243 -0
  16. package/lib-node/core/destroyable.js +49 -0
  17. package/lib-node/core/errors.js +23 -0
  18. package/lib-node/core/ivalue.js +63 -0
  19. package/lib-node/functional/options.js +2 -0
  20. package/lib-node/index.js +54 -0
  21. package/lib-node/models/array-model.js +218 -0
  22. package/lib-node/models/listener.js +134 -0
  23. package/lib-node/models/map-model.js +70 -0
  24. package/lib-node/models/model.js +2 -0
  25. package/lib-node/models/object-model.js +82 -0
  26. package/lib-node/models/set-model.js +66 -0
  27. package/lib-node/node/app.js +54 -0
  28. package/lib-node/node/node.js +885 -0
  29. package/lib-node/node/watch.js +23 -0
  30. package/lib-node/spec/html.js +2 -0
  31. package/lib-node/spec/react.js +2 -0
  32. package/lib-node/spec/svg.js +2 -0
  33. package/lib-node/value/expression.js +90 -0
  34. package/lib-node/value/mirror.js +60 -0
  35. package/lib-node/value/pointer.js +30 -0
  36. package/lib-node/value/reference.js +55 -0
  37. package/lib-node/views/array-view.js +21 -0
  38. package/lib-node/views/base-view.js +43 -0
  39. package/lib-node/views/map-view.js +18 -0
  40. package/lib-node/views/object-view.js +20 -0
  41. package/lib-node/views/repeat-node.js +71 -0
  42. package/lib-node/views/set-view.js +19 -0
  43. package/package.json +21 -17
  44. package/types/core/core.d.ts +4 -4
  45. package/types/functional/options.d.ts +2 -2
  46. package/types/index.d.ts +10 -7
  47. package/types/models/array-model.d.ts +1 -1
  48. package/types/models/object-model.d.ts +1 -1
  49. package/types/node/node.d.ts +5 -4
  50. package/types/node/watch.d.ts +2 -2
  51. package/types/views/repeat-node.d.ts +2 -2
  52. package/lib/core/executor.js +0 -154
  53. package/lib/core/signal.js +0 -50
  54. package/lib/core/slot.js +0 -47
  55. package/lib/functional/components.js +0 -17
  56. package/lib/functional/merge.js +0 -41
  57. package/lib/functional/models.js +0 -26
  58. package/lib/functional/reactivity.js +0 -33
  59. package/lib/functional/stack.js +0 -127
  60. package/lib/node/interceptor.js +0 -83
  61. package/lib/v/index.js +0 -23
  62. package/lib/views/repeater.js +0 -63
  63. package/types/functional/components.d.ts +0 -4
  64. package/types/functional/merge.d.ts +0 -1
  65. package/types/functional/models.d.ts +0 -10
  66. package/types/functional/reactivity.d.ts +0 -11
  67. package/types/functional/stack.d.ts +0 -24
  68. package/types/v/index.d.ts +0 -36
@@ -0,0 +1,885 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DebugNode = exports.DebugPrivate = exports.SwitchedNode = exports.SwitchedNodePrivate = exports.Component = exports.Extension = exports.Tag = exports.INode = exports.INodePrivate = exports.TextNode = exports.TextNodePrivate = exports.Fragment = exports.FragmentPrivate = void 0;
4
+ const core_1 = require("../core/core");
5
+ const ivalue_1 = require("../core/ivalue");
6
+ const reference_1 = require("../value/reference");
7
+ const expression_1 = require("../value/expression");
8
+ const attribute_1 = require("../binding/attribute");
9
+ const class_1 = require("../binding/class");
10
+ const style_1 = require("../binding/style");
11
+ const errors_1 = require("../core/errors");
12
+ /**
13
+ * Represents a Vasille.js node
14
+ * @class FragmentPrivate
15
+ * @extends ReactivePrivate
16
+ */
17
+ class FragmentPrivate extends core_1.ReactivePrivate {
18
+ constructor() {
19
+ super();
20
+ this.$seal();
21
+ }
22
+ /**
23
+ * Pre-initializes the base of a fragment
24
+ * @param app {App} the app node
25
+ * @param parent {Fragment} the parent node
26
+ */
27
+ preinit(app, parent) {
28
+ this.app = app;
29
+ this.parent = parent;
30
+ }
31
+ /**
32
+ * Unlinks all bindings
33
+ */
34
+ $destroy() {
35
+ this.next = null;
36
+ this.prev = null;
37
+ super.$destroy();
38
+ }
39
+ }
40
+ exports.FragmentPrivate = FragmentPrivate;
41
+ /**
42
+ * This class is symbolic
43
+ * @extends Reactive
44
+ */
45
+ class Fragment extends core_1.Reactive {
46
+ /**
47
+ * Constructs a Vasille Node
48
+ * @param input
49
+ * @param $ {FragmentPrivate}
50
+ */
51
+ constructor(input, $) {
52
+ super(input, $ || new FragmentPrivate);
53
+ /**
54
+ * The children list
55
+ * @type Array
56
+ */
57
+ this.children = new Set;
58
+ this.lastChild = null;
59
+ }
60
+ /**
61
+ * Gets the app of node
62
+ */
63
+ get app() {
64
+ return this.$.app;
65
+ }
66
+ /**
67
+ * Prepare to init fragment
68
+ * @param app {AppNode} app of node
69
+ * @param parent {Fragment} parent of node
70
+ * @param data {*} additional data
71
+ */
72
+ preinit(app, parent, data) {
73
+ const $ = this.$;
74
+ $.preinit(app, parent);
75
+ }
76
+ init() {
77
+ const ret = super.init();
78
+ this.ready();
79
+ return ret;
80
+ }
81
+ compose(input) {
82
+ input.slot && input.slot(this);
83
+ }
84
+ /** To be overloaded: ready event handler */
85
+ ready() {
86
+ // empty
87
+ }
88
+ /**
89
+ * Pushes a node to children immediately
90
+ * @param node {Fragment} A node to push
91
+ * @protected
92
+ */
93
+ pushNode(node) {
94
+ if (this.lastChild) {
95
+ this.lastChild.$.next = node;
96
+ }
97
+ node.$.prev = this.lastChild;
98
+ this.lastChild = node;
99
+ this.children.add(node);
100
+ }
101
+ /**
102
+ * Find first node in element if so exists
103
+ * @return {?Element}
104
+ * @protected
105
+ */
106
+ findFirstChild() {
107
+ let first;
108
+ this.children.forEach(child => {
109
+ first = first || child.findFirstChild();
110
+ });
111
+ return first;
112
+ }
113
+ /**
114
+ * Append a node to end of element
115
+ * @param node {Node} node to insert
116
+ */
117
+ appendNode(node) {
118
+ const $ = this.$;
119
+ if ($.next) {
120
+ $.next.insertAdjacent(node);
121
+ }
122
+ else {
123
+ $.parent.appendNode(node);
124
+ }
125
+ }
126
+ /**
127
+ * Insert a node as a sibling of this
128
+ * @param node {Node} node to insert
129
+ */
130
+ insertAdjacent(node) {
131
+ const child = this.findFirstChild();
132
+ const $ = this.$;
133
+ if (child) {
134
+ child.parentElement.insertBefore(node, child);
135
+ }
136
+ else if ($.next) {
137
+ $.next.insertAdjacent(node);
138
+ }
139
+ else {
140
+ $.parent.appendNode(node);
141
+ }
142
+ }
143
+ /**
144
+ * Defines a text fragment
145
+ * @param text {String | IValue} A text fragment string
146
+ * @param cb {function (TextNode)} Callback if previous is slot name
147
+ */
148
+ text(text, cb) {
149
+ const $ = this.$;
150
+ const node = new TextNode();
151
+ node.preinit($.app, this, text);
152
+ this.pushNode(node);
153
+ cb && cb(node);
154
+ }
155
+ debug(text) {
156
+ if (this.$.app.debugUi) {
157
+ const node = new DebugNode();
158
+ node.preinit(this.$.app, this, text);
159
+ this.pushNode(node);
160
+ }
161
+ }
162
+ /**
163
+ * Defines a tag element
164
+ * @param tagName {String} the tag name
165
+ * @param input
166
+ * @param cb {function(Tag, *)} callback
167
+ */
168
+ tag(tagName, input, cb) {
169
+ const $ = this.$;
170
+ const node = new Tag(input);
171
+ input.slot = cb || input.slot;
172
+ node.preinit($.app, this, tagName);
173
+ node.init();
174
+ this.pushNode(node);
175
+ node.ready();
176
+ return node.node;
177
+ }
178
+ /**
179
+ * Defines a custom element
180
+ * @param node {Fragment} vasille element to insert
181
+ * @param callback {function($ : *)}
182
+ */
183
+ create(node, callback) {
184
+ const $ = this.$;
185
+ node.$.parent = this;
186
+ node.preinit($.app, this);
187
+ node.input.slot = callback || node.input.slot;
188
+ this.pushNode(node);
189
+ return node.init();
190
+ }
191
+ /**
192
+ * Defines an if node
193
+ * @param cond {IValue} condition
194
+ * @param cb {function(Fragment)} callback to run on true
195
+ * @return {this}
196
+ */
197
+ if(cond, cb) {
198
+ const node = new SwitchedNode();
199
+ node.preinit(this.$.app, this);
200
+ node.init();
201
+ this.pushNode(node);
202
+ node.addCase(this.case(cond, cb));
203
+ node.ready();
204
+ }
205
+ else(cb) {
206
+ if (this.lastChild instanceof SwitchedNode) {
207
+ this.lastChild.addCase(this.default(cb));
208
+ }
209
+ else {
210
+ throw (0, errors_1.userError)('wrong `else` function use', 'logic-error');
211
+ }
212
+ }
213
+ elif(cond, cb) {
214
+ if (this.lastChild instanceof SwitchedNode) {
215
+ this.lastChild.addCase(this.case(cond, cb));
216
+ }
217
+ else {
218
+ throw (0, errors_1.userError)('wrong `elif` function use', 'logic-error');
219
+ }
220
+ }
221
+ /**
222
+ * Create a case for switch
223
+ * @param cond {IValue<boolean>}
224
+ * @param cb {function(Fragment) : void}
225
+ * @return {{cond : IValue, cb : (function(Fragment) : void)}}
226
+ */
227
+ case(cond, cb) {
228
+ return { cond, cb };
229
+ }
230
+ /**
231
+ * @param cb {(function(Fragment) : void)}
232
+ * @return {{cond : IValue, cb : (function(Fragment) : void)}}
233
+ */
234
+ default(cb) {
235
+ return { cond: trueIValue, cb };
236
+ }
237
+ insertBefore(node) {
238
+ const $ = this.$;
239
+ node.$.prev = $.prev;
240
+ node.$.next = this;
241
+ if ($.prev) {
242
+ $.prev.$.next = node;
243
+ }
244
+ $.prev = node;
245
+ }
246
+ insertAfter(node) {
247
+ const $ = this.$;
248
+ node.$.prev = this;
249
+ node.$.next = $.next;
250
+ $.next = node;
251
+ }
252
+ remove() {
253
+ const $ = this.$;
254
+ if ($.next) {
255
+ $.next.$.prev = $.prev;
256
+ }
257
+ if ($.prev) {
258
+ $.prev.$.next = $.next;
259
+ }
260
+ }
261
+ $destroy() {
262
+ this.children.forEach(child => child.$destroy());
263
+ this.children.clear();
264
+ this.lastChild = null;
265
+ if (this.$.parent.lastChild === this) {
266
+ this.$.parent.lastChild = this.$.prev;
267
+ }
268
+ super.$destroy();
269
+ }
270
+ }
271
+ exports.Fragment = Fragment;
272
+ const trueIValue = new reference_1.Reference(true);
273
+ /**
274
+ * The private part of a text node
275
+ * @class TextNodePrivate
276
+ * @extends FragmentPrivate
277
+ */
278
+ class TextNodePrivate extends FragmentPrivate {
279
+ constructor() {
280
+ super();
281
+ this.$seal();
282
+ }
283
+ /**
284
+ * Pre-initializes a text node
285
+ * @param app {AppNode} the app node
286
+ * @param parent
287
+ * @param text {IValue}
288
+ */
289
+ preinitText(app, parent, text) {
290
+ super.preinit(app, parent);
291
+ this.node = document.createTextNode(text instanceof ivalue_1.IValue ? text.$ : text);
292
+ if (text instanceof ivalue_1.IValue) {
293
+ this.bindings.add(new expression_1.Expression((v) => {
294
+ this.node.replaceData(0, -1, v);
295
+ }, true, text));
296
+ }
297
+ }
298
+ /**
299
+ * Clear node data
300
+ */
301
+ $destroy() {
302
+ super.$destroy();
303
+ }
304
+ }
305
+ exports.TextNodePrivate = TextNodePrivate;
306
+ /**
307
+ * Represents a text node
308
+ * @class TextNode
309
+ * @extends Fragment
310
+ */
311
+ class TextNode extends Fragment {
312
+ constructor($ = new TextNodePrivate()) {
313
+ super({}, $);
314
+ this.$seal();
315
+ }
316
+ preinit(app, parent, text) {
317
+ const $ = this.$;
318
+ if (!text) {
319
+ throw (0, errors_1.internalError)('wrong TextNode::$preninit call');
320
+ }
321
+ $.preinitText(app, parent, text);
322
+ $.parent.appendNode($.node);
323
+ }
324
+ findFirstChild() {
325
+ return this.$.node;
326
+ }
327
+ $destroy() {
328
+ this.$.node.remove();
329
+ this.$.$destroy();
330
+ super.$destroy();
331
+ }
332
+ }
333
+ exports.TextNode = TextNode;
334
+ /**
335
+ * The private part of a base node
336
+ * @class INodePrivate
337
+ * @extends FragmentPrivate
338
+ */
339
+ class INodePrivate extends FragmentPrivate {
340
+ constructor() {
341
+ super();
342
+ /**
343
+ * Defines if node is unmounted
344
+ * @type {boolean}
345
+ */
346
+ this.unmounted = false;
347
+ this.$seal();
348
+ }
349
+ $destroy() {
350
+ super.$destroy();
351
+ }
352
+ }
353
+ exports.INodePrivate = INodePrivate;
354
+ /**
355
+ * Vasille node which can manipulate an element node
356
+ * @class INode
357
+ * @extends Fragment
358
+ */
359
+ class INode extends Fragment {
360
+ /**
361
+ * Constructs a base node
362
+ * @param input
363
+ * @param $ {?INodePrivate}
364
+ */
365
+ constructor(input, $) {
366
+ super(input, $ || new INodePrivate);
367
+ this.$seal();
368
+ }
369
+ /**
370
+ * Get the bound node
371
+ */
372
+ get node() {
373
+ return this.$.node;
374
+ }
375
+ /**
376
+ * Bind attribute value
377
+ * @param name {String} name of attribute
378
+ * @param value {IValue} value
379
+ */
380
+ attr(name, value) {
381
+ const $ = this.$;
382
+ const attr = new attribute_1.AttributeBinding(this, name, value);
383
+ $.bindings.add(attr);
384
+ }
385
+ /**
386
+ * Set attribute value
387
+ * @param name {string} name of attribute
388
+ * @param value {string} value
389
+ */
390
+ setAttr(name, value) {
391
+ if (typeof value === 'boolean') {
392
+ if (value) {
393
+ this.$.node.setAttribute(name, "");
394
+ }
395
+ }
396
+ else {
397
+ this.$.node.setAttribute(name, `${value}`);
398
+ }
399
+ return this;
400
+ }
401
+ /**
402
+ * Adds a CSS class
403
+ * @param cl {string} Class name
404
+ */
405
+ addClass(cl) {
406
+ this.$.node.classList.add(cl);
407
+ return this;
408
+ }
409
+ /**
410
+ * Adds some CSS classes
411
+ * @param cls {...string} classes names
412
+ */
413
+ removeClasse(cl) {
414
+ this.$.node.classList.remove(cl);
415
+ return this;
416
+ }
417
+ /**
418
+ * Bind a CSS class
419
+ * @param className {IValue}
420
+ */
421
+ bindClass(className) {
422
+ const $ = this.$;
423
+ $.bindings.add(new class_1.DynamicalClassBinding(this, className));
424
+ return this;
425
+ }
426
+ /**
427
+ * Bind a floating class name
428
+ * @param cond {IValue} condition
429
+ * @param className {string} class name
430
+ */
431
+ floatingClass(cond, className) {
432
+ this.$.bindings.add(new class_1.StaticClassBinding(this, className, cond));
433
+ return this;
434
+ }
435
+ /**
436
+ * Defines a style attribute
437
+ * @param name {String} name of style attribute
438
+ * @param value {IValue} value
439
+ */
440
+ style(name, value) {
441
+ const $ = this.$;
442
+ if ($.node instanceof HTMLElement) {
443
+ $.bindings.add(new style_1.StyleBinding(this, name, value));
444
+ }
445
+ else {
446
+ throw (0, errors_1.userError)('style can be applied to HTML elements only', 'non-html-element');
447
+ }
448
+ return this;
449
+ }
450
+ /**
451
+ * Sets a style property value
452
+ * @param prop {string} Property name
453
+ * @param value {string} Property value
454
+ */
455
+ setStyle(prop, value) {
456
+ if (this.$.node instanceof HTMLElement) {
457
+ this.$.node.style.setProperty(prop, value);
458
+ }
459
+ else {
460
+ throw (0, errors_1.userError)("Style can be set for HTML elements only", "non-html-element");
461
+ }
462
+ return this;
463
+ }
464
+ /**
465
+ * Add a listener for an event
466
+ * @param name {string} Event name
467
+ * @param handler {function (Event)} Event handler
468
+ * @param options {Object | boolean} addEventListener options
469
+ */
470
+ listen(name, handler, options) {
471
+ this.$.node.addEventListener(name, handler, options);
472
+ return this;
473
+ }
474
+ insertAdjacent(node) {
475
+ this.$.node.parentNode.insertBefore(node, this.$.node);
476
+ }
477
+ /**
478
+ * A v-show & ngShow alternative
479
+ * @param cond {IValue} show condition
480
+ */
481
+ bindShow(cond) {
482
+ const $ = this.$;
483
+ const node = $.node;
484
+ if (node instanceof HTMLElement) {
485
+ let lastDisplay = node.style.display;
486
+ const htmlNode = node;
487
+ return this.bindAlive(cond, () => {
488
+ lastDisplay = htmlNode.style.display;
489
+ htmlNode.style.display = 'none';
490
+ }, () => {
491
+ htmlNode.style.display = lastDisplay;
492
+ });
493
+ }
494
+ else {
495
+ throw (0, errors_1.userError)('the element must be a html element', 'bind-show');
496
+ }
497
+ }
498
+ /**
499
+ * bind HTML
500
+ * @param value {IValue}
501
+ */
502
+ bindDomApi(name, value) {
503
+ const $ = this.$;
504
+ const node = $.node;
505
+ if (node instanceof HTMLElement) {
506
+ node[name] = value.$;
507
+ this.watch((v) => {
508
+ node[name] = v;
509
+ }, value);
510
+ }
511
+ else {
512
+ throw (0, errors_1.userError)("HTML can be bound for HTML nodes only", "dom-error");
513
+ }
514
+ }
515
+ applyOptions(options) {
516
+ options["v:attr"] && Object.keys(options["v:attr"]).forEach(name => {
517
+ const value = options["v:attr"][name];
518
+ if (value instanceof ivalue_1.IValue) {
519
+ this.attr(name, value);
520
+ }
521
+ else {
522
+ this.setAttr(name, value);
523
+ }
524
+ });
525
+ if (options.class) {
526
+ const handleClass = (name, value) => {
527
+ if (value instanceof ivalue_1.IValue) {
528
+ this.floatingClass(value, name);
529
+ }
530
+ else if (value && name !== '$') {
531
+ this.addClass(name);
532
+ }
533
+ else {
534
+ this.removeClasse(name);
535
+ }
536
+ };
537
+ if (Array.isArray(options.class)) {
538
+ options.class.forEach(item => {
539
+ if (item instanceof ivalue_1.IValue) {
540
+ this.bindClass(item);
541
+ }
542
+ else if (typeof item == "string") {
543
+ this.addClass(item);
544
+ }
545
+ else {
546
+ Reflect.ownKeys(item).forEach((name) => {
547
+ handleClass(name, item[name]);
548
+ });
549
+ }
550
+ });
551
+ }
552
+ else {
553
+ options.class.$.forEach(item => {
554
+ this.bindClass(item);
555
+ });
556
+ Reflect.ownKeys(options.class).forEach((name) => {
557
+ handleClass(name, options.class[name]);
558
+ });
559
+ }
560
+ }
561
+ options.style && Object.keys(options.style).forEach(name => {
562
+ const value = options.style[name];
563
+ if (value instanceof ivalue_1.IValue) {
564
+ this.style(name, value);
565
+ }
566
+ else if (typeof value === "string") {
567
+ this.setStyle(name, value);
568
+ }
569
+ else {
570
+ if (value[0] instanceof ivalue_1.IValue) {
571
+ this.style(name, this.expr((v) => v + value[1], value[0]));
572
+ }
573
+ else {
574
+ this.setStyle(name, value[0] + value[1]);
575
+ }
576
+ }
577
+ });
578
+ options["v:events"] && Object.keys(options["v:events"]).forEach(name => {
579
+ this.listen(name, options["v:events"][name]);
580
+ });
581
+ if (options["v:bind"]) {
582
+ const inode = this.node;
583
+ Reflect.ownKeys(options["v:bind"]).forEach((k) => {
584
+ const value = options["v:bind"][k];
585
+ if (k === 'value' && (inode instanceof HTMLInputElement || inode instanceof HTMLTextAreaElement)) {
586
+ inode.oninput = () => value.$ = inode.value;
587
+ }
588
+ else if (k === 'checked' && inode instanceof HTMLInputElement) {
589
+ inode.oninput = () => value.$ = inode.checked;
590
+ }
591
+ else if (k === 'volume' && inode instanceof HTMLMediaElement) {
592
+ inode.onvolumechange = () => value.$ = inode.volume;
593
+ }
594
+ this.bindDomApi(k, value);
595
+ });
596
+ }
597
+ options["v:set"] && Object.keys(options["v:set"]).forEach(key => {
598
+ this.node[key] = options["v:set"][key];
599
+ });
600
+ }
601
+ }
602
+ exports.INode = INode;
603
+ /**
604
+ * Represents an Vasille.js HTML element node
605
+ * @class Tag
606
+ * @extends INode
607
+ */
608
+ class Tag extends INode {
609
+ constructor(input) {
610
+ super(input);
611
+ this.$seal();
612
+ }
613
+ preinit(app, parent, tagName) {
614
+ if (!tagName || typeof tagName !== "string") {
615
+ throw (0, errors_1.internalError)('wrong Tag::$preinit call');
616
+ }
617
+ const node = document.createElement(tagName);
618
+ const $ = this.$;
619
+ $.preinit(app, parent);
620
+ $.node = node;
621
+ $.parent.appendNode(node);
622
+ }
623
+ compose(input) {
624
+ input.slot && input.slot(this);
625
+ }
626
+ findFirstChild() {
627
+ return this.$.unmounted ? null : this.$.node;
628
+ }
629
+ insertAdjacent(node) {
630
+ if (this.$.unmounted) {
631
+ if (this.$.next) {
632
+ this.$.next.insertAdjacent(node);
633
+ }
634
+ else {
635
+ this.$.parent.appendNode(node);
636
+ }
637
+ }
638
+ else {
639
+ super.insertAdjacent(node);
640
+ }
641
+ }
642
+ appendNode(node) {
643
+ this.$.node.appendChild(node);
644
+ }
645
+ /**
646
+ * Mount/Unmount a node
647
+ * @param cond {IValue} show condition
648
+ */
649
+ bindMount(cond) {
650
+ const $ = this.$;
651
+ this.bindAlive(cond, () => {
652
+ $.node.remove();
653
+ $.unmounted = true;
654
+ }, () => {
655
+ if ($.unmounted) {
656
+ this.insertAdjacent($.node);
657
+ $.unmounted = false;
658
+ }
659
+ });
660
+ }
661
+ /**
662
+ * Runs GC
663
+ */
664
+ $destroy() {
665
+ this.node.remove();
666
+ super.$destroy();
667
+ }
668
+ }
669
+ exports.Tag = Tag;
670
+ /**
671
+ * Represents a vasille extension node
672
+ * @class Extension
673
+ * @extends INode
674
+ */
675
+ class Extension extends INode {
676
+ preinit(app, parent) {
677
+ const $ = this.$;
678
+ let it = parent;
679
+ while (it && !(it instanceof INode)) {
680
+ it = it.parent;
681
+ }
682
+ if (it && it instanceof INode) {
683
+ $.node = it.node;
684
+ }
685
+ $.preinit(app, parent);
686
+ if (!it) {
687
+ throw (0, errors_1.userError)("A extension node can be encapsulated only in a tag/extension/component", "virtual-dom");
688
+ }
689
+ }
690
+ extend(options) {
691
+ this.applyOptions(options);
692
+ }
693
+ $destroy() {
694
+ super.$destroy();
695
+ }
696
+ }
697
+ exports.Extension = Extension;
698
+ /**
699
+ * Node which cas has just a child
700
+ * @class Component
701
+ * @extends Extension
702
+ */
703
+ class Component extends Extension {
704
+ init() {
705
+ super.composeNow();
706
+ this.ready();
707
+ super.applyOptionsNow();
708
+ }
709
+ ready() {
710
+ super.ready();
711
+ if (this.children.size !== 1) {
712
+ throw (0, errors_1.userError)("Component must have a child only", "dom-error");
713
+ }
714
+ const child = this.lastChild;
715
+ if (child instanceof Tag || child instanceof Component) {
716
+ const $ = this.$;
717
+ $.node = child.node;
718
+ }
719
+ else {
720
+ throw (0, errors_1.userError)("Component child must be Tag or Component", "dom-error");
721
+ }
722
+ }
723
+ preinit(app, parent) {
724
+ this.$.preinit(app, parent);
725
+ }
726
+ }
727
+ exports.Component = Component;
728
+ /**
729
+ * Private part of switch node
730
+ * @class SwitchedNodePrivate
731
+ * @extends INodePrivate
732
+ */
733
+ class SwitchedNodePrivate extends FragmentPrivate {
734
+ constructor() {
735
+ super();
736
+ /**
737
+ * Array of possible cases
738
+ * @type {Array<{cond : IValue<boolean>, cb : function(Fragment)}>}
739
+ */
740
+ this.cases = [];
741
+ this.$seal();
742
+ }
743
+ /**
744
+ * Runs GC
745
+ */
746
+ $destroy() {
747
+ this.cases.forEach(c => {
748
+ delete c.cond;
749
+ delete c.cb;
750
+ });
751
+ this.cases.splice(0);
752
+ super.$destroy();
753
+ }
754
+ }
755
+ exports.SwitchedNodePrivate = SwitchedNodePrivate;
756
+ /**
757
+ * Defines a node witch can switch its children conditionally
758
+ */
759
+ class SwitchedNode extends Fragment {
760
+ /**
761
+ * Constructs a switch node and define a sync function
762
+ */
763
+ constructor() {
764
+ super({}, new SwitchedNodePrivate);
765
+ this.$.sync = () => {
766
+ const $ = this.$;
767
+ let i = 0;
768
+ for (; i < $.cases.length; i++) {
769
+ if ($.cases[i].cond.$) {
770
+ break;
771
+ }
772
+ }
773
+ if (i === $.index) {
774
+ return;
775
+ }
776
+ if (this.lastChild) {
777
+ this.lastChild.$destroy();
778
+ this.children.clear();
779
+ this.lastChild = null;
780
+ }
781
+ if (i !== $.cases.length) {
782
+ $.index = i;
783
+ this.createChild($.cases[i].cb);
784
+ }
785
+ else {
786
+ $.index = -1;
787
+ }
788
+ };
789
+ this.$seal();
790
+ }
791
+ addCase(case_) {
792
+ this.$.cases.push(case_);
793
+ case_.cond.$on(this.$.sync);
794
+ this.$.sync();
795
+ }
796
+ /**
797
+ * Creates a child node
798
+ * @param cb {function(Fragment)} Call-back
799
+ */
800
+ createChild(cb) {
801
+ const node = new Fragment({});
802
+ node.preinit(this.$.app, this);
803
+ node.init();
804
+ this.lastChild = node;
805
+ this.children.add(node);
806
+ cb(node);
807
+ }
808
+ ready() {
809
+ const $ = this.$;
810
+ $.cases.forEach(c => {
811
+ c.cond.$on($.sync);
812
+ });
813
+ $.sync();
814
+ }
815
+ $destroy() {
816
+ const $ = this.$;
817
+ $.cases.forEach(c => {
818
+ c.cond.$off($.sync);
819
+ });
820
+ super.$destroy();
821
+ }
822
+ }
823
+ exports.SwitchedNode = SwitchedNode;
824
+ /**
825
+ * The private part of a text node
826
+ */
827
+ class DebugPrivate extends FragmentPrivate {
828
+ constructor() {
829
+ super();
830
+ this.$seal();
831
+ }
832
+ /**
833
+ * Pre-initializes a text node
834
+ * @param app {App} the app node
835
+ * @param parent {Fragment} parent node
836
+ * @param text {String | IValue}
837
+ */
838
+ preinitComment(app, parent, text) {
839
+ super.preinit(app, parent);
840
+ this.node = document.createComment(text.$);
841
+ this.bindings.add(new expression_1.Expression((v) => {
842
+ this.node.replaceData(0, -1, v);
843
+ }, true, text));
844
+ this.parent.appendNode(this.node);
845
+ }
846
+ /**
847
+ * Clear node data
848
+ */
849
+ $destroy() {
850
+ this.node.remove();
851
+ super.$destroy();
852
+ }
853
+ }
854
+ exports.DebugPrivate = DebugPrivate;
855
+ /**
856
+ * Represents a debug node
857
+ * @class DebugNode
858
+ * @extends Fragment
859
+ */
860
+ class DebugNode extends Fragment {
861
+ constructor() {
862
+ super({});
863
+ /**
864
+ * private data
865
+ * @type {DebugNode}
866
+ */
867
+ this.$ = new DebugPrivate();
868
+ this.$seal();
869
+ }
870
+ preinit(app, parent, text) {
871
+ const $ = this.$;
872
+ if (!text) {
873
+ throw (0, errors_1.internalError)('wrong DebugNode::$preninit call');
874
+ }
875
+ $.preinitComment(app, parent, text);
876
+ }
877
+ /**
878
+ * Runs garbage collector
879
+ */
880
+ $destroy() {
881
+ this.$.$destroy();
882
+ super.$destroy();
883
+ }
884
+ }
885
+ exports.DebugNode = DebugNode;