vasille 2.0.3 → 2.2.0

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 (79) hide show
  1. package/README.md +7 -3
  2. package/cdn/es2015.js +939 -1009
  3. package/cdn/es5.js +1048 -1029
  4. package/flow-typed/vasille.js +2641 -832
  5. package/lib/binding/attribute.js +11 -12
  6. package/lib/binding/binding.js +9 -19
  7. package/lib/binding/class.js +34 -42
  8. package/lib/binding/style.js +5 -11
  9. package/lib/core/core.js +78 -32
  10. package/lib/core/destroyable.js +2 -2
  11. package/lib/core/ivalue.js +15 -13
  12. package/lib/functional/components.js +17 -0
  13. package/lib/functional/merge.js +41 -0
  14. package/lib/functional/models.js +26 -0
  15. package/lib/functional/options.js +1 -0
  16. package/lib/functional/reactivity.js +33 -0
  17. package/lib/functional/stack.js +127 -0
  18. package/lib/index.js +2 -7
  19. package/lib/models/array-model.js +9 -0
  20. package/lib/models/object-model.js +28 -14
  21. package/lib/node/app.js +23 -14
  22. package/lib/node/interceptor.js +3 -3
  23. package/lib/node/node.js +338 -684
  24. package/lib/node/watch.js +9 -17
  25. package/lib/spec/html.js +1 -0
  26. package/lib/spec/react.js +1 -0
  27. package/lib/spec/svg.js +1 -0
  28. package/lib/v/index.js +23 -0
  29. package/lib/value/expression.js +11 -8
  30. package/lib/value/mirror.js +6 -8
  31. package/lib/value/reference.js +3 -7
  32. package/lib/views/array-view.js +6 -10
  33. package/lib/views/base-view.js +12 -23
  34. package/lib/views/map-view.js +4 -9
  35. package/lib/views/object-view.js +5 -8
  36. package/lib/views/repeat-node.js +20 -60
  37. package/lib/views/repeater.js +7 -7
  38. package/lib/views/set-view.js +4 -11
  39. package/package.json +7 -6
  40. package/types/binding/attribute.d.ts +2 -8
  41. package/types/binding/binding.d.ts +4 -13
  42. package/types/binding/class.d.ts +7 -19
  43. package/types/binding/style.d.ts +0 -6
  44. package/types/core/core.d.ts +40 -54
  45. package/types/core/destroyable.d.ts +2 -2
  46. package/types/core/ivalue.d.ts +13 -11
  47. package/types/functional/components.d.ts +4 -0
  48. package/types/functional/merge.d.ts +1 -0
  49. package/types/functional/models.d.ts +10 -0
  50. package/types/functional/options.d.ts +23 -0
  51. package/types/functional/reactivity.d.ts +11 -0
  52. package/types/functional/stack.d.ts +24 -0
  53. package/types/index.d.ts +3 -7
  54. package/types/models/array-model.d.ts +3 -2
  55. package/types/models/map-model.d.ts +2 -2
  56. package/types/models/model.d.ts +3 -1
  57. package/types/models/object-model.d.ts +4 -2
  58. package/types/models/set-model.d.ts +2 -2
  59. package/types/node/app.d.ts +21 -19
  60. package/types/node/node.d.ts +97 -422
  61. package/types/node/watch.d.ts +9 -15
  62. package/types/spec/html.d.ts +975 -0
  63. package/types/spec/react.d.ts +4 -0
  64. package/types/spec/svg.d.ts +314 -0
  65. package/types/v/index.d.ts +32 -0
  66. package/types/value/expression.d.ts +7 -20
  67. package/types/value/mirror.d.ts +3 -3
  68. package/types/value/reference.d.ts +5 -5
  69. package/types/views/array-view.d.ts +3 -4
  70. package/types/views/base-view.d.ts +9 -17
  71. package/types/views/map-view.d.ts +2 -3
  72. package/types/views/object-view.d.ts +2 -3
  73. package/types/views/repeat-node.d.ts +8 -9
  74. package/types/views/set-view.d.ts +2 -3
  75. package/types/core/executor.d.ts +0 -87
  76. package/types/core/signal.d.ts +0 -35
  77. package/types/core/slot.d.ts +0 -45
  78. package/types/node/interceptor.d.ts +0 -50
  79. package/types/views/repeater.d.ts +0 -38
package/lib/node/node.js CHANGED
@@ -3,7 +3,7 @@ import { IValue } from "../core/ivalue";
3
3
  import { Reference } from "../value/reference";
4
4
  import { Expression } from "../value/expression";
5
5
  import { AttributeBinding } from "../binding/attribute";
6
- import { ClassBinding } from "../binding/class";
6
+ import { StaticClassBinding, DynamicalClassBinding } from "../binding/class";
7
7
  import { StyleBinding } from "../binding/style";
8
8
  import { internalError, userError } from "../core/errors";
9
9
  /**
@@ -14,7 +14,7 @@ import { internalError, userError } from "../core/errors";
14
14
  export class FragmentPrivate extends ReactivePrivate {
15
15
  constructor() {
16
16
  super();
17
- this.$seal();
17
+ this.seal();
18
18
  }
19
19
  /**
20
20
  * Pre-initializes the base of a fragment
@@ -28,10 +28,10 @@ export class FragmentPrivate extends ReactivePrivate {
28
28
  /**
29
29
  * Unlinks all bindings
30
30
  */
31
- $destroy() {
31
+ destroy() {
32
32
  this.next = null;
33
33
  this.prev = null;
34
- super.$destroy();
34
+ super.destroy();
35
35
  }
36
36
  }
37
37
  /**
@@ -41,16 +41,17 @@ export class FragmentPrivate extends ReactivePrivate {
41
41
  export class Fragment extends Reactive {
42
42
  /**
43
43
  * Constructs a Vasille Node
44
+ * @param input
44
45
  * @param $ {FragmentPrivate}
45
46
  */
46
- constructor($) {
47
- super();
47
+ constructor(input, $) {
48
+ super(input, $ || new FragmentPrivate);
48
49
  /**
49
50
  * The children list
50
51
  * @type Array
51
52
  */
52
- this.$children = [];
53
- this.$ = $ || new FragmentPrivate;
53
+ this.children = new Set;
54
+ this.lastChild = null;
54
55
  }
55
56
  /**
56
57
  * Gets the app of node
@@ -64,43 +65,16 @@ export class Fragment extends Reactive {
64
65
  * @param parent {Fragment} parent of node
65
66
  * @param data {*} additional data
66
67
  */
67
- $preinit(app, parent, data) {
68
+ preinit(app, parent, data) {
68
69
  const $ = this.$;
69
70
  $.preinit(app, parent);
70
71
  }
71
- /**
72
- * Initialize node
73
- */
74
- $init() {
75
- this.$createSignals();
76
- this.$createWatchers();
77
- this.$created();
78
- this.$compose();
79
- this.$mounted();
80
- return this;
81
- }
82
- /** To be overloaded: created event handler */
83
- $created() {
84
- // empty
85
- }
86
- /** To be overloaded: mounted event handler */
87
- $mounted() {
88
- // empty
72
+ compose(input) {
73
+ super.compose(input);
74
+ input.slot && input.slot(this);
89
75
  }
90
76
  /** To be overloaded: ready event handler */
91
- $ready() {
92
- // empty
93
- }
94
- /** To be overloaded: signals creation milestone */
95
- $createSignals() {
96
- // empty
97
- }
98
- /** To be overloaded: watchers creation milestone */
99
- $createWatchers() {
100
- // empty
101
- }
102
- /** To be overloaded: DOM creation milestone */
103
- $compose() {
77
+ ready() {
104
78
  // empty
105
79
  }
106
80
  /**
@@ -108,27 +82,23 @@ export class Fragment extends Reactive {
108
82
  * @param node {Fragment} A node to push
109
83
  * @protected
110
84
  */
111
- $$pushNode(node) {
112
- let lastChild = null;
113
- if (this.$children.length) {
114
- lastChild = this.$children[this.$children.length - 1];
115
- }
116
- if (lastChild) {
117
- lastChild.$.next = node;
85
+ pushNode(node) {
86
+ if (this.lastChild) {
87
+ this.lastChild.$.next = node;
118
88
  }
119
- node.$.prev = lastChild;
120
- node.$.parent = this;
121
- this.$children.push(node);
89
+ node.$.prev = this.lastChild;
90
+ this.lastChild = node;
91
+ this.children.add(node);
122
92
  }
123
93
  /**
124
94
  * Find first node in element if so exists
125
95
  * @return {?Element}
126
96
  * @protected
127
97
  */
128
- $$findFirstChild() {
98
+ findFirstChild() {
129
99
  let first;
130
- this.$children.forEach(child => {
131
- first = first || child.$$findFirstChild();
100
+ this.children.forEach(child => {
101
+ first = first || child.findFirstChild();
132
102
  });
133
103
  return first;
134
104
  }
@@ -136,30 +106,30 @@ export class Fragment extends Reactive {
136
106
  * Append a node to end of element
137
107
  * @param node {Node} node to insert
138
108
  */
139
- $$appendNode(node) {
109
+ appendNode(node) {
140
110
  const $ = this.$;
141
111
  if ($.next) {
142
- $.next.$$insertAdjacent(node);
112
+ $.next.insertAdjacent(node);
143
113
  }
144
114
  else {
145
- $.parent.$$appendNode(node);
115
+ $.parent.appendNode(node);
146
116
  }
147
117
  }
148
118
  /**
149
119
  * Insert a node as a sibling of this
150
120
  * @param node {Node} node to insert
151
121
  */
152
- $$insertAdjacent(node) {
153
- const child = this.$$findFirstChild();
122
+ insertAdjacent(node) {
123
+ const child = this.findFirstChild();
154
124
  const $ = this.$;
155
125
  if (child) {
156
- $.app.$run.insertBefore(child, node);
126
+ child.parentElement.insertBefore(node, child);
157
127
  }
158
128
  else if ($.next) {
159
- $.next.$$insertAdjacent(node);
129
+ $.next.insertAdjacent(node);
160
130
  }
161
131
  else {
162
- $.parent.$$appendNode(node);
132
+ $.parent.appendNode(node);
163
133
  }
164
134
  }
165
135
  /**
@@ -167,60 +137,49 @@ export class Fragment extends Reactive {
167
137
  * @param text {String | IValue} A text fragment string
168
138
  * @param cb {function (TextNode)} Callback if previous is slot name
169
139
  */
170
- $text(text, cb) {
140
+ text(text, cb) {
171
141
  const $ = this.$;
172
142
  const node = new TextNode();
173
- const textValue = text instanceof IValue ? text : this.$ref(text);
174
- node.$preinit($.app, this, textValue);
175
- this.$$pushNode(node);
176
- if (cb) {
177
- $.app.$run.callCallback(() => {
178
- cb(node);
179
- });
180
- }
181
- return this;
143
+ node.preinit($.app, this, text);
144
+ this.pushNode(node);
145
+ cb && cb(node);
182
146
  }
183
- $debug(text) {
184
- if (this.$.app.$debugUi) {
147
+ debug(text) {
148
+ if (this.$.app.debugUi) {
185
149
  const node = new DebugNode();
186
- node.$preinit(this.$.app, this, text);
187
- this.$$pushNode(node);
150
+ node.preinit(this.$.app, this, text);
151
+ this.pushNode(node);
188
152
  }
189
- return this;
190
153
  }
191
- $tag(tagName, cb) {
154
+ /**
155
+ * Defines a tag element
156
+ * @param tagName {String} the tag name
157
+ * @param input
158
+ * @param cb {function(Tag, *)} callback
159
+ */
160
+ tag(tagName, input, cb) {
192
161
  const $ = this.$;
193
- const node = new Tag();
194
- node.$preinit($.app, this, tagName);
195
- node.$init();
196
- this.$$pushNode(node);
197
- $.app.$run.callCallback(() => {
198
- if (cb) {
199
- cb(node, node.node);
200
- }
201
- node.$ready();
202
- });
203
- return this;
162
+ const node = new Tag(input);
163
+ input.slot = cb || input.slot;
164
+ node.preinit($.app, this, tagName);
165
+ node.init();
166
+ this.pushNode(node);
167
+ node.ready();
168
+ return node.node;
204
169
  }
205
170
  /**
206
171
  * Defines a custom element
207
172
  * @param node {Fragment} vasille element to insert
208
173
  * @param callback {function($ : *)}
209
- * @param callback1 {function($ : *)}
210
174
  */
211
- $create(node, callback, callback1) {
175
+ create(node, callback) {
212
176
  const $ = this.$;
213
177
  node.$.parent = this;
214
- node.$preinit($.app, this);
215
- if (callback) {
216
- callback(node);
217
- }
218
- if (callback1) {
219
- callback1(node);
220
- }
221
- this.$$pushNode(node);
222
- node.$init().$ready();
223
- return this;
178
+ node.preinit($.app, this);
179
+ node.input.slot = callback || node.input.slot;
180
+ this.pushNode(node);
181
+ node.init();
182
+ node.ready();
224
183
  }
225
184
  /**
226
185
  * Defines an if node
@@ -228,32 +187,29 @@ export class Fragment extends Reactive {
228
187
  * @param cb {function(Fragment)} callback to run on true
229
188
  * @return {this}
230
189
  */
231
- $if(cond, cb) {
232
- return this.$switch({ cond, cb });
190
+ if(cond, cb) {
191
+ const node = new SwitchedNode();
192
+ node.preinit(this.$.app, this);
193
+ node.init();
194
+ this.pushNode(node);
195
+ node.addCase(this.case(cond, cb));
196
+ node.ready();
233
197
  }
234
- /**
235
- * Defines a if-else node
236
- * @param ifCond {IValue} `if` condition
237
- * @param ifCb {function(Fragment)} Call-back to create `if` child nodes
238
- * @param elseCb {function(Fragment)} Call-back to create `else` child nodes
239
- */
240
- $if_else(ifCond, ifCb, elseCb) {
241
- return this.$switch({ cond: ifCond, cb: ifCb }, { cond: trueIValue, cb: elseCb });
198
+ else(cb) {
199
+ if (this.lastChild instanceof SwitchedNode) {
200
+ this.lastChild.addCase(this.default(cb));
201
+ }
202
+ else {
203
+ throw userError('wrong `else` function use', 'logic-error');
204
+ }
242
205
  }
243
- /**
244
- * Defines a switch nodes: Will break after first true condition
245
- * @param cases {...{ cond : IValue, cb : function(Fragment) }} cases
246
- * @return {INode}
247
- */
248
- $switch(...cases) {
249
- const $ = this.$;
250
- const node = new SwitchedNode();
251
- node.$preinit($.app, this);
252
- node.$init();
253
- this.$$pushNode(node);
254
- node.setCases(cases);
255
- node.$ready();
256
- return this;
206
+ elif(cond, cb) {
207
+ if (this.lastChild instanceof SwitchedNode) {
208
+ this.lastChild.addCase(this.case(cond, cb));
209
+ }
210
+ else {
211
+ throw userError('wrong `elif` function use', 'logic-error');
212
+ }
257
213
  }
258
214
  /**
259
215
  * Create a case for switch
@@ -261,22 +217,48 @@ export class Fragment extends Reactive {
261
217
  * @param cb {function(Fragment) : void}
262
218
  * @return {{cond : IValue, cb : (function(Fragment) : void)}}
263
219
  */
264
- $case(cond, cb) {
220
+ case(cond, cb) {
265
221
  return { cond, cb };
266
222
  }
267
223
  /**
268
224
  * @param cb {(function(Fragment) : void)}
269
225
  * @return {{cond : IValue, cb : (function(Fragment) : void)}}
270
226
  */
271
- $default(cb) {
227
+ default(cb) {
272
228
  return { cond: trueIValue, cb };
273
229
  }
274
- $destroy() {
275
- for (const child of this.$children) {
276
- child.$destroy();
230
+ insertBefore(node) {
231
+ const $ = this.$;
232
+ node.$.prev = $.prev;
233
+ node.$.next = this;
234
+ if ($.prev) {
235
+ $.prev.$.next = node;
277
236
  }
278
- this.$children.splice(0);
279
- super.$destroy();
237
+ $.prev = node;
238
+ }
239
+ insertAfter(node) {
240
+ const $ = this.$;
241
+ node.$.prev = this;
242
+ node.$.next = $.next;
243
+ $.next = node;
244
+ }
245
+ remove() {
246
+ const $ = this.$;
247
+ if ($.next) {
248
+ $.next.$.prev = $.prev;
249
+ }
250
+ if ($.prev) {
251
+ $.prev.$.next = $.next;
252
+ }
253
+ }
254
+ destroy() {
255
+ this.children.forEach(child => child.destroy());
256
+ this.children.clear();
257
+ this.lastChild = null;
258
+ if (this.$.parent.lastChild === this) {
259
+ this.$.parent.lastChild = this.$.prev;
260
+ }
261
+ super.destroy();
280
262
  }
281
263
  }
282
264
  const trueIValue = new Reference(true);
@@ -288,26 +270,28 @@ const trueIValue = new Reference(true);
288
270
  export class TextNodePrivate extends FragmentPrivate {
289
271
  constructor() {
290
272
  super();
291
- this.$seal();
273
+ this.seal();
292
274
  }
293
275
  /**
294
276
  * Pre-initializes a text node
295
277
  * @param app {AppNode} the app node
278
+ * @param parent
296
279
  * @param text {IValue}
297
280
  */
298
281
  preinitText(app, parent, text) {
299
282
  super.preinit(app, parent);
300
- this.node = document.createTextNode(text.$);
301
- this.bindings.add(new Expression((v) => {
302
- this.node.replaceData(0, -1, v);
303
- }, true, text));
304
- this.parent.$$appendNode(this.node);
283
+ this.node = document.createTextNode(text instanceof IValue ? text.$ : text);
284
+ if (text instanceof IValue) {
285
+ this.bindings.add(new Expression((v) => {
286
+ this.node.replaceData(0, -1, v);
287
+ }, true, text));
288
+ }
305
289
  }
306
290
  /**
307
291
  * Clear node data
308
292
  */
309
- $destroy() {
310
- super.$destroy();
293
+ destroy() {
294
+ super.destroy();
311
295
  }
312
296
  }
313
297
  /**
@@ -316,25 +300,25 @@ export class TextNodePrivate extends FragmentPrivate {
316
300
  * @extends Fragment
317
301
  */
318
302
  export class TextNode extends Fragment {
319
- constructor() {
320
- super();
321
- this.$ = new TextNodePrivate();
322
- this.$seal();
303
+ constructor($ = new TextNodePrivate()) {
304
+ super({}, $);
305
+ this.seal();
323
306
  }
324
- $preinit(app, parent, text) {
307
+ preinit(app, parent, text) {
325
308
  const $ = this.$;
326
309
  if (!text) {
327
310
  throw internalError('wrong TextNode::$preninit call');
328
311
  }
329
312
  $.preinitText(app, parent, text);
313
+ $.parent.appendNode($.node);
330
314
  }
331
- $$findFirstChild() {
315
+ findFirstChild() {
332
316
  return this.$.node;
333
317
  }
334
- $destroy() {
318
+ destroy() {
335
319
  this.$.node.remove();
336
- this.$.$destroy();
337
- super.$destroy();
320
+ this.$.destroy();
321
+ super.destroy();
338
322
  }
339
323
  }
340
324
  /**
@@ -350,10 +334,10 @@ export class INodePrivate extends FragmentPrivate {
350
334
  * @type {boolean}
351
335
  */
352
336
  this.unmounted = false;
353
- this.$seal();
337
+ this.seal();
354
338
  }
355
- $destroy() {
356
- super.$destroy();
339
+ destroy() {
340
+ super.destroy();
357
341
  }
358
342
  }
359
343
  /**
@@ -364,11 +348,12 @@ export class INodePrivate extends FragmentPrivate {
364
348
  export class INode extends Fragment {
365
349
  /**
366
350
  * Constructs a base node
351
+ * @param input
367
352
  * @param $ {?INodePrivate}
368
353
  */
369
- constructor($) {
370
- super($ || new INodePrivate);
371
- this.$seal();
354
+ constructor(input, $) {
355
+ super(input, $ || new INodePrivate);
356
+ this.seal();
372
357
  }
373
358
  /**
374
359
  * Get the bound node
@@ -376,78 +361,55 @@ export class INode extends Fragment {
376
361
  get node() {
377
362
  return this.$.node;
378
363
  }
379
- /**
380
- * Initialize node
381
- */
382
- $init() {
383
- this.$createSignals();
384
- this.$createWatchers();
385
- this.$createAttrs();
386
- this.$createStyle();
387
- this.$created();
388
- this.$compose();
389
- this.$mounted();
390
- return this;
391
- }
392
- /** To be overloaded: attributes creation milestone */
393
- $createAttrs() {
394
- // empty
395
- }
396
- /** To be overloaded: $style attributes creation milestone */
397
- $createStyle() {
398
- // empty
399
- }
400
364
  /**
401
365
  * Bind attribute value
402
366
  * @param name {String} name of attribute
403
367
  * @param value {IValue} value
404
368
  */
405
- $attr(name, value) {
369
+ attr(name, value) {
406
370
  const $ = this.$;
407
371
  const attr = new AttributeBinding(this, name, value);
408
372
  $.bindings.add(attr);
409
- return this;
410
- }
411
- $bindAttr(name, calculator, v1, v2, v3, v4, v5, v6, v7, v8, v9) {
412
- const $ = this.$;
413
- const expr = this.$bind(calculator, v1, v2, v3, v4, v5, v6, v7, v8, v9);
414
- $.bindings.add(new AttributeBinding(this, name, expr));
415
- return this;
416
373
  }
417
374
  /**
418
375
  * Set attribute value
419
376
  * @param name {string} name of attribute
420
377
  * @param value {string} value
421
378
  */
422
- $setAttr(name, value) {
423
- this.$.app.$run.setAttribute(this.$.node, name, value);
379
+ setAttr(name, value) {
380
+ if (typeof value === 'boolean') {
381
+ if (value) {
382
+ this.$.node.setAttribute(name, "");
383
+ }
384
+ }
385
+ else {
386
+ this.$.node.setAttribute(name, `${value}`);
387
+ }
424
388
  return this;
425
389
  }
426
390
  /**
427
391
  * Adds a CSS class
428
392
  * @param cl {string} Class name
429
393
  */
430
- $addClass(cl) {
431
- this.$.app.$run.addClass(this.$.node, cl);
394
+ addClass(cl) {
395
+ this.$.node.classList.add(cl);
432
396
  return this;
433
397
  }
434
398
  /**
435
399
  * Adds some CSS classes
436
400
  * @param cls {...string} classes names
437
401
  */
438
- $addClasses(...cls) {
439
- cls.forEach(cl => {
440
- this.$.app.$run.addClass(this.$.node, cl);
441
- });
402
+ removeClasse(cl) {
403
+ this.$.node.classList.remove(cl);
442
404
  return this;
443
405
  }
444
406
  /**
445
407
  * Bind a CSS class
446
408
  * @param className {IValue}
447
409
  */
448
- $bindClass(className) {
410
+ bindClass(className) {
449
411
  const $ = this.$;
450
- $.bindings.add(new ClassBinding(this, "", className));
412
+ $.bindings.add(new DynamicalClassBinding(this, className));
451
413
  return this;
452
414
  }
453
415
  /**
@@ -455,8 +417,8 @@ export class INode extends Fragment {
455
417
  * @param cond {IValue} condition
456
418
  * @param className {string} class name
457
419
  */
458
- $floatingClass(cond, className) {
459
- this.$.bindings.add(new ClassBinding(this, className, cond));
420
+ floatingClass(cond, className) {
421
+ this.$.bindings.add(new StaticClassBinding(this, className, cond));
460
422
  return this;
461
423
  }
462
424
  /**
@@ -464,7 +426,7 @@ export class INode extends Fragment {
464
426
  * @param name {String} name of style attribute
465
427
  * @param value {IValue} value
466
428
  */
467
- $style(name, value) {
429
+ style(name, value) {
468
430
  const $ = this.$;
469
431
  if ($.node instanceof HTMLElement) {
470
432
  $.bindings.add(new StyleBinding(this, name, value));
@@ -474,28 +436,17 @@ export class INode extends Fragment {
474
436
  }
475
437
  return this;
476
438
  }
477
- $bindStyle(name, calculator, v1, v2, v3, v4, v5, v6, v7, v8, v9) {
478
- const $ = this.$;
479
- const expr = this.$bind(calculator, v1, v2, v3, v4, v5, v6, v7, v8, v9);
480
- if ($.node instanceof HTMLElement) {
481
- $.bindings.add(new StyleBinding(this, name, expr));
482
- }
483
- else {
484
- throw userError('style can be applied to HTML elements only', 'non-html-element');
485
- }
486
- return this;
487
- }
488
439
  /**
489
440
  * Sets a style property value
490
441
  * @param prop {string} Property name
491
442
  * @param value {string} Property value
492
443
  */
493
- $setStyle(prop, value) {
444
+ setStyle(prop, value) {
494
445
  if (this.$.node instanceof HTMLElement) {
495
- this.$.app.$run.setStyle(this.$.node, prop, value);
446
+ this.$.node.style.setProperty(prop, value);
496
447
  }
497
448
  else {
498
- throw userError("Style can be setted for HTML elements only", "non-html-element");
449
+ throw userError("Style can be set for HTML elements only", "non-html-element");
499
450
  }
500
451
  return this;
501
452
  }
@@ -505,403 +456,24 @@ export class INode extends Fragment {
505
456
  * @param handler {function (Event)} Event handler
506
457
  * @param options {Object | boolean} addEventListener options
507
458
  */
508
- $listen(name, handler, options) {
459
+ listen(name, handler, options) {
509
460
  this.$.node.addEventListener(name, handler, options);
510
461
  return this;
511
462
  }
512
- /**
513
- * @param handler {function (MouseEvent)}
514
- * @param options {Object | boolean}
515
- */
516
- $oncontextmenu(handler, options) {
517
- return this.$listen("contextmenu", handler, options);
518
- }
519
- /**
520
- * @param handler {function (MouseEvent)}
521
- * @param options {Object | boolean}
522
- */
523
- $onmousedown(handler, options) {
524
- return this.$listen("mousedown", handler, options);
525
- }
526
- /**
527
- * @param handler {function (MouseEvent)}
528
- * @param options {Object | boolean}
529
- */
530
- $onmouseenter(handler, options) {
531
- return this.$listen("mouseenter", handler, options);
532
- }
533
- /**
534
- * @param handler {function (MouseEvent)}
535
- * @param options {Object | boolean}
536
- */
537
- $onmouseleave(handler, options) {
538
- return this.$listen("mouseleave", handler, options);
539
- }
540
- /**
541
- * @param handler {function (MouseEvent)}
542
- * @param options {Object | boolean}
543
- */
544
- $onmousemove(handler, options) {
545
- return this.$listen("mousemove", handler, options);
546
- }
547
- /**
548
- * @param handler {function (MouseEvent)}
549
- * @param options {Object | boolean}
550
- */
551
- $onmouseout(handler, options) {
552
- return this.$listen("mouseout", handler, options);
553
- }
554
- /**
555
- * @param handler {function (MouseEvent)}
556
- * @param options {Object | boolean}
557
- */
558
- $onmouseover(handler, options) {
559
- return this.$listen("mouseover", handler, options);
560
- }
561
- /**
562
- * @param handler {function (MouseEvent)}
563
- * @param options {Object | boolean}
564
- */
565
- $onmouseup(handler, options) {
566
- return this.$listen("mouseup", handler, options);
567
- }
568
- /**
569
- * @param handler {function (MouseEvent)}
570
- * @param options {Object | boolean}
571
- */
572
- $onclick(handler, options) {
573
- return this.$listen("click", handler, options);
574
- }
575
- /**
576
- * @param handler {function (MouseEvent)}
577
- * @param options {Object | boolean}
578
- */
579
- $ondblclick(handler, options) {
580
- return this.$listen("dblclick", handler, options);
581
- }
582
- /**
583
- * @param handler {function (FocusEvent)}
584
- * @param options {Object | boolean}
585
- */
586
- $onblur(handler, options) {
587
- return this.$listen("blur", handler, options);
588
- }
589
- /**
590
- * @param handler {function (FocusEvent)}
591
- * @param options {Object | boolean}
592
- */
593
- $onfocus(handler, options) {
594
- return this.$listen("focus", handler, options);
595
- }
596
- /**
597
- * @param handler {function (FocusEvent)}
598
- * @param options {Object | boolean}
599
- */
600
- $onfocusin(handler, options) {
601
- return this.$listen("focusin", handler, options);
602
- }
603
- /**
604
- * @param handler {function (FocusEvent)}
605
- * @param options {Object | boolean}
606
- */
607
- $onfocusout(handler, options) {
608
- return this.$listen("focusout", handler, options);
609
- }
610
- /**
611
- * @param handler {function (KeyboardEvent)}
612
- * @param options {Object | boolean}
613
- */
614
- $onkeydown(handler, options) {
615
- return this.$listen("keydown", handler, options);
616
- }
617
- /**
618
- * @param handler {function (KeyboardEvent)}
619
- * @param options {Object | boolean}
620
- */
621
- $onkeyup(handler, options) {
622
- return this.$listen("keyup", handler, options);
623
- }
624
- /**
625
- * @param handler {function (KeyboardEvent)}
626
- * @param options {Object | boolean}
627
- */
628
- $onkeypress(handler, options) {
629
- return this.$listen("keypress", handler, options);
630
- }
631
- /**
632
- * @param handler {function (TouchEvent)}
633
- * @param options {Object | boolean}
634
- */
635
- $ontouchstart(handler, options) {
636
- return this.$listen("touchstart", handler, options);
637
- }
638
- /**
639
- * @param handler {function (TouchEvent)}
640
- * @param options {Object | boolean}
641
- */
642
- $ontouchmove(handler, options) {
643
- return this.$listen("touchmove", handler, options);
644
- }
645
- /**
646
- * @param handler {function (TouchEvent)}
647
- * @param options {Object | boolean}
648
- */
649
- $ontouchend(handler, options) {
650
- return this.$listen("touchend", handler, options);
651
- }
652
- /**
653
- * @param handler {function (TouchEvent)}
654
- * @param options {Object | boolean}
655
- */
656
- $ontouchcancel(handler, options) {
657
- return this.$listen("touchcancel", handler, options);
658
- }
659
- /**
660
- * @param handler {function (WheelEvent)}
661
- * @param options {Object | boolean}
662
- */
663
- $onwheel(handler, options) {
664
- return this.$listen("wheel", handler, options);
665
- }
666
- /**
667
- * @param handler {function (ProgressEvent)}
668
- * @param options {Object | boolean}
669
- */
670
- $onabort(handler, options) {
671
- return this.$listen("abort", handler, options);
672
- }
673
- /**
674
- * @param handler {function (ProgressEvent)}
675
- * @param options {Object | boolean}
676
- */
677
- $onerror(handler, options) {
678
- return this.$listen("error", handler, options);
679
- }
680
- /**
681
- * @param handler {function (ProgressEvent)}
682
- * @param options {Object | boolean}
683
- */
684
- $onload(handler, options) {
685
- return this.$listen("load", handler, options);
686
- }
687
- /**
688
- * @param handler {function (ProgressEvent)}
689
- * @param options {Object | boolean}
690
- */
691
- $onloadend(handler, options) {
692
- return this.$listen("loadend", handler, options);
693
- }
694
- /**
695
- * @param handler {function (ProgressEvent)}
696
- * @param options {Object | boolean}
697
- */
698
- $onloadstart(handler, options) {
699
- return this.$listen("loadstart", handler, options);
700
- }
701
- /**
702
- * @param handler {function (ProgressEvent)}
703
- * @param options {Object | boolean}
704
- */
705
- $onprogress(handler, options) {
706
- return this.$listen("progress", handler, options);
707
- }
708
- /**
709
- * @param handler {function (ProgressEvent)}
710
- * @param options {Object | boolean}
711
- */
712
- $ontimeout(handler, options) {
713
- return this.$listen("timeout", handler, options);
714
- }
715
- /**
716
- * @param handler {function (DragEvent)}
717
- * @param options {Object | boolean}
718
- */
719
- $ondrag(handler, options) {
720
- return this.$listen("drag", handler, options);
721
- }
722
- /**
723
- * @param handler {function (DragEvent)}
724
- * @param options {Object | boolean}
725
- */
726
- $ondragend(handler, options) {
727
- return this.$listen("dragend", handler, options);
728
- }
729
- /**
730
- * @param handler {function (DragEvent)}
731
- * @param options {Object | boolean}
732
- */
733
- $ondragenter(handler, options) {
734
- return this.$listen("dragenter", handler, options);
735
- }
736
- /**
737
- * @param handler {function (DragEvent)}
738
- * @param options {Object | boolean}
739
- */
740
- $ondragexit(handler, options) {
741
- return this.$listen("dragexit", handler, options);
742
- }
743
- /**
744
- * @param handler {function (DragEvent)}
745
- * @param options {Object | boolean}
746
- */
747
- $ondragleave(handler, options) {
748
- return this.$listen("dragleave", handler, options);
749
- }
750
- /**
751
- * @param handler {function (DragEvent)}
752
- * @param options {Object | boolean}
753
- */
754
- $ondragover(handler, options) {
755
- return this.$listen("dragover", handler, options);
756
- }
757
- /**
758
- * @param handler {function (DragEvent)}
759
- * @param options {Object | boolean}
760
- */
761
- $ondragstart(handler, options) {
762
- return this.$listen("dragstart", handler, options);
763
- }
764
- /**
765
- * @param handler {function (DragEvent)}
766
- * @param options {Object | boolean}
767
- */
768
- $ondrop(handler, options) {
769
- return this.$listen("drop", handler, options);
770
- }
771
- /**
772
- * @param handler {function (PointerEvent)}
773
- * @param options {Object | boolean}
774
- */
775
- $onpointerover(handler, options) {
776
- return this.$listen("pointerover", handler, options);
777
- }
778
- /**
779
- * @param handler {function (PointerEvent)}
780
- * @param options {Object | boolean}
781
- */
782
- $onpointerenter(handler, options) {
783
- return this.$listen("pointerenter", handler, options);
784
- }
785
- /**
786
- * @param handler {function (PointerEvent)}
787
- * @param options {Object | boolean}
788
- */
789
- $onpointerdown(handler, options) {
790
- return this.$listen("pointerdown", handler, options);
791
- }
792
- /**
793
- * @param handler {function (PointerEvent)}
794
- * @param options {Object | boolean}
795
- */
796
- $onpointermove(handler, options) {
797
- return this.$listen("pointermove", handler, options);
798
- }
799
- /**
800
- * @param handler {function (PointerEvent)}
801
- * @param options {Object | boolean}
802
- */
803
- $onpointerup(handler, options) {
804
- return this.$listen("pointerup", handler, options);
805
- }
806
- /**
807
- * @param handler {function (PointerEvent)}
808
- * @param options {Object | boolean}
809
- */
810
- $onpointercancel(handler, options) {
811
- return this.$listen("pointercancel", handler, options);
812
- }
813
- /**
814
- * @param handler {function (PointerEvent)}
815
- * @param options {Object | boolean}
816
- */
817
- $onpointerout(handler, options) {
818
- return this.$listen("pointerout", handler, options);
819
- }
820
- /**
821
- * @param handler {function (PointerEvent)}
822
- * @param options {Object | boolean}
823
- */
824
- $onpointerleave(handler, options) {
825
- return this.$listen("pointerleave", handler, options);
826
- }
827
- /**
828
- * @param handler {function (PointerEvent)}
829
- * @param options {Object | boolean}
830
- */
831
- $ongotpointercapture(handler, options) {
832
- return this.$listen("gotpointercapture", handler, options);
833
- }
834
- /**
835
- * @param handler {function (PointerEvent)}
836
- * @param options {Object | boolean}
837
- */
838
- $onlostpointercapture(handler, options) {
839
- return this.$listen("lostpointercapture", handler, options);
840
- }
841
- /**
842
- * @param handler {function (AnimationEvent)}
843
- * @param options {Object | boolean}
844
- */
845
- $onanimationstart(handler, options) {
846
- return this.$listen("animationstart", handler, options);
847
- }
848
- /**
849
- * @param handler {function (AnimationEvent)}
850
- * @param options {Object | boolean}
851
- */
852
- $onanimationend(handler, options) {
853
- return this.$listen("animationend", handler, options);
854
- }
855
- /**
856
- * @param handler {function (AnimationEvent)}
857
- * @param options {Object | boolean}
858
- */
859
- $onanimationiteraton(handler, options) {
860
- return this.$listen("animationiteration", handler, options);
861
- }
862
- /**
863
- * @param handler {function (ClipboardEvent)}
864
- * @param options {Object | boolean}
865
- */
866
- $onclipboardchange(handler, options) {
867
- return this.$listen("clipboardchange", handler, options);
868
- }
869
- /**
870
- * @param handler {function (ClipboardEvent)}
871
- * @param options {Object | boolean}
872
- */
873
- $oncut(handler, options) {
874
- return this.$listen("cut", handler, options);
875
- }
876
- /**
877
- * @param handler {function (ClipboardEvent)}
878
- * @param options {Object | boolean}
879
- */
880
- $oncopy(handler, options) {
881
- return this.$listen("copy", handler, options);
882
- }
883
- /**
884
- * @param handler {function (ClipboardEvent)}
885
- * @param options {Object | boolean}
886
- */
887
- $onpaste(handler, options) {
888
- return this.$listen("paste", handler, options);
889
- }
890
- $$insertAdjacent(node) {
891
- const $ = this.$;
892
- $.app.$run.insertBefore($.node, node);
463
+ insertAdjacent(node) {
464
+ this.$.node.parentNode.insertBefore(node, this.$.node);
893
465
  }
894
466
  /**
895
467
  * A v-show & ngShow alternative
896
468
  * @param cond {IValue} show condition
897
469
  */
898
- $bindShow(cond) {
470
+ bindShow(cond) {
899
471
  const $ = this.$;
900
472
  const node = $.node;
901
473
  if (node instanceof HTMLElement) {
902
474
  let lastDisplay = node.style.display;
903
475
  const htmlNode = node;
904
- return this.$bindAlive(cond, () => {
476
+ return this.bindAlive(cond, () => {
905
477
  lastDisplay = htmlNode.style.display;
906
478
  htmlNode.style.display = 'none';
907
479
  }, () => {
@@ -916,19 +488,105 @@ export class INode extends Fragment {
916
488
  * bind HTML
917
489
  * @param value {IValue}
918
490
  */
919
- $html(value) {
491
+ bindDomApi(name, value) {
920
492
  const $ = this.$;
921
493
  const node = $.node;
922
494
  if (node instanceof HTMLElement) {
923
- node.innerHTML = value.$;
924
- this.$watch((v) => {
925
- node.innerHTML = v;
495
+ node[name] = value.$;
496
+ this.watch((v) => {
497
+ node[name] = v;
926
498
  }, value);
927
499
  }
928
500
  else {
929
501
  throw userError("HTML can be bound for HTML nodes only", "dom-error");
930
502
  }
931
503
  }
504
+ applyOptions(options) {
505
+ options["v:attr"] && Object.keys(options["v:attr"]).forEach(name => {
506
+ const value = options["v:attr"][name];
507
+ if (value instanceof IValue) {
508
+ this.attr(name, value);
509
+ }
510
+ else {
511
+ this.setAttr(name, value);
512
+ }
513
+ });
514
+ if (options.class) {
515
+ const handleClass = (name, value) => {
516
+ if (value instanceof IValue) {
517
+ this.floatingClass(value, name);
518
+ }
519
+ else if (value && name !== '$') {
520
+ this.addClass(name);
521
+ }
522
+ else {
523
+ this.removeClasse(name);
524
+ }
525
+ };
526
+ if (Array.isArray(options.class)) {
527
+ options.class.forEach(item => {
528
+ if (item instanceof IValue) {
529
+ this.bindClass(item);
530
+ }
531
+ else if (typeof item == "string") {
532
+ this.addClass(item);
533
+ }
534
+ else {
535
+ Reflect.ownKeys(item).forEach((name) => {
536
+ handleClass(name, item[name]);
537
+ });
538
+ }
539
+ });
540
+ }
541
+ else {
542
+ options.class.$.forEach(item => {
543
+ this.bindClass(item);
544
+ });
545
+ Reflect.ownKeys(options.class).forEach((name) => {
546
+ handleClass(name, options.class[name]);
547
+ });
548
+ }
549
+ }
550
+ options.style && Object.keys(options.style).forEach(name => {
551
+ const value = options.style[name];
552
+ if (value instanceof IValue) {
553
+ this.style(name, value);
554
+ }
555
+ else if (typeof value === "string") {
556
+ this.setStyle(name, value);
557
+ }
558
+ else {
559
+ if (value[0] instanceof IValue) {
560
+ this.style(name, this.expr((v) => v + value[1], value[0]));
561
+ }
562
+ else {
563
+ this.setStyle(name, value[0] + value[1]);
564
+ }
565
+ }
566
+ });
567
+ options["v:events"] && Object.keys(options["v:events"]).forEach(name => {
568
+ this.listen(name, options["v:events"][name]);
569
+ });
570
+ if (options["v:bind"]) {
571
+ const inode = this.node;
572
+ Reflect.ownKeys(options["v:bind"]).forEach((k) => {
573
+ const value = options["v:bind"][k];
574
+ if (k === 'value' && (inode instanceof HTMLInputElement || inode instanceof HTMLTextAreaElement)) {
575
+ inode.oninput = () => value.$ = inode.value;
576
+ }
577
+ else if (k === 'checked' && inode instanceof HTMLInputElement) {
578
+ inode.oninput = () => value.$ = inode.checked;
579
+ }
580
+ else if (k === 'volume' && inode instanceof HTMLMediaElement) {
581
+ inode.onvolumechange = () => value.$ = inode.volume;
582
+ }
583
+ this.bindDomApi(k, value);
584
+ });
585
+ }
586
+ options["v:set"] && Object.keys(options["v:set"]).forEach(key => {
587
+ this.node[key] = options["v:set"][key];
588
+ });
589
+ }
932
590
  }
933
591
  /**
934
592
  * Represents an Vasille.js HTML element node
@@ -936,11 +594,11 @@ export class INode extends Fragment {
936
594
  * @extends INode
937
595
  */
938
596
  export class Tag extends INode {
939
- constructor() {
940
- super();
941
- this.$seal();
597
+ constructor(input) {
598
+ super(input);
599
+ this.seal();
942
600
  }
943
- $preinit(app, parent, tagName) {
601
+ preinit(app, parent, tagName) {
944
602
  if (!tagName || typeof tagName !== "string") {
945
603
  throw internalError('wrong Tag::$preinit call');
946
604
  }
@@ -948,55 +606,52 @@ export class Tag extends INode {
948
606
  const $ = this.$;
949
607
  $.preinit(app, parent);
950
608
  $.node = node;
951
- $.parent.$$appendNode(node);
609
+ $.parent.appendNode(node);
610
+ }
611
+ compose(input) {
612
+ input.slot && input.slot(this);
952
613
  }
953
- $$findFirstChild() {
614
+ findFirstChild() {
954
615
  return this.$.unmounted ? null : this.$.node;
955
616
  }
956
- $$insertAdjacent(node) {
617
+ insertAdjacent(node) {
957
618
  if (this.$.unmounted) {
958
619
  if (this.$.next) {
959
- this.$.next.$$insertAdjacent(node);
620
+ this.$.next.insertAdjacent(node);
960
621
  }
961
622
  else {
962
- this.$.parent.$$appendNode(node);
623
+ this.$.parent.appendNode(node);
963
624
  }
964
625
  }
965
626
  else {
966
- super.$$insertAdjacent(node);
627
+ super.insertAdjacent(node);
967
628
  }
968
629
  }
969
- $$appendNode(node) {
970
- const $ = this.$;
971
- $.app.$run.appendChild($.node, node);
630
+ appendNode(node) {
631
+ this.$.node.appendChild(node);
972
632
  }
973
633
  /**
974
634
  * Mount/Unmount a node
975
635
  * @param cond {IValue} show condition
976
636
  */
977
- $bindMount(cond) {
637
+ bindMount(cond) {
978
638
  const $ = this.$;
979
- return this.$bindAlive(cond, () => {
639
+ this.bindAlive(cond, () => {
980
640
  $.node.remove();
981
641
  $.unmounted = true;
982
642
  }, () => {
983
- if (!$.unmounted)
984
- return;
985
- if ($.next) {
986
- $.next.$$insertAdjacent($.node);
987
- }
988
- else {
989
- $.parent.$$appendNode($.node);
643
+ if ($.unmounted) {
644
+ this.insertAdjacent($.node);
645
+ $.unmounted = false;
990
646
  }
991
- $.unmounted = false;
992
647
  });
993
648
  }
994
649
  /**
995
650
  * Runs GC
996
651
  */
997
- $destroy() {
652
+ destroy() {
998
653
  this.node.remove();
999
- super.$destroy();
654
+ super.destroy();
1000
655
  }
1001
656
  }
1002
657
  /**
@@ -1005,22 +660,22 @@ export class Tag extends INode {
1005
660
  * @extends INode
1006
661
  */
1007
662
  export class Extension extends INode {
1008
- $preinit(app, parent) {
1009
- if (parent instanceof INode) {
1010
- const $ = this.$;
1011
- $.preinit(app, parent);
1012
- $.node = parent.node;
663
+ preinit(app, parent) {
664
+ const $ = this.$;
665
+ let it = parent;
666
+ while (it && !(it instanceof INode)) {
667
+ it = it.parent;
1013
668
  }
1014
- else {
1015
- throw internalError("A extension node can be encapsulated only in a tag/extension/component");
669
+ if (it && it instanceof INode) {
670
+ $.node = it.node;
671
+ }
672
+ $.preinit(app, parent);
673
+ if (!it) {
674
+ throw userError("A extension node can be encapsulated only in a tag/extension/component", "virtual-dom");
1016
675
  }
1017
676
  }
1018
- constructor($) {
1019
- super($);
1020
- this.$seal();
1021
- }
1022
- $destroy() {
1023
- super.$destroy();
677
+ destroy() {
678
+ super.destroy();
1024
679
  }
1025
680
  }
1026
681
  /**
@@ -1029,56 +684,60 @@ export class Extension extends INode {
1029
684
  * @extends Extension
1030
685
  */
1031
686
  export class Component extends Extension {
1032
- constructor() {
1033
- super();
1034
- this.$seal();
1035
- }
1036
- $mounted() {
1037
- super.$mounted();
1038
- if (this.$children.length !== 1) {
1039
- throw userError("UserNode must have a child only", "dom-error");
687
+ ready() {
688
+ super.ready();
689
+ if (this.children.size !== 1) {
690
+ throw userError("Component must have a child only", "dom-error");
1040
691
  }
1041
- const child = this.$children[0];
692
+ const child = this.lastChild;
1042
693
  if (child instanceof Tag || child instanceof Component) {
1043
694
  const $ = this.$;
1044
695
  $.node = child.node;
1045
696
  }
1046
697
  else {
1047
- throw userError("UserNode child must be Tag or Component", "dom-error");
698
+ throw userError("Component child must be Tag or Component", "dom-error");
1048
699
  }
1049
700
  }
701
+ preinit(app, parent) {
702
+ this.$.preinit(app, parent);
703
+ }
1050
704
  }
1051
705
  /**
1052
706
  * Private part of switch node
1053
707
  * @class SwitchedNodePrivate
1054
708
  * @extends INodePrivate
1055
709
  */
1056
- export class SwitchedNodePrivate extends INodePrivate {
710
+ export class SwitchedNodePrivate extends FragmentPrivate {
1057
711
  constructor() {
1058
712
  super();
1059
- this.$seal();
713
+ /**
714
+ * Array of possible cases
715
+ * @type {Array<{cond : IValue<boolean>, cb : function(Fragment)}>}
716
+ */
717
+ this.cases = [];
718
+ this.seal();
1060
719
  }
1061
720
  /**
1062
721
  * Runs GC
1063
722
  */
1064
- $destroy() {
723
+ destroy() {
1065
724
  this.cases.forEach(c => {
1066
725
  delete c.cond;
1067
726
  delete c.cb;
1068
727
  });
1069
728
  this.cases.splice(0);
1070
- super.$destroy();
729
+ super.destroy();
1071
730
  }
1072
731
  }
1073
732
  /**
1074
733
  * Defines a node witch can switch its children conditionally
1075
734
  */
1076
- class SwitchedNode extends Fragment {
735
+ export class SwitchedNode extends Fragment {
1077
736
  /**
1078
737
  * Constructs a switch node and define a sync function
1079
738
  */
1080
739
  constructor() {
1081
- super(new SwitchedNodePrivate);
740
+ super({}, new SwitchedNodePrivate);
1082
741
  this.$.sync = () => {
1083
742
  const $ = this.$;
1084
743
  let i = 0;
@@ -1090,10 +749,10 @@ class SwitchedNode extends Fragment {
1090
749
  if (i === $.index) {
1091
750
  return;
1092
751
  }
1093
- if ($.fragment) {
1094
- $.fragment.$destroy();
1095
- this.$children.splice(0);
1096
- $.fragment = null;
752
+ if (this.lastChild) {
753
+ this.lastChild.destroy();
754
+ this.children.clear();
755
+ this.lastChild = null;
1097
756
  }
1098
757
  if (i !== $.cases.length) {
1099
758
  $.index = i;
@@ -1103,43 +762,38 @@ class SwitchedNode extends Fragment {
1103
762
  $.index = -1;
1104
763
  }
1105
764
  };
1106
- this.$seal();
765
+ this.seal();
1107
766
  }
1108
- /**
1109
- * Set up switch cases
1110
- * @param cases {{ cond : IValue, cb : function(Fragment) }}
1111
- */
1112
- setCases(cases) {
1113
- const $ = this.$;
1114
- $.cases = [...cases];
767
+ addCase(case_) {
768
+ this.$.cases.push(case_);
769
+ case_.cond.on(this.$.sync);
770
+ this.$.sync();
1115
771
  }
1116
772
  /**
1117
773
  * Creates a child node
1118
774
  * @param cb {function(Fragment)} Call-back
1119
775
  */
1120
776
  createChild(cb) {
1121
- const node = new Fragment();
1122
- node.$preinit(this.$.app, this);
1123
- node.$init();
1124
- node.$ready();
1125
- this.$.fragment = node;
1126
- this.$children.push(node);
777
+ const node = new Fragment({});
778
+ node.preinit(this.$.app, this);
779
+ node.init();
780
+ this.lastChild = node;
781
+ this.children.add(node);
1127
782
  cb(node);
1128
783
  }
1129
- $ready() {
784
+ ready() {
1130
785
  const $ = this.$;
1131
- super.$ready();
1132
786
  $.cases.forEach(c => {
1133
787
  c.cond.on($.sync);
1134
788
  });
1135
789
  $.sync();
1136
790
  }
1137
- $destroy() {
791
+ destroy() {
1138
792
  const $ = this.$;
1139
793
  $.cases.forEach(c => {
1140
794
  c.cond.off($.sync);
1141
795
  });
1142
- super.$destroy();
796
+ super.destroy();
1143
797
  }
1144
798
  }
1145
799
  /**
@@ -1148,7 +802,7 @@ class SwitchedNode extends Fragment {
1148
802
  export class DebugPrivate extends FragmentPrivate {
1149
803
  constructor() {
1150
804
  super();
1151
- this.$seal();
805
+ this.seal();
1152
806
  }
1153
807
  /**
1154
808
  * Pre-initializes a text node
@@ -1162,14 +816,14 @@ export class DebugPrivate extends FragmentPrivate {
1162
816
  this.bindings.add(new Expression((v) => {
1163
817
  this.node.replaceData(0, -1, v);
1164
818
  }, true, text));
1165
- this.parent.$$appendNode(this.node);
819
+ this.parent.appendNode(this.node);
1166
820
  }
1167
821
  /**
1168
822
  * Clear node data
1169
823
  */
1170
- $destroy() {
824
+ destroy() {
1171
825
  this.node.remove();
1172
- super.$destroy();
826
+ super.destroy();
1173
827
  }
1174
828
  }
1175
829
  /**
@@ -1179,15 +833,15 @@ export class DebugPrivate extends FragmentPrivate {
1179
833
  */
1180
834
  export class DebugNode extends Fragment {
1181
835
  constructor() {
1182
- super();
836
+ super({});
1183
837
  /**
1184
838
  * private data
1185
839
  * @type {DebugNode}
1186
840
  */
1187
841
  this.$ = new DebugPrivate();
1188
- this.$seal();
842
+ this.seal();
1189
843
  }
1190
- $preinit(app, parent, text) {
844
+ preinit(app, parent, text) {
1191
845
  const $ = this.$;
1192
846
  if (!text) {
1193
847
  throw internalError('wrong DebugNode::$preninit call');
@@ -1197,8 +851,8 @@ export class DebugNode extends Fragment {
1197
851
  /**
1198
852
  * Runs garbage collector
1199
853
  */
1200
- $destroy() {
1201
- this.$.$destroy();
1202
- super.$destroy();
854
+ destroy() {
855
+ this.$.destroy();
856
+ super.destroy();
1203
857
  }
1204
858
  }