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