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.
- package/LICENSE.md +21 -0
- package/flow-typed/vasille.js +836 -0
- package/img/favicon.svg +17 -204
- package/lib/binding/attribute.js +32 -0
- package/lib/binding/binding.js +39 -0
- package/lib/binding/class.js +51 -0
- package/lib/binding/style.js +29 -0
- package/lib/core/core.js +178 -0
- package/lib/core/destroyable.js +45 -0
- package/lib/core/errors.js +16 -0
- package/lib/core/executor.js +154 -0
- package/lib/core/ivalue.js +56 -0
- package/lib/core/signal.js +50 -0
- package/lib/core/slot.js +47 -0
- package/lib/index.js +27 -22
- package/lib/models/array-model.js +208 -0
- package/lib/models/listener.js +130 -0
- package/lib/models/map-model.js +66 -0
- package/lib/models/model.js +1 -0
- package/lib/models/object-model.js +78 -0
- package/lib/models/set-model.js +62 -0
- package/lib/node/app.js +38 -0
- package/lib/node/interceptor.js +83 -0
- package/lib/node/node.js +1185 -0
- package/lib/node/watch.js +27 -0
- package/lib/value/expression.js +83 -0
- package/lib/value/mirror.js +58 -0
- package/lib/value/pointer.js +26 -0
- package/lib/value/reference.js +55 -0
- package/lib/views/array-view.js +24 -0
- package/lib/views/base-view.js +49 -0
- package/lib/views/map-view.js +20 -0
- package/lib/views/object-view.js +20 -0
- package/lib/views/repeat-node.js +106 -0
- package/lib/views/repeater.js +63 -0
- package/lib/views/set-view.js +23 -0
- package/package.json +16 -15
- package/types/binding/attribute.d.ts +23 -0
- package/types/binding/binding.d.ts +30 -0
- package/types/binding/class.d.ts +23 -0
- package/types/binding/style.d.ts +23 -0
- package/types/core/core.d.ts +140 -0
- package/types/core/destroyable.d.ts +15 -0
- package/types/core/errors.d.ts +4 -0
- package/types/core/executor.d.ts +87 -0
- package/types/core/ivalue.d.ts +45 -0
- package/types/core/signal.d.ts +35 -0
- package/types/core/slot.d.ts +45 -0
- package/types/index.d.ts +27 -21
- package/types/models/array-model.d.ts +103 -0
- package/types/models/listener.d.ts +74 -0
- package/types/models/map-model.d.ts +35 -0
- package/types/models/model.d.ts +19 -0
- package/types/models/object-model.d.ts +36 -0
- package/types/models/set-model.d.ts +34 -0
- package/types/node/app.d.ts +37 -0
- package/types/node/interceptor.d.ts +50 -0
- package/types/node/node.d.ts +739 -0
- package/types/node/watch.d.ts +23 -0
- package/types/value/expression.d.ts +60 -0
- package/types/value/mirror.d.ts +35 -0
- package/types/value/pointer.d.ts +19 -0
- package/types/value/reference.d.ts +30 -0
- package/types/views/array-view.d.ts +13 -0
- package/types/views/base-view.d.ts +44 -0
- package/types/views/map-view.d.ts +11 -0
- package/types/views/object-view.d.ts +11 -0
- package/types/views/repeat-node.d.ts +35 -0
- package/types/views/repeater.d.ts +38 -0
- package/types/views/set-view.d.ts +11 -0
- package/CHANGELOG.md +0 -23
- package/lib/attribute.js +0 -71
- package/lib/attribute.js.map +0 -1
- package/lib/bind.js +0 -286
- package/lib/bind.js.map +0 -1
- package/lib/class.js +0 -97
- package/lib/class.js.map +0 -1
- package/lib/executor.js +0 -167
- package/lib/executor.js.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/interfaces/core.js +0 -247
- package/lib/interfaces/core.js.map +0 -1
- package/lib/interfaces/destroyable.js +0 -39
- package/lib/interfaces/destroyable.js.map +0 -1
- package/lib/interfaces/errors.js +0 -49
- package/lib/interfaces/errors.js.map +0 -1
- package/lib/interfaces/ibind.js +0 -31
- package/lib/interfaces/ibind.js.map +0 -1
- package/lib/interfaces/idefinition.js +0 -64
- package/lib/interfaces/idefinition.js.map +0 -1
- package/lib/interfaces/ivalue.js +0 -60
- package/lib/interfaces/ivalue.js.map +0 -1
- package/lib/models.js +0 -579
- package/lib/models.js.map +0 -1
- package/lib/node.js +0 -2155
- package/lib/node.js.map +0 -1
- package/lib/property.js +0 -38
- package/lib/property.js.map +0 -1
- package/lib/style.js +0 -66
- package/lib/style.js.map +0 -1
- package/lib/value.js +0 -203
- package/lib/value.js.map +0 -1
- package/lib/views.js +0 -688
- package/lib/views.js.map +0 -1
- package/types/attribute.d.ts +0 -18
- package/types/bind.d.ts +0 -72
- package/types/class.d.ts +0 -19
- package/types/data.d.ts +0 -11
- package/types/event.d.ts +0 -10
- package/types/executor.d.ts +0 -57
- package/types/interfaces/core.d.ts +0 -129
- package/types/interfaces/destroyable.d.ts +0 -11
- package/types/interfaces/errors.d.ts +0 -24
- package/types/interfaces/ibind.d.ts +0 -19
- package/types/interfaces/idefinition.d.ts +0 -29
- package/types/interfaces/ivalue.d.ts +0 -40
- package/types/models.d.ts +0 -179
- package/types/node.d.ts +0 -906
- package/types/property.d.ts +0 -9
- package/types/style.d.ts +0 -28
- package/types/value.d.ts +0 -43
- package/types/views.d.ts +0 -135
package/lib/node/node.js
ADDED
|
@@ -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
|
+
}
|