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