vasille 1.2.7 → 2.0.1

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