vasille 1.2.9 → 2.0.3
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/README.md +195 -129
- package/cdn/es2015.js +3231 -0
- package/cdn/es5.js +3675 -0
- package/flow-typed/vasille.js +839 -0
- 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 +186 -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 +39 -0
- package/lib/node/interceptor.js +83 -0
- package/lib/node/node.js +1204 -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 +21 -0
- package/lib/views/base-view.js +49 -0
- package/lib/views/map-view.js +19 -0
- package/lib/views/object-view.js +19 -0
- package/lib/views/repeat-node.js +106 -0
- package/lib/views/repeater.js +63 -0
- package/lib/views/set-view.js +22 -0
- package/package.json +26 -18
- 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 +144 -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 +42 -0
- package/types/node/interceptor.d.ts +50 -0
- package/types/node/node.d.ts +741 -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 +43 -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/img/favicon.svg +0 -441
- package/img/getLocus.svg +0 -18
- package/img/logo.png +0 -0
- package/img/logo.svg +0 -550
- package/img/scores-o-log.png +0 -0
- package/img/scores-o.png +0 -0
- package/img/scores-wo-log.png +0 -0
- package/img/scores-wo.png +0 -0
- package/img/x1-x32.png +0 -0
- 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/cdn/es2015.js
ADDED
|
@@ -0,0 +1,3231 @@
|
|
|
1
|
+
(function(){
|
|
2
|
+
// ./lib/models/model.js
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
// ./lib/models/listener.js
|
|
7
|
+
/**
|
|
8
|
+
* Represent a listener for a model
|
|
9
|
+
* @class Listener
|
|
10
|
+
*/
|
|
11
|
+
class Listener {
|
|
12
|
+
constructor() {
|
|
13
|
+
Object.defineProperties(this, {
|
|
14
|
+
onAdded: {
|
|
15
|
+
value: new Set,
|
|
16
|
+
writable: false,
|
|
17
|
+
configurable: false
|
|
18
|
+
},
|
|
19
|
+
onRemoved: {
|
|
20
|
+
value: new Set,
|
|
21
|
+
writable: false,
|
|
22
|
+
configurable: false
|
|
23
|
+
},
|
|
24
|
+
frozen: {
|
|
25
|
+
value: false,
|
|
26
|
+
writable: true,
|
|
27
|
+
configurable: false
|
|
28
|
+
},
|
|
29
|
+
queue: {
|
|
30
|
+
value: [],
|
|
31
|
+
writable: false,
|
|
32
|
+
configurable: false
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Exclude the repeated operation in queue
|
|
38
|
+
* @private
|
|
39
|
+
*/
|
|
40
|
+
excludeRepeat(index) {
|
|
41
|
+
this.queue.forEach((item, i) => {
|
|
42
|
+
if (item.index === index) {
|
|
43
|
+
this.queue.splice(i, 1);
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Emits added event to listeners
|
|
51
|
+
* @param index {*} index of value
|
|
52
|
+
* @param value {*} value of added item
|
|
53
|
+
*/
|
|
54
|
+
emitAdded(index, value) {
|
|
55
|
+
if (this.frozen) {
|
|
56
|
+
if (!this.excludeRepeat(index)) {
|
|
57
|
+
this.queue.push({ sign: true, index, value });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
this.onAdded.forEach(handler => {
|
|
62
|
+
handler(index, value);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Emits removed event to listeners
|
|
68
|
+
* @param index {*} index of removed value
|
|
69
|
+
* @param value {*} value of removed item
|
|
70
|
+
*/
|
|
71
|
+
emitRemoved(index, value) {
|
|
72
|
+
if (this.frozen) {
|
|
73
|
+
if (!this.excludeRepeat(index)) {
|
|
74
|
+
this.queue.push({ sign: false, index, value });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
this.onRemoved.forEach(handler => {
|
|
79
|
+
handler(index, value);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Adds a handler to added event
|
|
85
|
+
* @param handler {function} function to run on event emitting
|
|
86
|
+
*/
|
|
87
|
+
onAdd(handler) {
|
|
88
|
+
this.onAdded.add(handler);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Adds a handler to removed event
|
|
92
|
+
* @param handler {function} function to run on event emitting
|
|
93
|
+
*/
|
|
94
|
+
onRemove(handler) {
|
|
95
|
+
this.onRemoved.add(handler);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Removes an handler from added event
|
|
99
|
+
* @param handler {function} handler to remove
|
|
100
|
+
*/
|
|
101
|
+
offAdd(handler) {
|
|
102
|
+
this.onAdded.delete(handler);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Removes an handler form removed event
|
|
106
|
+
* @param handler {function} handler to remove
|
|
107
|
+
*/
|
|
108
|
+
offRemove(handler) {
|
|
109
|
+
this.onRemoved.delete(handler);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Run all queued operation and enable reactivity
|
|
113
|
+
*/
|
|
114
|
+
enableReactivity() {
|
|
115
|
+
this.queue.forEach(item => {
|
|
116
|
+
if (item.sign) {
|
|
117
|
+
this.onAdded.forEach(handler => {
|
|
118
|
+
handler(item.index, item.value);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
this.onRemoved.forEach(handler => {
|
|
123
|
+
handler(item.index, item.value);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
this.queue.splice(0);
|
|
128
|
+
this.frozen = false;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Disable the reactivity and enable the queue
|
|
132
|
+
*/
|
|
133
|
+
disableReactivity() {
|
|
134
|
+
this.frozen = true;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
window.Listener = Listener;
|
|
139
|
+
|
|
140
|
+
// ./lib/models/object-model.js
|
|
141
|
+
/**
|
|
142
|
+
* Object based model
|
|
143
|
+
* @extends Object
|
|
144
|
+
*/
|
|
145
|
+
class ObjectModel extends Object {
|
|
146
|
+
/**
|
|
147
|
+
* Constructs a object model
|
|
148
|
+
* @param obj {Object} input data
|
|
149
|
+
*/
|
|
150
|
+
constructor(obj = {}) {
|
|
151
|
+
super();
|
|
152
|
+
Object.defineProperty(this, 'listener', {
|
|
153
|
+
value: new Listener,
|
|
154
|
+
writable: false,
|
|
155
|
+
configurable: false
|
|
156
|
+
});
|
|
157
|
+
for (const i in obj) {
|
|
158
|
+
Object.defineProperty(this, i, {
|
|
159
|
+
value: obj[i],
|
|
160
|
+
configurable: true,
|
|
161
|
+
writable: true,
|
|
162
|
+
enumerable: true
|
|
163
|
+
});
|
|
164
|
+
this.listener.emitAdded(i, obj[i]);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Gets a value of a field
|
|
169
|
+
* @param key {string}
|
|
170
|
+
* @return {*}
|
|
171
|
+
*/
|
|
172
|
+
get(key) {
|
|
173
|
+
const ts = this;
|
|
174
|
+
return ts[key];
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Sets an object property value
|
|
178
|
+
* @param key {string} property name
|
|
179
|
+
* @param v {*} property value
|
|
180
|
+
* @return {ObjectModel} a pointer to this
|
|
181
|
+
*/
|
|
182
|
+
set(key, v) {
|
|
183
|
+
const ts = this;
|
|
184
|
+
// eslint-disable-next-line no-prototype-builtins
|
|
185
|
+
if (ts.hasOwnProperty(key)) {
|
|
186
|
+
this.listener.emitRemoved(key, ts[key]);
|
|
187
|
+
ts[key] = v;
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
Object.defineProperty(ts, key, {
|
|
191
|
+
value: v,
|
|
192
|
+
configurable: true,
|
|
193
|
+
writable: true,
|
|
194
|
+
enumerable: true
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
this.listener.emitAdded(key, ts[key]);
|
|
198
|
+
return this;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Deletes an object property
|
|
202
|
+
* @param key {string} property name
|
|
203
|
+
*/
|
|
204
|
+
delete(key) {
|
|
205
|
+
const ts = this;
|
|
206
|
+
if (ts[key]) {
|
|
207
|
+
this.listener.emitRemoved(key, ts[key]);
|
|
208
|
+
delete ts[key];
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
enableReactivity() {
|
|
212
|
+
this.listener.enableReactivity();
|
|
213
|
+
}
|
|
214
|
+
disableReactivity() {
|
|
215
|
+
this.listener.disableReactivity();
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
window.ObjectModel = ObjectModel;
|
|
220
|
+
|
|
221
|
+
// ./lib/models/set-model.js
|
|
222
|
+
/**
|
|
223
|
+
* A Set based model
|
|
224
|
+
* @class SetModel
|
|
225
|
+
* @extends Set
|
|
226
|
+
* @implements IModel
|
|
227
|
+
*/
|
|
228
|
+
class SetModel extends Set {
|
|
229
|
+
/**
|
|
230
|
+
* Constructs a set model based on a set
|
|
231
|
+
* @param set {Set} input data
|
|
232
|
+
*/
|
|
233
|
+
constructor(set = []) {
|
|
234
|
+
super();
|
|
235
|
+
Object.defineProperty(this, 'listener', {
|
|
236
|
+
value: new Listener,
|
|
237
|
+
writable: false,
|
|
238
|
+
configurable: false
|
|
239
|
+
});
|
|
240
|
+
set.forEach(item => {
|
|
241
|
+
super.add(item);
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Calls Set.add and notify abut changes
|
|
246
|
+
* @param value {*} value
|
|
247
|
+
* @return {this} a pointer to this
|
|
248
|
+
*/
|
|
249
|
+
add(value) {
|
|
250
|
+
if (!super.has(value)) {
|
|
251
|
+
this.listener.emitAdded(value, value);
|
|
252
|
+
super.add(value);
|
|
253
|
+
}
|
|
254
|
+
return this;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Calls Set.clear and notify abut changes
|
|
258
|
+
*/
|
|
259
|
+
clear() {
|
|
260
|
+
this.forEach(item => {
|
|
261
|
+
this.listener.emitRemoved(item, item);
|
|
262
|
+
});
|
|
263
|
+
super.clear();
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Calls Set.delete and notify abut changes
|
|
267
|
+
* @param value {*}
|
|
268
|
+
* @return {boolean} true if a value was deleted, otherwise false
|
|
269
|
+
*/
|
|
270
|
+
delete(value) {
|
|
271
|
+
if (super.has(value)) {
|
|
272
|
+
this.listener.emitRemoved(value, value);
|
|
273
|
+
}
|
|
274
|
+
return super.delete(value);
|
|
275
|
+
}
|
|
276
|
+
enableReactivity() {
|
|
277
|
+
this.listener.enableReactivity();
|
|
278
|
+
}
|
|
279
|
+
disableReactivity() {
|
|
280
|
+
this.listener.disableReactivity();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
window.SetModel = SetModel;
|
|
285
|
+
|
|
286
|
+
// ./lib/models/map-model.js
|
|
287
|
+
/**
|
|
288
|
+
* A Map based memory
|
|
289
|
+
* @class MapModel
|
|
290
|
+
* @extends Map
|
|
291
|
+
* @implements IModel
|
|
292
|
+
*/
|
|
293
|
+
class MapModel extends Map {
|
|
294
|
+
/**
|
|
295
|
+
* Constructs a map model
|
|
296
|
+
* @param map {[*, *][]} input data
|
|
297
|
+
*/
|
|
298
|
+
constructor(map = []) {
|
|
299
|
+
super();
|
|
300
|
+
Object.defineProperty(this, 'listener', {
|
|
301
|
+
value: new Listener,
|
|
302
|
+
writable: false,
|
|
303
|
+
configurable: false
|
|
304
|
+
});
|
|
305
|
+
map.forEach(([key, value]) => {
|
|
306
|
+
super.set(key, value);
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Calls Map.clear and notify abut changes
|
|
311
|
+
*/
|
|
312
|
+
clear() {
|
|
313
|
+
this.forEach((value, key) => {
|
|
314
|
+
this.listener.emitRemoved(key, value);
|
|
315
|
+
});
|
|
316
|
+
super.clear();
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Calls Map.delete and notify abut changes
|
|
320
|
+
* @param key {*} key
|
|
321
|
+
* @return {boolean} true if removed something, otherwise false
|
|
322
|
+
*/
|
|
323
|
+
delete(key) {
|
|
324
|
+
const tmp = super.get(key);
|
|
325
|
+
if (tmp) {
|
|
326
|
+
this.listener.emitRemoved(key, tmp);
|
|
327
|
+
}
|
|
328
|
+
return super.delete(key);
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Calls Map.set and notify abut changes
|
|
332
|
+
* @param key {*} key
|
|
333
|
+
* @param value {*} value
|
|
334
|
+
* @return {MapModel} a pointer to this
|
|
335
|
+
*/
|
|
336
|
+
set(key, value) {
|
|
337
|
+
const tmp = super.get(key);
|
|
338
|
+
if (tmp) {
|
|
339
|
+
this.listener.emitRemoved(key, tmp);
|
|
340
|
+
}
|
|
341
|
+
super.set(key, value);
|
|
342
|
+
this.listener.emitAdded(key, value);
|
|
343
|
+
return this;
|
|
344
|
+
}
|
|
345
|
+
enableReactivity() {
|
|
346
|
+
this.listener.enableReactivity();
|
|
347
|
+
}
|
|
348
|
+
disableReactivity() {
|
|
349
|
+
this.listener.disableReactivity();
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
window.MapModel = MapModel;
|
|
354
|
+
|
|
355
|
+
// ./lib/models/array-model.js
|
|
356
|
+
/**
|
|
357
|
+
* Model based on Array class
|
|
358
|
+
* @extends Array
|
|
359
|
+
* @implements IModel
|
|
360
|
+
*/
|
|
361
|
+
class ArrayModel extends Array {
|
|
362
|
+
/**
|
|
363
|
+
* @param data {Array} input data
|
|
364
|
+
*/
|
|
365
|
+
constructor(data = []) {
|
|
366
|
+
super();
|
|
367
|
+
Object.defineProperty(this, 'listener', {
|
|
368
|
+
value: new Listener,
|
|
369
|
+
writable: false,
|
|
370
|
+
configurable: false
|
|
371
|
+
});
|
|
372
|
+
for (let i = 0; i < data.length; i++) {
|
|
373
|
+
super.push(data[i]);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
/* Array members */
|
|
377
|
+
/**
|
|
378
|
+
* Gets the last item of array
|
|
379
|
+
* @return {*} the last item of array
|
|
380
|
+
*/
|
|
381
|
+
get last() {
|
|
382
|
+
return this.length ? this[this.length - 1] : null;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Calls Array.fill and notify about changes
|
|
386
|
+
* @param value {*} value to fill with
|
|
387
|
+
* @param start {?number} begin index
|
|
388
|
+
* @param end {?number} end index
|
|
389
|
+
*/
|
|
390
|
+
fill(value, start, end) {
|
|
391
|
+
if (!start) {
|
|
392
|
+
start = 0;
|
|
393
|
+
}
|
|
394
|
+
if (!end) {
|
|
395
|
+
end = this.length;
|
|
396
|
+
}
|
|
397
|
+
for (let i = start; i < end; i++) {
|
|
398
|
+
this.listener.emitRemoved(this[i], this[i]);
|
|
399
|
+
this[i] = value;
|
|
400
|
+
this.listener.emitAdded(value, value);
|
|
401
|
+
}
|
|
402
|
+
return this;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Calls Array.pop and notify about changes
|
|
406
|
+
* @return {*} removed value
|
|
407
|
+
*/
|
|
408
|
+
pop() {
|
|
409
|
+
const v = super.pop();
|
|
410
|
+
if (v !== undefined) {
|
|
411
|
+
this.listener.emitRemoved(v, v);
|
|
412
|
+
}
|
|
413
|
+
return v;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Calls Array.push and notify about changes
|
|
417
|
+
* @param items {...*} values to push
|
|
418
|
+
* @return {number} new length of array
|
|
419
|
+
*/
|
|
420
|
+
push(...items) {
|
|
421
|
+
items.forEach(item => {
|
|
422
|
+
this.listener.emitAdded(item, item);
|
|
423
|
+
super.push(item);
|
|
424
|
+
});
|
|
425
|
+
return this.length;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Calls Array.shift and notify about changed
|
|
429
|
+
* @return {*} the shifted value
|
|
430
|
+
*/
|
|
431
|
+
shift() {
|
|
432
|
+
const v = super.shift();
|
|
433
|
+
if (v !== undefined) {
|
|
434
|
+
this.listener.emitRemoved(v, v);
|
|
435
|
+
}
|
|
436
|
+
return v;
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Calls Array.splice and notify about changed
|
|
440
|
+
* @param start {number} start index
|
|
441
|
+
* @param deleteCount {?number} delete count
|
|
442
|
+
* @param items {...*}
|
|
443
|
+
* @return {ArrayModel} a pointer to this
|
|
444
|
+
*/
|
|
445
|
+
splice(start, deleteCount, ...items) {
|
|
446
|
+
start = Math.min(start, this.length);
|
|
447
|
+
deleteCount = deleteCount || this.length - start;
|
|
448
|
+
const before = this[start + deleteCount];
|
|
449
|
+
for (let i = 0; i < deleteCount; i++) {
|
|
450
|
+
const index = start + deleteCount - i - 1;
|
|
451
|
+
if (this[index] !== undefined) {
|
|
452
|
+
this.listener.emitRemoved(this[index], this[index]);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
for (let i = 0; i < items.length; i++) {
|
|
456
|
+
this.listener.emitAdded(before, items[i]);
|
|
457
|
+
}
|
|
458
|
+
return new ArrayModel(super.splice(start, deleteCount, ...items));
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Calls Array.unshift and notify about changed
|
|
462
|
+
* @param items {...*} values to insert
|
|
463
|
+
* @return {number} the length after prepend
|
|
464
|
+
*/
|
|
465
|
+
unshift(...items) {
|
|
466
|
+
for (let i = 0; i < items.length; i++) {
|
|
467
|
+
this.listener.emitAdded(this[i], items[i]);
|
|
468
|
+
}
|
|
469
|
+
return super.unshift(...items);
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Inserts a value to the end of array
|
|
473
|
+
* @param v {*} value to insert
|
|
474
|
+
*/
|
|
475
|
+
append(v) {
|
|
476
|
+
this.listener.emitAdded(null, v);
|
|
477
|
+
super.push(v);
|
|
478
|
+
return this;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Clears array
|
|
482
|
+
* @return {this} a pointer to this
|
|
483
|
+
*/
|
|
484
|
+
clear() {
|
|
485
|
+
this.forEach(v => {
|
|
486
|
+
this.listener.emitRemoved(v, v);
|
|
487
|
+
});
|
|
488
|
+
super.splice(0);
|
|
489
|
+
return this;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Inserts a value to position `index`
|
|
493
|
+
* @param index {number} index to insert value
|
|
494
|
+
* @param v {*} value to insert
|
|
495
|
+
* @return {this} a pointer to this
|
|
496
|
+
*/
|
|
497
|
+
insert(index, v) {
|
|
498
|
+
this.listener.emitAdded(this[index], v);
|
|
499
|
+
super.splice(index, 0, v);
|
|
500
|
+
return this;
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Inserts a value to the beginning of array
|
|
504
|
+
* @param v {*} value to insert
|
|
505
|
+
* @return {this} a pointer to this
|
|
506
|
+
*/
|
|
507
|
+
prepend(v) {
|
|
508
|
+
this.listener.emitAdded(this[0], v);
|
|
509
|
+
super.unshift(v);
|
|
510
|
+
return this;
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Removes a value from an index
|
|
514
|
+
* @param index {number} index of value to remove
|
|
515
|
+
* @return {this} a pointer to this
|
|
516
|
+
*/
|
|
517
|
+
removeAt(index) {
|
|
518
|
+
if (index > 0 && index < this.length) {
|
|
519
|
+
this.listener.emitRemoved(this[index], this[index]);
|
|
520
|
+
super.splice(index, 1);
|
|
521
|
+
}
|
|
522
|
+
return this;
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Removes the first value of array
|
|
526
|
+
* @return {this} a pointer to this
|
|
527
|
+
*/
|
|
528
|
+
removeFirst() {
|
|
529
|
+
if (this.length) {
|
|
530
|
+
this.listener.emitRemoved(this[0], this[0]);
|
|
531
|
+
super.shift();
|
|
532
|
+
}
|
|
533
|
+
return this;
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Removes the ast value of array
|
|
537
|
+
* @return {this} a pointer to this
|
|
538
|
+
*/
|
|
539
|
+
removeLast() {
|
|
540
|
+
const last = this.last;
|
|
541
|
+
if (last != null) {
|
|
542
|
+
this.listener.emitRemoved(this[this.length - 1], last);
|
|
543
|
+
super.pop();
|
|
544
|
+
}
|
|
545
|
+
return this;
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Remove the first occurrence of value
|
|
549
|
+
* @param v {*} value to remove
|
|
550
|
+
* @return {this}
|
|
551
|
+
*/
|
|
552
|
+
removeOne(v) {
|
|
553
|
+
this.removeAt(this.indexOf(v));
|
|
554
|
+
return this;
|
|
555
|
+
}
|
|
556
|
+
enableReactivity() {
|
|
557
|
+
this.listener.enableReactivity();
|
|
558
|
+
}
|
|
559
|
+
disableReactivity() {
|
|
560
|
+
this.listener.disableReactivity();
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
window.ArrayModel = ArrayModel;
|
|
565
|
+
|
|
566
|
+
// ./lib/core/signal.js
|
|
567
|
+
/**
|
|
568
|
+
* Signal is an event generator
|
|
569
|
+
* @class Signal
|
|
570
|
+
*/
|
|
571
|
+
class Signal {
|
|
572
|
+
constructor() {
|
|
573
|
+
/**
|
|
574
|
+
* Handler of event
|
|
575
|
+
* @type {Set}
|
|
576
|
+
* @private
|
|
577
|
+
*/
|
|
578
|
+
this.handlers = new Set;
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Emit event
|
|
582
|
+
* @param a1 {*} argument
|
|
583
|
+
* @param a2 {*} argument
|
|
584
|
+
* @param a3 {*} argument
|
|
585
|
+
* @param a4 {*} argument
|
|
586
|
+
* @param a5 {*} argument
|
|
587
|
+
* @param a6 {*} argument
|
|
588
|
+
* @param a7 {*} argument
|
|
589
|
+
* @param a8 {*} argument
|
|
590
|
+
* @param a9 {*} argument
|
|
591
|
+
*/
|
|
592
|
+
emit(a1, a2, a3, a4, a5, a6, a7, a8, a9) {
|
|
593
|
+
this.handlers.forEach(handler => {
|
|
594
|
+
try {
|
|
595
|
+
handler(a1, a2, a3, a4, a5, a6, a7, a8, a9);
|
|
596
|
+
}
|
|
597
|
+
catch (e) {
|
|
598
|
+
console.error(`Vasille.js: Handler throw exception: `, e);
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Subscribe to event
|
|
604
|
+
* @param func {function} handler
|
|
605
|
+
*/
|
|
606
|
+
subscribe(func) {
|
|
607
|
+
this.handlers.add(func);
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Unsubscribe from event
|
|
611
|
+
* @param func {function} handler
|
|
612
|
+
*/
|
|
613
|
+
unsubscribe(func) {
|
|
614
|
+
this.handlers.delete(func);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
window.Signal = Signal;
|
|
619
|
+
|
|
620
|
+
// ./lib/core/slot.js
|
|
621
|
+
/**
|
|
622
|
+
* Component slot
|
|
623
|
+
* @class Slot
|
|
624
|
+
*/
|
|
625
|
+
class Slot {
|
|
626
|
+
/**
|
|
627
|
+
* Sets the runner
|
|
628
|
+
* @param func {function} the function to run
|
|
629
|
+
*/
|
|
630
|
+
insert(func) {
|
|
631
|
+
this.runner = func;
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* @param a0 {Fragment} node to paste content
|
|
635
|
+
* @param a1 {*} 1st argument
|
|
636
|
+
* @param a2 {*} 2nd argument
|
|
637
|
+
* @param a3 {*} 3rd argument
|
|
638
|
+
* @param a4 {*} 4th argument
|
|
639
|
+
* @param a5 {*} 5th argument
|
|
640
|
+
* @param a6 {*} 6th argument
|
|
641
|
+
* @param a7 {*} 7th argument
|
|
642
|
+
* @param a8 {*} 8th argument
|
|
643
|
+
* @param a9 {*} 9th argument
|
|
644
|
+
*/
|
|
645
|
+
release(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) {
|
|
646
|
+
if (this.runner) {
|
|
647
|
+
this.runner(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Predefine a handler for a slot
|
|
652
|
+
* @param func {function(node : Fragment)} Function to run if no handler specified
|
|
653
|
+
* @param a0 {Fragment} node to paste content
|
|
654
|
+
* @param a1 {*} 1st argument
|
|
655
|
+
* @param a2 {*} 2nd argument
|
|
656
|
+
* @param a3 {*} 3rd argument
|
|
657
|
+
* @param a4 {*} 4th argument
|
|
658
|
+
* @param a5 {*} 5th argument
|
|
659
|
+
* @param a6 {*} 6th argument
|
|
660
|
+
* @param a7 {*} 7th argument
|
|
661
|
+
* @param a8 {*} 8th argument
|
|
662
|
+
* @param a9 {*} 9th argument
|
|
663
|
+
*/
|
|
664
|
+
predefine(func, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) {
|
|
665
|
+
(this.runner || func)(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
window.Slot = Slot;
|
|
670
|
+
|
|
671
|
+
// ./lib/core/errors.js
|
|
672
|
+
const reportIt = "Report it here: https://gitlab.com/vasille-js/vasille-js/-/issues";
|
|
673
|
+
function notOverwritten() {
|
|
674
|
+
console.error("Vasille-SFP: Internal error", "Must be overwritten", reportIt);
|
|
675
|
+
return "not-overwritten";
|
|
676
|
+
}
|
|
677
|
+
function internalError(msg) {
|
|
678
|
+
console.error("Vasille-SFP: Internal error", msg, reportIt);
|
|
679
|
+
return "internal-error";
|
|
680
|
+
}
|
|
681
|
+
function userError(msg, err) {
|
|
682
|
+
console.error("Vasille-SFP: User error", msg);
|
|
683
|
+
return err;
|
|
684
|
+
}
|
|
685
|
+
function wrongBinding(msg) {
|
|
686
|
+
return userError(msg, "wrong-binding");
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
window.notOverwritten = notOverwritten;
|
|
690
|
+
window.internalError = internalError;
|
|
691
|
+
window.userError = userError;
|
|
692
|
+
window.wrongBinding = wrongBinding;
|
|
693
|
+
|
|
694
|
+
// ./lib/core/executor.js
|
|
695
|
+
/**
|
|
696
|
+
* Represents an executor unit interface
|
|
697
|
+
* @class Executor
|
|
698
|
+
*/
|
|
699
|
+
class Executor {
|
|
700
|
+
/**
|
|
701
|
+
* Adds a CSS class
|
|
702
|
+
* @param el {Element} element to manipulate
|
|
703
|
+
* @param cl {string} class to be added
|
|
704
|
+
*/
|
|
705
|
+
addClass(el, cl) {
|
|
706
|
+
throw notOverwritten();
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Removes a CSS class
|
|
710
|
+
* @param el {Element} element to manipulate
|
|
711
|
+
* @param cl {string} class to be removed
|
|
712
|
+
*/
|
|
713
|
+
removeClass(el, cl) {
|
|
714
|
+
throw notOverwritten();
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Sets a tag attribute
|
|
718
|
+
* @param el {Element} element to manipulate
|
|
719
|
+
* @param name {string} name of attribute
|
|
720
|
+
* @param value {string} value of attribute
|
|
721
|
+
*/
|
|
722
|
+
setAttribute(el, name, value) {
|
|
723
|
+
throw notOverwritten();
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Removes a tag attribute
|
|
727
|
+
* @param el {Element} element to manipulate
|
|
728
|
+
* @param name {string} name of attribute
|
|
729
|
+
*/
|
|
730
|
+
removeAttribute(el, name) {
|
|
731
|
+
throw notOverwritten();
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* Sets a style attribute
|
|
735
|
+
* @param el {HTMLElement} element to manipulate
|
|
736
|
+
* @param prop {string} property name
|
|
737
|
+
* @param value {string} property value
|
|
738
|
+
*/
|
|
739
|
+
setStyle(el, prop, value) {
|
|
740
|
+
throw notOverwritten();
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* Inserts a child before target
|
|
744
|
+
* @param target {Element} target element
|
|
745
|
+
* @param child {Node} element to insert before
|
|
746
|
+
*/
|
|
747
|
+
insertBefore(target, child) {
|
|
748
|
+
throw notOverwritten();
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Appends a child to element
|
|
752
|
+
* @param el {Element} element
|
|
753
|
+
* @param child {Node} child to be inserted
|
|
754
|
+
*/
|
|
755
|
+
appendChild(el, child) {
|
|
756
|
+
throw notOverwritten();
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Calls a call-back function
|
|
760
|
+
* @param cb {function} call-back function
|
|
761
|
+
*/
|
|
762
|
+
callCallback(cb) {
|
|
763
|
+
throw notOverwritten();
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* Executor which execute any commands immediately
|
|
768
|
+
* @class InstantExecutor
|
|
769
|
+
* @extends Executor
|
|
770
|
+
*/
|
|
771
|
+
class InstantExecutor extends Executor {
|
|
772
|
+
addClass(el, cl) {
|
|
773
|
+
el.classList.add(cl);
|
|
774
|
+
}
|
|
775
|
+
removeClass(el, cl) {
|
|
776
|
+
el.classList.remove(cl);
|
|
777
|
+
}
|
|
778
|
+
setAttribute(el, name, value) {
|
|
779
|
+
el.setAttribute(name, value);
|
|
780
|
+
}
|
|
781
|
+
removeAttribute(el, name) {
|
|
782
|
+
el.removeAttribute(name);
|
|
783
|
+
}
|
|
784
|
+
setStyle(el, prop, value) {
|
|
785
|
+
el.style.setProperty(prop, value);
|
|
786
|
+
}
|
|
787
|
+
insertBefore(target, child) {
|
|
788
|
+
const parent = target.parentNode;
|
|
789
|
+
if (!parent) {
|
|
790
|
+
throw internalError('element don\'t have a parent node');
|
|
791
|
+
}
|
|
792
|
+
parent.insertBefore(child, target);
|
|
793
|
+
}
|
|
794
|
+
appendChild(el, child) {
|
|
795
|
+
el.appendChild(child);
|
|
796
|
+
}
|
|
797
|
+
callCallback(cb) {
|
|
798
|
+
cb();
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* Executor which execute any commands over timeout
|
|
803
|
+
* @class TimeoutExecutor
|
|
804
|
+
* @extends InstantExecutor
|
|
805
|
+
*/
|
|
806
|
+
class TimeoutExecutor extends InstantExecutor {
|
|
807
|
+
addClass(el, cl) {
|
|
808
|
+
setTimeout(() => {
|
|
809
|
+
super.addClass(el, cl);
|
|
810
|
+
}, 0);
|
|
811
|
+
}
|
|
812
|
+
removeClass(el, cl) {
|
|
813
|
+
setTimeout(() => {
|
|
814
|
+
super.removeClass(el, cl);
|
|
815
|
+
}, 0);
|
|
816
|
+
}
|
|
817
|
+
setAttribute(el, name, value) {
|
|
818
|
+
setTimeout(() => {
|
|
819
|
+
super.setAttribute(el, name, value);
|
|
820
|
+
}, 0);
|
|
821
|
+
}
|
|
822
|
+
removeAttribute(el, name) {
|
|
823
|
+
setTimeout(() => {
|
|
824
|
+
super.removeAttribute(el, name);
|
|
825
|
+
}, 0);
|
|
826
|
+
}
|
|
827
|
+
setStyle(el, prop, value) {
|
|
828
|
+
setTimeout(() => {
|
|
829
|
+
super.setStyle(el, prop, value);
|
|
830
|
+
}, 0);
|
|
831
|
+
}
|
|
832
|
+
insertBefore(target, child) {
|
|
833
|
+
setTimeout(() => {
|
|
834
|
+
super.insertBefore(target, child);
|
|
835
|
+
}, 0);
|
|
836
|
+
}
|
|
837
|
+
appendChild(el, child) {
|
|
838
|
+
setTimeout(() => {
|
|
839
|
+
super.appendChild(el, child);
|
|
840
|
+
}, 0);
|
|
841
|
+
}
|
|
842
|
+
callCallback(cb) {
|
|
843
|
+
setTimeout(cb, 0);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
const instantExecutor = new InstantExecutor();
|
|
847
|
+
const timeoutExecutor = new TimeoutExecutor();
|
|
848
|
+
|
|
849
|
+
window.Executor = Executor;
|
|
850
|
+
window.InstantExecutor = InstantExecutor;
|
|
851
|
+
window.TimeoutExecutor = TimeoutExecutor;
|
|
852
|
+
window.instantExecutor = instantExecutor;
|
|
853
|
+
window.timeoutExecutor = timeoutExecutor;
|
|
854
|
+
|
|
855
|
+
// ./lib/core/destroyable.js
|
|
856
|
+
/**
|
|
857
|
+
* Mark an object which can be destroyed
|
|
858
|
+
* @class Destroyable
|
|
859
|
+
*/
|
|
860
|
+
class Destroyable {
|
|
861
|
+
/**
|
|
862
|
+
* Make object fields non configurable
|
|
863
|
+
* @protected
|
|
864
|
+
*/
|
|
865
|
+
$seal() {
|
|
866
|
+
const $ = this;
|
|
867
|
+
Object.keys($).forEach(i => {
|
|
868
|
+
// eslint-disable-next-line no-prototype-builtins
|
|
869
|
+
if (this.hasOwnProperty(i)) {
|
|
870
|
+
const config = Object.getOwnPropertyDescriptor($, i);
|
|
871
|
+
if (config.configurable) {
|
|
872
|
+
let descriptor;
|
|
873
|
+
if (config.set || config.get) {
|
|
874
|
+
descriptor = {
|
|
875
|
+
configurable: false,
|
|
876
|
+
get: config.get,
|
|
877
|
+
set: config.set,
|
|
878
|
+
enumerable: config.enumerable
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
else {
|
|
882
|
+
descriptor = {
|
|
883
|
+
value: $[i],
|
|
884
|
+
configurable: false,
|
|
885
|
+
writable: config.writable,
|
|
886
|
+
enumerable: config.enumerable
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
Object.defineProperty($, i, descriptor);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* Garbage collector method
|
|
896
|
+
*/
|
|
897
|
+
$destroy() {
|
|
898
|
+
// nothing here
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
window.Destroyable = Destroyable;
|
|
903
|
+
|
|
904
|
+
// ./lib/core/ivalue.js
|
|
905
|
+
/**
|
|
906
|
+
* Interface which describes a value
|
|
907
|
+
* @class IValue
|
|
908
|
+
* @extends Destroyable
|
|
909
|
+
*/
|
|
910
|
+
class IValue extends Destroyable {
|
|
911
|
+
/**
|
|
912
|
+
* @param isEnabled {boolean} initial is enabled state
|
|
913
|
+
*/
|
|
914
|
+
constructor(isEnabled) {
|
|
915
|
+
super();
|
|
916
|
+
this.isEnabled = isEnabled;
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Get the encapsulated value
|
|
920
|
+
* @return {*} the encapsulated value
|
|
921
|
+
*/
|
|
922
|
+
get $() {
|
|
923
|
+
throw notOverwritten();
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Sets the encapsulated value
|
|
927
|
+
* @param value {*} value to encapsulate
|
|
928
|
+
*/
|
|
929
|
+
set $(value) {
|
|
930
|
+
throw notOverwritten();
|
|
931
|
+
}
|
|
932
|
+
/**
|
|
933
|
+
* Add a new handler to value change
|
|
934
|
+
* @param handler {function(value : *)} the handler to add
|
|
935
|
+
*/
|
|
936
|
+
on(handler) {
|
|
937
|
+
throw notOverwritten();
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* Removes a handler of value change
|
|
941
|
+
* @param handler {function(value : *)} the handler to remove
|
|
942
|
+
*/
|
|
943
|
+
off(handler) {
|
|
944
|
+
throw notOverwritten();
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* Enable update handlers triggering
|
|
948
|
+
*/
|
|
949
|
+
enable() {
|
|
950
|
+
throw notOverwritten();
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* disable update handlers triggering
|
|
954
|
+
*/
|
|
955
|
+
disable() {
|
|
956
|
+
throw notOverwritten();
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
window.IValue = IValue;
|
|
961
|
+
|
|
962
|
+
// ./lib/index.js
|
|
963
|
+
|
|
964
|
+
|
|
965
|
+
|
|
966
|
+
// ./lib/value/expression.js
|
|
967
|
+
/**
|
|
968
|
+
* Bind some values to one expression
|
|
969
|
+
* @class Expression
|
|
970
|
+
* @extends IValue
|
|
971
|
+
*/
|
|
972
|
+
class Expression extends IValue {
|
|
973
|
+
constructor(func, link, v1, v2, v3, v4, v5, v6, v7, v8, v9) {
|
|
974
|
+
super(false);
|
|
975
|
+
/**
|
|
976
|
+
* Expression will link different handler for each value of list
|
|
977
|
+
*/
|
|
978
|
+
this.linkedFunc = [];
|
|
979
|
+
const values = [v1, v2, v3, v4, v5, v6, v7, v8, v9].filter(v => v instanceof IValue);
|
|
980
|
+
const handler = (i) => {
|
|
981
|
+
if (i != null) {
|
|
982
|
+
this.valuesCache[i] = this.values[i].$;
|
|
983
|
+
}
|
|
984
|
+
this.sync.$ = func.apply(this, this.valuesCache);
|
|
985
|
+
};
|
|
986
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
987
|
+
// @ts-ignore
|
|
988
|
+
this.valuesCache = values.map(iValue => iValue.$);
|
|
989
|
+
this.sync = new Reference(func.apply(this, this.valuesCache));
|
|
990
|
+
let i = 0;
|
|
991
|
+
values.forEach(() => {
|
|
992
|
+
this.linkedFunc.push(handler.bind(this, Number(i++)));
|
|
993
|
+
});
|
|
994
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
995
|
+
// @ts-ignore
|
|
996
|
+
this.values = values;
|
|
997
|
+
this.func = handler;
|
|
998
|
+
if (link) {
|
|
999
|
+
this.enable();
|
|
1000
|
+
}
|
|
1001
|
+
else {
|
|
1002
|
+
handler();
|
|
1003
|
+
}
|
|
1004
|
+
this.$seal();
|
|
1005
|
+
}
|
|
1006
|
+
get $() {
|
|
1007
|
+
return this.sync.$;
|
|
1008
|
+
}
|
|
1009
|
+
set $(value) {
|
|
1010
|
+
this.sync.$ = value;
|
|
1011
|
+
}
|
|
1012
|
+
on(handler) {
|
|
1013
|
+
this.sync.on(handler);
|
|
1014
|
+
return this;
|
|
1015
|
+
}
|
|
1016
|
+
off(handler) {
|
|
1017
|
+
this.sync.off(handler);
|
|
1018
|
+
return this;
|
|
1019
|
+
}
|
|
1020
|
+
enable() {
|
|
1021
|
+
if (!this.isEnabled) {
|
|
1022
|
+
for (let i = 0; i < this.values.length; i++) {
|
|
1023
|
+
this.values[i].on(this.linkedFunc[i]);
|
|
1024
|
+
this.valuesCache[i] = this.values[i].$;
|
|
1025
|
+
}
|
|
1026
|
+
this.func();
|
|
1027
|
+
this.isEnabled = true;
|
|
1028
|
+
}
|
|
1029
|
+
return this;
|
|
1030
|
+
}
|
|
1031
|
+
disable() {
|
|
1032
|
+
if (this.isEnabled) {
|
|
1033
|
+
for (let i = 0; i < this.values.length; i++) {
|
|
1034
|
+
this.values[i].off(this.linkedFunc[i]);
|
|
1035
|
+
}
|
|
1036
|
+
this.isEnabled = false;
|
|
1037
|
+
}
|
|
1038
|
+
return this;
|
|
1039
|
+
}
|
|
1040
|
+
$destroy() {
|
|
1041
|
+
this.disable();
|
|
1042
|
+
this.values.splice(0);
|
|
1043
|
+
this.valuesCache.splice(0);
|
|
1044
|
+
this.linkedFunc.splice(0);
|
|
1045
|
+
super.$destroy();
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
window.Expression = Expression;
|
|
1050
|
+
|
|
1051
|
+
// ./lib/value/reference.js
|
|
1052
|
+
/**
|
|
1053
|
+
* Declares a notifiable value
|
|
1054
|
+
* @class Reference
|
|
1055
|
+
* @extends IValue
|
|
1056
|
+
*/
|
|
1057
|
+
class Reference extends IValue {
|
|
1058
|
+
/**
|
|
1059
|
+
* @param value {any} the initial value
|
|
1060
|
+
*/
|
|
1061
|
+
constructor(value) {
|
|
1062
|
+
super(true);
|
|
1063
|
+
this.value = value;
|
|
1064
|
+
this.onchange = new Set;
|
|
1065
|
+
this.$seal();
|
|
1066
|
+
}
|
|
1067
|
+
get $() {
|
|
1068
|
+
return this.value;
|
|
1069
|
+
}
|
|
1070
|
+
set $(value) {
|
|
1071
|
+
if (this.value !== value) {
|
|
1072
|
+
this.value = value;
|
|
1073
|
+
if (this.isEnabled) {
|
|
1074
|
+
this.onchange.forEach(handler => {
|
|
1075
|
+
handler(value);
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
enable() {
|
|
1081
|
+
if (!this.isEnabled) {
|
|
1082
|
+
this.onchange.forEach(handler => {
|
|
1083
|
+
handler(this.value);
|
|
1084
|
+
});
|
|
1085
|
+
this.isEnabled = true;
|
|
1086
|
+
}
|
|
1087
|
+
return this;
|
|
1088
|
+
}
|
|
1089
|
+
disable() {
|
|
1090
|
+
this.isEnabled = false;
|
|
1091
|
+
return this;
|
|
1092
|
+
}
|
|
1093
|
+
on(handler) {
|
|
1094
|
+
this.onchange.add(handler);
|
|
1095
|
+
return this;
|
|
1096
|
+
}
|
|
1097
|
+
off(handler) {
|
|
1098
|
+
this.onchange.delete(handler);
|
|
1099
|
+
return this;
|
|
1100
|
+
}
|
|
1101
|
+
$destroy() {
|
|
1102
|
+
super.$destroy();
|
|
1103
|
+
this.onchange.clear();
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
window.Reference = Reference;
|
|
1108
|
+
|
|
1109
|
+
// ./lib/value/mirror.js
|
|
1110
|
+
/**
|
|
1111
|
+
* Declares a notifiable bind to a value
|
|
1112
|
+
* @class Mirror
|
|
1113
|
+
* @extends IValue
|
|
1114
|
+
* @version 2
|
|
1115
|
+
*/
|
|
1116
|
+
class Mirror extends Reference {
|
|
1117
|
+
/**
|
|
1118
|
+
* Constructs a notifiable bind to a value
|
|
1119
|
+
* @param value {IValue} is initial value
|
|
1120
|
+
* @param forwardOnly {boolean} ensure forward only synchronization
|
|
1121
|
+
*/
|
|
1122
|
+
constructor(value, forwardOnly = false) {
|
|
1123
|
+
super(value.$);
|
|
1124
|
+
this.handler = (v) => {
|
|
1125
|
+
this.$ = v;
|
|
1126
|
+
};
|
|
1127
|
+
this.pointedValue = value;
|
|
1128
|
+
this.forwardOnly = forwardOnly;
|
|
1129
|
+
value.on(this.handler);
|
|
1130
|
+
this.$seal();
|
|
1131
|
+
}
|
|
1132
|
+
get $() {
|
|
1133
|
+
// this is a ts bug
|
|
1134
|
+
// eslint-disable-next-line
|
|
1135
|
+
// @ts-ignore
|
|
1136
|
+
return super.$;
|
|
1137
|
+
}
|
|
1138
|
+
set $(v) {
|
|
1139
|
+
// this is a ts bug
|
|
1140
|
+
// eslint-disable-next-line
|
|
1141
|
+
// @ts-ignore
|
|
1142
|
+
super.$ = v;
|
|
1143
|
+
if (!this.forwardOnly) {
|
|
1144
|
+
this.pointedValue.$ = v;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
enable() {
|
|
1148
|
+
if (!this.isEnabled) {
|
|
1149
|
+
this.isEnabled = true;
|
|
1150
|
+
this.pointedValue.on(this.handler);
|
|
1151
|
+
this.$ = this.pointedValue.$;
|
|
1152
|
+
}
|
|
1153
|
+
return this;
|
|
1154
|
+
}
|
|
1155
|
+
disable() {
|
|
1156
|
+
if (this.isEnabled) {
|
|
1157
|
+
this.pointedValue.off(this.handler);
|
|
1158
|
+
this.isEnabled = false;
|
|
1159
|
+
}
|
|
1160
|
+
return this;
|
|
1161
|
+
}
|
|
1162
|
+
$destroy() {
|
|
1163
|
+
this.disable();
|
|
1164
|
+
super.$destroy();
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
window.Mirror = Mirror;
|
|
1169
|
+
|
|
1170
|
+
// ./lib/value/pointer.js
|
|
1171
|
+
/**
|
|
1172
|
+
* r/w pointer to a value
|
|
1173
|
+
* @class Pointer
|
|
1174
|
+
* @extends Mirror
|
|
1175
|
+
*/
|
|
1176
|
+
class Pointer extends Mirror {
|
|
1177
|
+
/**
|
|
1178
|
+
* @param value {IValue} value to point
|
|
1179
|
+
* @param forwardOnly {boolean} forward only data flow
|
|
1180
|
+
*/
|
|
1181
|
+
constructor(value, forwardOnly = false) {
|
|
1182
|
+
super(value, forwardOnly);
|
|
1183
|
+
}
|
|
1184
|
+
/**
|
|
1185
|
+
* Point a new ivalue
|
|
1186
|
+
* @param value {IValue} value to point
|
|
1187
|
+
*/
|
|
1188
|
+
point(value) {
|
|
1189
|
+
if (this.pointedValue !== value) {
|
|
1190
|
+
this.disable();
|
|
1191
|
+
this.pointedValue = value;
|
|
1192
|
+
this.enable();
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
window.Pointer = Pointer;
|
|
1198
|
+
|
|
1199
|
+
// ./lib/binding/binding.js
|
|
1200
|
+
/**
|
|
1201
|
+
* Describe a common binding logic
|
|
1202
|
+
* @class Binding
|
|
1203
|
+
* @extends Destroyable
|
|
1204
|
+
*/
|
|
1205
|
+
class Binding extends Destroyable {
|
|
1206
|
+
/**
|
|
1207
|
+
* Constructs a common binding logic
|
|
1208
|
+
* @param node {INode} the vasille node
|
|
1209
|
+
* @param name {String} the name of property/attribute/class
|
|
1210
|
+
* @param value {IValue} the value to bind
|
|
1211
|
+
*/
|
|
1212
|
+
constructor(node, name, value) {
|
|
1213
|
+
super();
|
|
1214
|
+
this.updateFunc = this.bound(name).bind(null, node);
|
|
1215
|
+
this.binding = value;
|
|
1216
|
+
this.binding.on(this.updateFunc);
|
|
1217
|
+
this.updateFunc(this.binding.$);
|
|
1218
|
+
this.$seal();
|
|
1219
|
+
}
|
|
1220
|
+
/**
|
|
1221
|
+
* Is a virtual function to get the specific bind function
|
|
1222
|
+
* @param name {String} the name of attribute/property
|
|
1223
|
+
* @returns {Function} a function to update attribute/property value
|
|
1224
|
+
* @throws Always throws and must be overloaded in child class
|
|
1225
|
+
*/
|
|
1226
|
+
bound(name) {
|
|
1227
|
+
throw notOverwritten();
|
|
1228
|
+
}
|
|
1229
|
+
/**
|
|
1230
|
+
* Just clear bindings
|
|
1231
|
+
*/
|
|
1232
|
+
$destroy() {
|
|
1233
|
+
this.binding.off(this.updateFunc);
|
|
1234
|
+
super.$destroy();
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
window.Binding = Binding;
|
|
1239
|
+
|
|
1240
|
+
// ./lib/core/core.js
|
|
1241
|
+
/**
|
|
1242
|
+
* Private stuff of a reactive object
|
|
1243
|
+
* @class ReactivePrivate
|
|
1244
|
+
* @extends Destroyable
|
|
1245
|
+
*/
|
|
1246
|
+
class ReactivePrivate extends Destroyable {
|
|
1247
|
+
constructor() {
|
|
1248
|
+
super();
|
|
1249
|
+
/**
|
|
1250
|
+
* A list of user-defined values
|
|
1251
|
+
* @type {Set}
|
|
1252
|
+
*/
|
|
1253
|
+
this.watch = new Set;
|
|
1254
|
+
/**
|
|
1255
|
+
* A list of user-defined bindings
|
|
1256
|
+
* @type {Set}
|
|
1257
|
+
*/
|
|
1258
|
+
this.bindings = new Set;
|
|
1259
|
+
/**
|
|
1260
|
+
* A list of user defined models
|
|
1261
|
+
*/
|
|
1262
|
+
this.models = new Set;
|
|
1263
|
+
/**
|
|
1264
|
+
* Reactivity switch state
|
|
1265
|
+
* @type {boolean}
|
|
1266
|
+
*/
|
|
1267
|
+
this.enabled = true;
|
|
1268
|
+
/**
|
|
1269
|
+
* The frozen state of object
|
|
1270
|
+
* @type {boolean}
|
|
1271
|
+
*/
|
|
1272
|
+
this.frozen = false;
|
|
1273
|
+
this.$seal();
|
|
1274
|
+
}
|
|
1275
|
+
$destroy() {
|
|
1276
|
+
var _a;
|
|
1277
|
+
this.watch.forEach(value => value.$destroy());
|
|
1278
|
+
this.watch.clear();
|
|
1279
|
+
this.bindings.forEach(binding => binding.$destroy());
|
|
1280
|
+
this.bindings.clear();
|
|
1281
|
+
(_a = this.freezeExpr) === null || _a === void 0 ? void 0 : _a.$destroy();
|
|
1282
|
+
super.$destroy();
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
/**
|
|
1286
|
+
* A reactive object
|
|
1287
|
+
* @class Reactive
|
|
1288
|
+
* @extends Destroyable
|
|
1289
|
+
*/
|
|
1290
|
+
class Reactive extends Destroyable {
|
|
1291
|
+
constructor($) {
|
|
1292
|
+
super();
|
|
1293
|
+
this.$ = $ || new ReactivePrivate;
|
|
1294
|
+
}
|
|
1295
|
+
/**
|
|
1296
|
+
* Create a reference
|
|
1297
|
+
* @param value {*} value to reference
|
|
1298
|
+
*/
|
|
1299
|
+
$ref(value) {
|
|
1300
|
+
const $ = this.$;
|
|
1301
|
+
const ref = new Reference(value);
|
|
1302
|
+
$.watch.add(ref);
|
|
1303
|
+
return ref;
|
|
1304
|
+
}
|
|
1305
|
+
/**
|
|
1306
|
+
* Create a mirror
|
|
1307
|
+
* @param value {IValue} value to mirror
|
|
1308
|
+
*/
|
|
1309
|
+
$mirror(value) {
|
|
1310
|
+
const mirror = new Mirror(value, false);
|
|
1311
|
+
this.$.watch.add(mirror);
|
|
1312
|
+
return mirror;
|
|
1313
|
+
}
|
|
1314
|
+
/**
|
|
1315
|
+
* Create a forward-only mirror
|
|
1316
|
+
* @param value {IValue} value to mirror
|
|
1317
|
+
*/
|
|
1318
|
+
$forward(value) {
|
|
1319
|
+
const mirror = new Mirror(value, true);
|
|
1320
|
+
this.$.watch.add(mirror);
|
|
1321
|
+
return mirror;
|
|
1322
|
+
}
|
|
1323
|
+
/**
|
|
1324
|
+
* Creates a pointer
|
|
1325
|
+
* @param value {*} default value to point
|
|
1326
|
+
* @param forwardOnly {boolean} forward only sync
|
|
1327
|
+
*/
|
|
1328
|
+
$point(value, forwardOnly = false) {
|
|
1329
|
+
const $ = this.$;
|
|
1330
|
+
const ref = value instanceof IValue ? value : new Reference(value);
|
|
1331
|
+
const pointer = new Pointer(ref, forwardOnly);
|
|
1332
|
+
// when value is an ivalue will be equal to ref
|
|
1333
|
+
if (value !== ref) {
|
|
1334
|
+
$.watch.add(ref);
|
|
1335
|
+
}
|
|
1336
|
+
$.watch.add(pointer);
|
|
1337
|
+
return pointer;
|
|
1338
|
+
}
|
|
1339
|
+
/**
|
|
1340
|
+
* Register a model
|
|
1341
|
+
* @param model
|
|
1342
|
+
*/
|
|
1343
|
+
$register(model) {
|
|
1344
|
+
this.$.models.add(model);
|
|
1345
|
+
return model;
|
|
1346
|
+
}
|
|
1347
|
+
$watch(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) {
|
|
1348
|
+
const $ = this.$;
|
|
1349
|
+
$.watch.add(new Expression(func, !this.$.frozen, v1, v2, v3, v4, v5, v6, v7, v8, v9));
|
|
1350
|
+
}
|
|
1351
|
+
$bind(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) {
|
|
1352
|
+
const res = new Expression(func, !this.$.frozen, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
|
1353
|
+
const $ = this.$;
|
|
1354
|
+
$.watch.add(res);
|
|
1355
|
+
return res;
|
|
1356
|
+
}
|
|
1357
|
+
/**
|
|
1358
|
+
* Enable reactivity of fields
|
|
1359
|
+
*/
|
|
1360
|
+
$enable() {
|
|
1361
|
+
const $ = this.$;
|
|
1362
|
+
if (!$.enabled) {
|
|
1363
|
+
$.watch.forEach(watcher => {
|
|
1364
|
+
watcher.enable();
|
|
1365
|
+
});
|
|
1366
|
+
$.models.forEach(model => {
|
|
1367
|
+
model.enableReactivity();
|
|
1368
|
+
});
|
|
1369
|
+
$.enabled = true;
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
/**
|
|
1373
|
+
* Disable reactivity of fields
|
|
1374
|
+
*/
|
|
1375
|
+
$disable() {
|
|
1376
|
+
const $ = this.$;
|
|
1377
|
+
if ($.enabled) {
|
|
1378
|
+
$.watch.forEach(watcher => {
|
|
1379
|
+
watcher.disable();
|
|
1380
|
+
});
|
|
1381
|
+
$.models.forEach(model => {
|
|
1382
|
+
model.disableReactivity();
|
|
1383
|
+
});
|
|
1384
|
+
$.enabled = false;
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
/**
|
|
1388
|
+
* Disable/Enable reactivity of object fields with feedback
|
|
1389
|
+
* @param cond {IValue} show condition
|
|
1390
|
+
* @param onOff {function} on show feedback
|
|
1391
|
+
* @param onOn {function} on hide feedback
|
|
1392
|
+
*/
|
|
1393
|
+
$bindAlive(cond, onOff, onOn) {
|
|
1394
|
+
const $ = this.$;
|
|
1395
|
+
if ($.freezeExpr) {
|
|
1396
|
+
throw wrongBinding("this component already have a freeze state");
|
|
1397
|
+
}
|
|
1398
|
+
if ($.watch.has(cond)) {
|
|
1399
|
+
throw wrongBinding("freeze state must be bound to an external component");
|
|
1400
|
+
}
|
|
1401
|
+
$.freezeExpr = new Expression((cond) => {
|
|
1402
|
+
$.frozen = !cond;
|
|
1403
|
+
if (cond) {
|
|
1404
|
+
onOn === null || onOn === void 0 ? void 0 : onOn();
|
|
1405
|
+
this.$enable();
|
|
1406
|
+
}
|
|
1407
|
+
else {
|
|
1408
|
+
onOff === null || onOff === void 0 ? void 0 : onOff();
|
|
1409
|
+
this.$disable();
|
|
1410
|
+
}
|
|
1411
|
+
}, true, cond);
|
|
1412
|
+
return this;
|
|
1413
|
+
}
|
|
1414
|
+
$destroy() {
|
|
1415
|
+
super.$destroy();
|
|
1416
|
+
this.$.$destroy();
|
|
1417
|
+
this.$ = null;
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
window.ReactivePrivate = ReactivePrivate;
|
|
1422
|
+
window.Reactive = Reactive;
|
|
1423
|
+
|
|
1424
|
+
// ./lib/node/node.js
|
|
1425
|
+
/**
|
|
1426
|
+
* Represents a Vasille.js node
|
|
1427
|
+
* @class FragmentPrivate
|
|
1428
|
+
* @extends ReactivePrivate
|
|
1429
|
+
*/
|
|
1430
|
+
class FragmentPrivate extends ReactivePrivate {
|
|
1431
|
+
constructor() {
|
|
1432
|
+
super();
|
|
1433
|
+
this.$seal();
|
|
1434
|
+
}
|
|
1435
|
+
/**
|
|
1436
|
+
* Pre-initializes the base of a fragment
|
|
1437
|
+
* @param app {App} the app node
|
|
1438
|
+
* @param parent {Fragment} the parent node
|
|
1439
|
+
*/
|
|
1440
|
+
preinit(app, parent) {
|
|
1441
|
+
this.app = app;
|
|
1442
|
+
this.parent = parent;
|
|
1443
|
+
}
|
|
1444
|
+
/**
|
|
1445
|
+
* Unlinks all bindings
|
|
1446
|
+
*/
|
|
1447
|
+
$destroy() {
|
|
1448
|
+
this.next = null;
|
|
1449
|
+
this.prev = null;
|
|
1450
|
+
super.$destroy();
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
/**
|
|
1454
|
+
* This class is symbolic
|
|
1455
|
+
* @extends Reactive
|
|
1456
|
+
*/
|
|
1457
|
+
class Fragment extends Reactive {
|
|
1458
|
+
/**
|
|
1459
|
+
* Constructs a Vasille Node
|
|
1460
|
+
* @param $ {FragmentPrivate}
|
|
1461
|
+
*/
|
|
1462
|
+
constructor($) {
|
|
1463
|
+
super();
|
|
1464
|
+
/**
|
|
1465
|
+
* The children list
|
|
1466
|
+
* @type Array
|
|
1467
|
+
*/
|
|
1468
|
+
this.$children = [];
|
|
1469
|
+
this.$ = $ || new FragmentPrivate;
|
|
1470
|
+
}
|
|
1471
|
+
/**
|
|
1472
|
+
* Gets the app of node
|
|
1473
|
+
*/
|
|
1474
|
+
get app() {
|
|
1475
|
+
return this.$.app;
|
|
1476
|
+
}
|
|
1477
|
+
/**
|
|
1478
|
+
* Prepare to init fragment
|
|
1479
|
+
* @param app {AppNode} app of node
|
|
1480
|
+
* @param parent {Fragment} parent of node
|
|
1481
|
+
* @param data {*} additional data
|
|
1482
|
+
*/
|
|
1483
|
+
$preinit(app, parent, data) {
|
|
1484
|
+
const $ = this.$;
|
|
1485
|
+
$.preinit(app, parent);
|
|
1486
|
+
}
|
|
1487
|
+
/**
|
|
1488
|
+
* Initialize node
|
|
1489
|
+
*/
|
|
1490
|
+
$init() {
|
|
1491
|
+
this.$createSignals();
|
|
1492
|
+
this.$createWatchers();
|
|
1493
|
+
this.$created();
|
|
1494
|
+
this.$compose();
|
|
1495
|
+
this.$mounted();
|
|
1496
|
+
return this;
|
|
1497
|
+
}
|
|
1498
|
+
/** To be overloaded: created event handler */
|
|
1499
|
+
$created() {
|
|
1500
|
+
// empty
|
|
1501
|
+
}
|
|
1502
|
+
/** To be overloaded: mounted event handler */
|
|
1503
|
+
$mounted() {
|
|
1504
|
+
// empty
|
|
1505
|
+
}
|
|
1506
|
+
/** To be overloaded: ready event handler */
|
|
1507
|
+
$ready() {
|
|
1508
|
+
// empty
|
|
1509
|
+
}
|
|
1510
|
+
/** To be overloaded: signals creation milestone */
|
|
1511
|
+
$createSignals() {
|
|
1512
|
+
// empty
|
|
1513
|
+
}
|
|
1514
|
+
/** To be overloaded: watchers creation milestone */
|
|
1515
|
+
$createWatchers() {
|
|
1516
|
+
// empty
|
|
1517
|
+
}
|
|
1518
|
+
/** To be overloaded: DOM creation milestone */
|
|
1519
|
+
$compose() {
|
|
1520
|
+
// empty
|
|
1521
|
+
}
|
|
1522
|
+
/**
|
|
1523
|
+
* Pushes a node to children immediately
|
|
1524
|
+
* @param node {Fragment} A node to push
|
|
1525
|
+
* @protected
|
|
1526
|
+
*/
|
|
1527
|
+
$$pushNode(node) {
|
|
1528
|
+
let lastChild = null;
|
|
1529
|
+
if (this.$children.length) {
|
|
1530
|
+
lastChild = this.$children[this.$children.length - 1];
|
|
1531
|
+
}
|
|
1532
|
+
if (lastChild) {
|
|
1533
|
+
lastChild.$.next = node;
|
|
1534
|
+
}
|
|
1535
|
+
node.$.prev = lastChild;
|
|
1536
|
+
node.$.parent = this;
|
|
1537
|
+
this.$children.push(node);
|
|
1538
|
+
}
|
|
1539
|
+
/**
|
|
1540
|
+
* Find first node in element if so exists
|
|
1541
|
+
* @return {?Element}
|
|
1542
|
+
* @protected
|
|
1543
|
+
*/
|
|
1544
|
+
$$findFirstChild() {
|
|
1545
|
+
let first;
|
|
1546
|
+
this.$children.forEach(child => {
|
|
1547
|
+
first = first || child.$$findFirstChild();
|
|
1548
|
+
});
|
|
1549
|
+
return first;
|
|
1550
|
+
}
|
|
1551
|
+
/**
|
|
1552
|
+
* Append a node to end of element
|
|
1553
|
+
* @param node {Node} node to insert
|
|
1554
|
+
*/
|
|
1555
|
+
$$appendNode(node) {
|
|
1556
|
+
const $ = this.$;
|
|
1557
|
+
if ($.next) {
|
|
1558
|
+
$.next.$$insertAdjacent(node);
|
|
1559
|
+
}
|
|
1560
|
+
else {
|
|
1561
|
+
$.parent.$$appendNode(node);
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
/**
|
|
1565
|
+
* Insert a node as a sibling of this
|
|
1566
|
+
* @param node {Node} node to insert
|
|
1567
|
+
*/
|
|
1568
|
+
$$insertAdjacent(node) {
|
|
1569
|
+
const child = this.$$findFirstChild();
|
|
1570
|
+
const $ = this.$;
|
|
1571
|
+
if (child) {
|
|
1572
|
+
$.app.$run.insertBefore(child, node);
|
|
1573
|
+
}
|
|
1574
|
+
else if ($.next) {
|
|
1575
|
+
$.next.$$insertAdjacent(node);
|
|
1576
|
+
}
|
|
1577
|
+
else {
|
|
1578
|
+
$.parent.$$appendNode(node);
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
/**
|
|
1582
|
+
* Defines a text fragment
|
|
1583
|
+
* @param text {String | IValue} A text fragment string
|
|
1584
|
+
* @param cb {function (TextNode)} Callback if previous is slot name
|
|
1585
|
+
*/
|
|
1586
|
+
$text(text, cb) {
|
|
1587
|
+
const $ = this.$;
|
|
1588
|
+
const node = new TextNode();
|
|
1589
|
+
const textValue = text instanceof IValue ? text : this.$ref(text);
|
|
1590
|
+
node.$preinit($.app, this, textValue);
|
|
1591
|
+
this.$$pushNode(node);
|
|
1592
|
+
if (cb) {
|
|
1593
|
+
$.app.$run.callCallback(() => {
|
|
1594
|
+
cb(node);
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1597
|
+
return this;
|
|
1598
|
+
}
|
|
1599
|
+
$debug(text) {
|
|
1600
|
+
if (this.$.app.$debugUi) {
|
|
1601
|
+
const node = new DebugNode();
|
|
1602
|
+
node.$preinit(this.$.app, this, text);
|
|
1603
|
+
this.$$pushNode(node);
|
|
1604
|
+
}
|
|
1605
|
+
return this;
|
|
1606
|
+
}
|
|
1607
|
+
$tag(tagName, cb) {
|
|
1608
|
+
const $ = this.$;
|
|
1609
|
+
const node = new Tag();
|
|
1610
|
+
node.$preinit($.app, this, tagName);
|
|
1611
|
+
node.$init();
|
|
1612
|
+
this.$$pushNode(node);
|
|
1613
|
+
$.app.$run.callCallback(() => {
|
|
1614
|
+
if (cb) {
|
|
1615
|
+
cb(node, node.node);
|
|
1616
|
+
}
|
|
1617
|
+
node.$ready();
|
|
1618
|
+
});
|
|
1619
|
+
return this;
|
|
1620
|
+
}
|
|
1621
|
+
/**
|
|
1622
|
+
* Defines a custom element
|
|
1623
|
+
* @param node {Fragment} vasille element to insert
|
|
1624
|
+
* @param callback {function($ : *)}
|
|
1625
|
+
* @param callback1 {function($ : *)}
|
|
1626
|
+
*/
|
|
1627
|
+
$create(node, callback, callback1) {
|
|
1628
|
+
const $ = this.$;
|
|
1629
|
+
node.$.parent = this;
|
|
1630
|
+
node.$preinit($.app, this);
|
|
1631
|
+
if (callback) {
|
|
1632
|
+
callback(node);
|
|
1633
|
+
}
|
|
1634
|
+
if (callback1) {
|
|
1635
|
+
callback1(node);
|
|
1636
|
+
}
|
|
1637
|
+
this.$$pushNode(node);
|
|
1638
|
+
node.$init().$ready();
|
|
1639
|
+
return this;
|
|
1640
|
+
}
|
|
1641
|
+
/**
|
|
1642
|
+
* Defines an if node
|
|
1643
|
+
* @param cond {IValue} condition
|
|
1644
|
+
* @param cb {function(Fragment)} callback to run on true
|
|
1645
|
+
* @return {this}
|
|
1646
|
+
*/
|
|
1647
|
+
$if(cond, cb) {
|
|
1648
|
+
return this.$switch({ cond, cb });
|
|
1649
|
+
}
|
|
1650
|
+
/**
|
|
1651
|
+
* Defines a if-else node
|
|
1652
|
+
* @param ifCond {IValue} `if` condition
|
|
1653
|
+
* @param ifCb {function(Fragment)} Call-back to create `if` child nodes
|
|
1654
|
+
* @param elseCb {function(Fragment)} Call-back to create `else` child nodes
|
|
1655
|
+
*/
|
|
1656
|
+
$if_else(ifCond, ifCb, elseCb) {
|
|
1657
|
+
return this.$switch({ cond: ifCond, cb: ifCb }, { cond: trueIValue, cb: elseCb });
|
|
1658
|
+
}
|
|
1659
|
+
/**
|
|
1660
|
+
* Defines a switch nodes: Will break after first true condition
|
|
1661
|
+
* @param cases {...{ cond : IValue, cb : function(Fragment) }} cases
|
|
1662
|
+
* @return {INode}
|
|
1663
|
+
*/
|
|
1664
|
+
$switch(...cases) {
|
|
1665
|
+
const $ = this.$;
|
|
1666
|
+
const node = new SwitchedNode();
|
|
1667
|
+
node.$preinit($.app, this);
|
|
1668
|
+
node.$init();
|
|
1669
|
+
this.$$pushNode(node);
|
|
1670
|
+
node.setCases(cases);
|
|
1671
|
+
node.$ready();
|
|
1672
|
+
return this;
|
|
1673
|
+
}
|
|
1674
|
+
/**
|
|
1675
|
+
* Create a case for switch
|
|
1676
|
+
* @param cond {IValue<boolean>}
|
|
1677
|
+
* @param cb {function(Fragment) : void}
|
|
1678
|
+
* @return {{cond : IValue, cb : (function(Fragment) : void)}}
|
|
1679
|
+
*/
|
|
1680
|
+
$case(cond, cb) {
|
|
1681
|
+
return { cond, cb };
|
|
1682
|
+
}
|
|
1683
|
+
/**
|
|
1684
|
+
* @param cb {(function(Fragment) : void)}
|
|
1685
|
+
* @return {{cond : IValue, cb : (function(Fragment) : void)}}
|
|
1686
|
+
*/
|
|
1687
|
+
$default(cb) {
|
|
1688
|
+
return { cond: trueIValue, cb };
|
|
1689
|
+
}
|
|
1690
|
+
$destroy() {
|
|
1691
|
+
for (const child of this.$children) {
|
|
1692
|
+
child.$destroy();
|
|
1693
|
+
}
|
|
1694
|
+
this.$children.splice(0);
|
|
1695
|
+
super.$destroy();
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
const trueIValue = new Reference(true);
|
|
1699
|
+
/**
|
|
1700
|
+
* The private part of a text node
|
|
1701
|
+
* @class TextNodePrivate
|
|
1702
|
+
* @extends FragmentPrivate
|
|
1703
|
+
*/
|
|
1704
|
+
class TextNodePrivate extends FragmentPrivate {
|
|
1705
|
+
constructor() {
|
|
1706
|
+
super();
|
|
1707
|
+
this.$seal();
|
|
1708
|
+
}
|
|
1709
|
+
/**
|
|
1710
|
+
* Pre-initializes a text node
|
|
1711
|
+
* @param app {AppNode} the app node
|
|
1712
|
+
* @param text {IValue}
|
|
1713
|
+
*/
|
|
1714
|
+
preinitText(app, parent, text) {
|
|
1715
|
+
super.preinit(app, parent);
|
|
1716
|
+
this.node = document.createTextNode(text.$);
|
|
1717
|
+
this.bindings.add(new Expression((v) => {
|
|
1718
|
+
this.node.replaceData(0, -1, v);
|
|
1719
|
+
}, true, text));
|
|
1720
|
+
this.parent.$$appendNode(this.node);
|
|
1721
|
+
}
|
|
1722
|
+
/**
|
|
1723
|
+
* Clear node data
|
|
1724
|
+
*/
|
|
1725
|
+
$destroy() {
|
|
1726
|
+
super.$destroy();
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
/**
|
|
1730
|
+
* Represents a text node
|
|
1731
|
+
* @class TextNode
|
|
1732
|
+
* @extends Fragment
|
|
1733
|
+
*/
|
|
1734
|
+
class TextNode extends Fragment {
|
|
1735
|
+
constructor() {
|
|
1736
|
+
super();
|
|
1737
|
+
this.$ = new TextNodePrivate();
|
|
1738
|
+
this.$seal();
|
|
1739
|
+
}
|
|
1740
|
+
$preinit(app, parent, text) {
|
|
1741
|
+
const $ = this.$;
|
|
1742
|
+
if (!text) {
|
|
1743
|
+
throw internalError('wrong TextNode::$preninit call');
|
|
1744
|
+
}
|
|
1745
|
+
$.preinitText(app, parent, text);
|
|
1746
|
+
}
|
|
1747
|
+
$$findFirstChild() {
|
|
1748
|
+
return this.$.node;
|
|
1749
|
+
}
|
|
1750
|
+
$destroy() {
|
|
1751
|
+
this.$.node.remove();
|
|
1752
|
+
this.$.$destroy();
|
|
1753
|
+
super.$destroy();
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
/**
|
|
1757
|
+
* The private part of a base node
|
|
1758
|
+
* @class INodePrivate
|
|
1759
|
+
* @extends FragmentPrivate
|
|
1760
|
+
*/
|
|
1761
|
+
class INodePrivate extends FragmentPrivate {
|
|
1762
|
+
constructor() {
|
|
1763
|
+
super();
|
|
1764
|
+
/**
|
|
1765
|
+
* Defines if node is unmounted
|
|
1766
|
+
* @type {boolean}
|
|
1767
|
+
*/
|
|
1768
|
+
this.unmounted = false;
|
|
1769
|
+
this.$seal();
|
|
1770
|
+
}
|
|
1771
|
+
$destroy() {
|
|
1772
|
+
super.$destroy();
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
/**
|
|
1776
|
+
* Vasille node which can manipulate an element node
|
|
1777
|
+
* @class INode
|
|
1778
|
+
* @extends Fragment
|
|
1779
|
+
*/
|
|
1780
|
+
class INode extends Fragment {
|
|
1781
|
+
/**
|
|
1782
|
+
* Constructs a base node
|
|
1783
|
+
* @param $ {?INodePrivate}
|
|
1784
|
+
*/
|
|
1785
|
+
constructor($) {
|
|
1786
|
+
super($ || new INodePrivate);
|
|
1787
|
+
this.$seal();
|
|
1788
|
+
}
|
|
1789
|
+
/**
|
|
1790
|
+
* Get the bound node
|
|
1791
|
+
*/
|
|
1792
|
+
get node() {
|
|
1793
|
+
return this.$.node;
|
|
1794
|
+
}
|
|
1795
|
+
/**
|
|
1796
|
+
* Initialize node
|
|
1797
|
+
*/
|
|
1798
|
+
$init() {
|
|
1799
|
+
this.$createSignals();
|
|
1800
|
+
this.$createWatchers();
|
|
1801
|
+
this.$createAttrs();
|
|
1802
|
+
this.$createStyle();
|
|
1803
|
+
this.$created();
|
|
1804
|
+
this.$compose();
|
|
1805
|
+
this.$mounted();
|
|
1806
|
+
return this;
|
|
1807
|
+
}
|
|
1808
|
+
/** To be overloaded: attributes creation milestone */
|
|
1809
|
+
$createAttrs() {
|
|
1810
|
+
// empty
|
|
1811
|
+
}
|
|
1812
|
+
/** To be overloaded: $style attributes creation milestone */
|
|
1813
|
+
$createStyle() {
|
|
1814
|
+
// empty
|
|
1815
|
+
}
|
|
1816
|
+
/**
|
|
1817
|
+
* Bind attribute value
|
|
1818
|
+
* @param name {String} name of attribute
|
|
1819
|
+
* @param value {IValue} value
|
|
1820
|
+
*/
|
|
1821
|
+
$attr(name, value) {
|
|
1822
|
+
const $ = this.$;
|
|
1823
|
+
const attr = new AttributeBinding(this, name, value);
|
|
1824
|
+
$.bindings.add(attr);
|
|
1825
|
+
return this;
|
|
1826
|
+
}
|
|
1827
|
+
$bindAttr(name, calculator, v1, v2, v3, v4, v5, v6, v7, v8, v9) {
|
|
1828
|
+
const $ = this.$;
|
|
1829
|
+
const expr = this.$bind(calculator, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
|
1830
|
+
$.bindings.add(new AttributeBinding(this, name, expr));
|
|
1831
|
+
return this;
|
|
1832
|
+
}
|
|
1833
|
+
/**
|
|
1834
|
+
* Set attribute value
|
|
1835
|
+
* @param name {string} name of attribute
|
|
1836
|
+
* @param value {string} value
|
|
1837
|
+
*/
|
|
1838
|
+
$setAttr(name, value) {
|
|
1839
|
+
this.$.app.$run.setAttribute(this.$.node, name, value);
|
|
1840
|
+
return this;
|
|
1841
|
+
}
|
|
1842
|
+
/**
|
|
1843
|
+
* Adds a CSS class
|
|
1844
|
+
* @param cl {string} Class name
|
|
1845
|
+
*/
|
|
1846
|
+
$addClass(cl) {
|
|
1847
|
+
this.$.app.$run.addClass(this.$.node, cl);
|
|
1848
|
+
return this;
|
|
1849
|
+
}
|
|
1850
|
+
/**
|
|
1851
|
+
* Adds some CSS classes
|
|
1852
|
+
* @param cls {...string} classes names
|
|
1853
|
+
*/
|
|
1854
|
+
$addClasses(...cls) {
|
|
1855
|
+
cls.forEach(cl => {
|
|
1856
|
+
this.$.app.$run.addClass(this.$.node, cl);
|
|
1857
|
+
});
|
|
1858
|
+
return this;
|
|
1859
|
+
}
|
|
1860
|
+
/**
|
|
1861
|
+
* Bind a CSS class
|
|
1862
|
+
* @param className {IValue}
|
|
1863
|
+
*/
|
|
1864
|
+
$bindClass(className) {
|
|
1865
|
+
const $ = this.$;
|
|
1866
|
+
$.bindings.add(new ClassBinding(this, "", className));
|
|
1867
|
+
return this;
|
|
1868
|
+
}
|
|
1869
|
+
/**
|
|
1870
|
+
* Bind a floating class name
|
|
1871
|
+
* @param cond {IValue} condition
|
|
1872
|
+
* @param className {string} class name
|
|
1873
|
+
*/
|
|
1874
|
+
$floatingClass(cond, className) {
|
|
1875
|
+
this.$.bindings.add(new ClassBinding(this, className, cond));
|
|
1876
|
+
return this;
|
|
1877
|
+
}
|
|
1878
|
+
/**
|
|
1879
|
+
* Defines a style attribute
|
|
1880
|
+
* @param name {String} name of style attribute
|
|
1881
|
+
* @param value {IValue} value
|
|
1882
|
+
*/
|
|
1883
|
+
$style(name, value) {
|
|
1884
|
+
const $ = this.$;
|
|
1885
|
+
if ($.node instanceof HTMLElement) {
|
|
1886
|
+
$.bindings.add(new StyleBinding(this, name, value));
|
|
1887
|
+
}
|
|
1888
|
+
else {
|
|
1889
|
+
throw userError('style can be applied to HTML elements only', 'non-html-element');
|
|
1890
|
+
}
|
|
1891
|
+
return this;
|
|
1892
|
+
}
|
|
1893
|
+
$bindStyle(name, calculator, v1, v2, v3, v4, v5, v6, v7, v8, v9) {
|
|
1894
|
+
const $ = this.$;
|
|
1895
|
+
const expr = this.$bind(calculator, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
|
1896
|
+
if ($.node instanceof HTMLElement) {
|
|
1897
|
+
$.bindings.add(new StyleBinding(this, name, expr));
|
|
1898
|
+
}
|
|
1899
|
+
else {
|
|
1900
|
+
throw userError('style can be applied to HTML elements only', 'non-html-element');
|
|
1901
|
+
}
|
|
1902
|
+
return this;
|
|
1903
|
+
}
|
|
1904
|
+
/**
|
|
1905
|
+
* Sets a style property value
|
|
1906
|
+
* @param prop {string} Property name
|
|
1907
|
+
* @param value {string} Property value
|
|
1908
|
+
*/
|
|
1909
|
+
$setStyle(prop, value) {
|
|
1910
|
+
if (this.$.node instanceof HTMLElement) {
|
|
1911
|
+
this.$.app.$run.setStyle(this.$.node, prop, value);
|
|
1912
|
+
}
|
|
1913
|
+
else {
|
|
1914
|
+
throw userError("Style can be setted for HTML elements only", "non-html-element");
|
|
1915
|
+
}
|
|
1916
|
+
return this;
|
|
1917
|
+
}
|
|
1918
|
+
/**
|
|
1919
|
+
* Add a listener for an event
|
|
1920
|
+
* @param name {string} Event name
|
|
1921
|
+
* @param handler {function (Event)} Event handler
|
|
1922
|
+
* @param options {Object | boolean} addEventListener options
|
|
1923
|
+
*/
|
|
1924
|
+
$listen(name, handler, options) {
|
|
1925
|
+
this.$.node.addEventListener(name, handler, options);
|
|
1926
|
+
return this;
|
|
1927
|
+
}
|
|
1928
|
+
/**
|
|
1929
|
+
* @param handler {function (MouseEvent)}
|
|
1930
|
+
* @param options {Object | boolean}
|
|
1931
|
+
*/
|
|
1932
|
+
$oncontextmenu(handler, options) {
|
|
1933
|
+
return this.$listen("contextmenu", handler, options);
|
|
1934
|
+
}
|
|
1935
|
+
/**
|
|
1936
|
+
* @param handler {function (MouseEvent)}
|
|
1937
|
+
* @param options {Object | boolean}
|
|
1938
|
+
*/
|
|
1939
|
+
$onmousedown(handler, options) {
|
|
1940
|
+
return this.$listen("mousedown", handler, options);
|
|
1941
|
+
}
|
|
1942
|
+
/**
|
|
1943
|
+
* @param handler {function (MouseEvent)}
|
|
1944
|
+
* @param options {Object | boolean}
|
|
1945
|
+
*/
|
|
1946
|
+
$onmouseenter(handler, options) {
|
|
1947
|
+
return this.$listen("mouseenter", handler, options);
|
|
1948
|
+
}
|
|
1949
|
+
/**
|
|
1950
|
+
* @param handler {function (MouseEvent)}
|
|
1951
|
+
* @param options {Object | boolean}
|
|
1952
|
+
*/
|
|
1953
|
+
$onmouseleave(handler, options) {
|
|
1954
|
+
return this.$listen("mouseleave", handler, options);
|
|
1955
|
+
}
|
|
1956
|
+
/**
|
|
1957
|
+
* @param handler {function (MouseEvent)}
|
|
1958
|
+
* @param options {Object | boolean}
|
|
1959
|
+
*/
|
|
1960
|
+
$onmousemove(handler, options) {
|
|
1961
|
+
return this.$listen("mousemove", handler, options);
|
|
1962
|
+
}
|
|
1963
|
+
/**
|
|
1964
|
+
* @param handler {function (MouseEvent)}
|
|
1965
|
+
* @param options {Object | boolean}
|
|
1966
|
+
*/
|
|
1967
|
+
$onmouseout(handler, options) {
|
|
1968
|
+
return this.$listen("mouseout", handler, options);
|
|
1969
|
+
}
|
|
1970
|
+
/**
|
|
1971
|
+
* @param handler {function (MouseEvent)}
|
|
1972
|
+
* @param options {Object | boolean}
|
|
1973
|
+
*/
|
|
1974
|
+
$onmouseover(handler, options) {
|
|
1975
|
+
return this.$listen("mouseover", handler, options);
|
|
1976
|
+
}
|
|
1977
|
+
/**
|
|
1978
|
+
* @param handler {function (MouseEvent)}
|
|
1979
|
+
* @param options {Object | boolean}
|
|
1980
|
+
*/
|
|
1981
|
+
$onmouseup(handler, options) {
|
|
1982
|
+
return this.$listen("mouseup", handler, options);
|
|
1983
|
+
}
|
|
1984
|
+
/**
|
|
1985
|
+
* @param handler {function (MouseEvent)}
|
|
1986
|
+
* @param options {Object | boolean}
|
|
1987
|
+
*/
|
|
1988
|
+
$onclick(handler, options) {
|
|
1989
|
+
return this.$listen("click", handler, options);
|
|
1990
|
+
}
|
|
1991
|
+
/**
|
|
1992
|
+
* @param handler {function (MouseEvent)}
|
|
1993
|
+
* @param options {Object | boolean}
|
|
1994
|
+
*/
|
|
1995
|
+
$ondblclick(handler, options) {
|
|
1996
|
+
return this.$listen("dblclick", handler, options);
|
|
1997
|
+
}
|
|
1998
|
+
/**
|
|
1999
|
+
* @param handler {function (FocusEvent)}
|
|
2000
|
+
* @param options {Object | boolean}
|
|
2001
|
+
*/
|
|
2002
|
+
$onblur(handler, options) {
|
|
2003
|
+
return this.$listen("blur", handler, options);
|
|
2004
|
+
}
|
|
2005
|
+
/**
|
|
2006
|
+
* @param handler {function (FocusEvent)}
|
|
2007
|
+
* @param options {Object | boolean}
|
|
2008
|
+
*/
|
|
2009
|
+
$onfocus(handler, options) {
|
|
2010
|
+
return this.$listen("focus", handler, options);
|
|
2011
|
+
}
|
|
2012
|
+
/**
|
|
2013
|
+
* @param handler {function (FocusEvent)}
|
|
2014
|
+
* @param options {Object | boolean}
|
|
2015
|
+
*/
|
|
2016
|
+
$onfocusin(handler, options) {
|
|
2017
|
+
return this.$listen("focusin", handler, options);
|
|
2018
|
+
}
|
|
2019
|
+
/**
|
|
2020
|
+
* @param handler {function (FocusEvent)}
|
|
2021
|
+
* @param options {Object | boolean}
|
|
2022
|
+
*/
|
|
2023
|
+
$onfocusout(handler, options) {
|
|
2024
|
+
return this.$listen("focusout", handler, options);
|
|
2025
|
+
}
|
|
2026
|
+
/**
|
|
2027
|
+
* @param handler {function (KeyboardEvent)}
|
|
2028
|
+
* @param options {Object | boolean}
|
|
2029
|
+
*/
|
|
2030
|
+
$onkeydown(handler, options) {
|
|
2031
|
+
return this.$listen("keydown", handler, options);
|
|
2032
|
+
}
|
|
2033
|
+
/**
|
|
2034
|
+
* @param handler {function (KeyboardEvent)}
|
|
2035
|
+
* @param options {Object | boolean}
|
|
2036
|
+
*/
|
|
2037
|
+
$onkeyup(handler, options) {
|
|
2038
|
+
return this.$listen("keyup", handler, options);
|
|
2039
|
+
}
|
|
2040
|
+
/**
|
|
2041
|
+
* @param handler {function (KeyboardEvent)}
|
|
2042
|
+
* @param options {Object | boolean}
|
|
2043
|
+
*/
|
|
2044
|
+
$onkeypress(handler, options) {
|
|
2045
|
+
return this.$listen("keypress", handler, options);
|
|
2046
|
+
}
|
|
2047
|
+
/**
|
|
2048
|
+
* @param handler {function (TouchEvent)}
|
|
2049
|
+
* @param options {Object | boolean}
|
|
2050
|
+
*/
|
|
2051
|
+
$ontouchstart(handler, options) {
|
|
2052
|
+
return this.$listen("touchstart", handler, options);
|
|
2053
|
+
}
|
|
2054
|
+
/**
|
|
2055
|
+
* @param handler {function (TouchEvent)}
|
|
2056
|
+
* @param options {Object | boolean}
|
|
2057
|
+
*/
|
|
2058
|
+
$ontouchmove(handler, options) {
|
|
2059
|
+
return this.$listen("touchmove", handler, options);
|
|
2060
|
+
}
|
|
2061
|
+
/**
|
|
2062
|
+
* @param handler {function (TouchEvent)}
|
|
2063
|
+
* @param options {Object | boolean}
|
|
2064
|
+
*/
|
|
2065
|
+
$ontouchend(handler, options) {
|
|
2066
|
+
return this.$listen("touchend", handler, options);
|
|
2067
|
+
}
|
|
2068
|
+
/**
|
|
2069
|
+
* @param handler {function (TouchEvent)}
|
|
2070
|
+
* @param options {Object | boolean}
|
|
2071
|
+
*/
|
|
2072
|
+
$ontouchcancel(handler, options) {
|
|
2073
|
+
return this.$listen("touchcancel", handler, options);
|
|
2074
|
+
}
|
|
2075
|
+
/**
|
|
2076
|
+
* @param handler {function (WheelEvent)}
|
|
2077
|
+
* @param options {Object | boolean}
|
|
2078
|
+
*/
|
|
2079
|
+
$onwheel(handler, options) {
|
|
2080
|
+
return this.$listen("wheel", handler, options);
|
|
2081
|
+
}
|
|
2082
|
+
/**
|
|
2083
|
+
* @param handler {function (ProgressEvent)}
|
|
2084
|
+
* @param options {Object | boolean}
|
|
2085
|
+
*/
|
|
2086
|
+
$onabort(handler, options) {
|
|
2087
|
+
return this.$listen("abort", handler, options);
|
|
2088
|
+
}
|
|
2089
|
+
/**
|
|
2090
|
+
* @param handler {function (ProgressEvent)}
|
|
2091
|
+
* @param options {Object | boolean}
|
|
2092
|
+
*/
|
|
2093
|
+
$onerror(handler, options) {
|
|
2094
|
+
return this.$listen("error", handler, options);
|
|
2095
|
+
}
|
|
2096
|
+
/**
|
|
2097
|
+
* @param handler {function (ProgressEvent)}
|
|
2098
|
+
* @param options {Object | boolean}
|
|
2099
|
+
*/
|
|
2100
|
+
$onload(handler, options) {
|
|
2101
|
+
return this.$listen("load", handler, options);
|
|
2102
|
+
}
|
|
2103
|
+
/**
|
|
2104
|
+
* @param handler {function (ProgressEvent)}
|
|
2105
|
+
* @param options {Object | boolean}
|
|
2106
|
+
*/
|
|
2107
|
+
$onloadend(handler, options) {
|
|
2108
|
+
return this.$listen("loadend", handler, options);
|
|
2109
|
+
}
|
|
2110
|
+
/**
|
|
2111
|
+
* @param handler {function (ProgressEvent)}
|
|
2112
|
+
* @param options {Object | boolean}
|
|
2113
|
+
*/
|
|
2114
|
+
$onloadstart(handler, options) {
|
|
2115
|
+
return this.$listen("loadstart", handler, options);
|
|
2116
|
+
}
|
|
2117
|
+
/**
|
|
2118
|
+
* @param handler {function (ProgressEvent)}
|
|
2119
|
+
* @param options {Object | boolean}
|
|
2120
|
+
*/
|
|
2121
|
+
$onprogress(handler, options) {
|
|
2122
|
+
return this.$listen("progress", handler, options);
|
|
2123
|
+
}
|
|
2124
|
+
/**
|
|
2125
|
+
* @param handler {function (ProgressEvent)}
|
|
2126
|
+
* @param options {Object | boolean}
|
|
2127
|
+
*/
|
|
2128
|
+
$ontimeout(handler, options) {
|
|
2129
|
+
return this.$listen("timeout", handler, options);
|
|
2130
|
+
}
|
|
2131
|
+
/**
|
|
2132
|
+
* @param handler {function (DragEvent)}
|
|
2133
|
+
* @param options {Object | boolean}
|
|
2134
|
+
*/
|
|
2135
|
+
$ondrag(handler, options) {
|
|
2136
|
+
return this.$listen("drag", handler, options);
|
|
2137
|
+
}
|
|
2138
|
+
/**
|
|
2139
|
+
* @param handler {function (DragEvent)}
|
|
2140
|
+
* @param options {Object | boolean}
|
|
2141
|
+
*/
|
|
2142
|
+
$ondragend(handler, options) {
|
|
2143
|
+
return this.$listen("dragend", handler, options);
|
|
2144
|
+
}
|
|
2145
|
+
/**
|
|
2146
|
+
* @param handler {function (DragEvent)}
|
|
2147
|
+
* @param options {Object | boolean}
|
|
2148
|
+
*/
|
|
2149
|
+
$ondragenter(handler, options) {
|
|
2150
|
+
return this.$listen("dragenter", handler, options);
|
|
2151
|
+
}
|
|
2152
|
+
/**
|
|
2153
|
+
* @param handler {function (DragEvent)}
|
|
2154
|
+
* @param options {Object | boolean}
|
|
2155
|
+
*/
|
|
2156
|
+
$ondragexit(handler, options) {
|
|
2157
|
+
return this.$listen("dragexit", handler, options);
|
|
2158
|
+
}
|
|
2159
|
+
/**
|
|
2160
|
+
* @param handler {function (DragEvent)}
|
|
2161
|
+
* @param options {Object | boolean}
|
|
2162
|
+
*/
|
|
2163
|
+
$ondragleave(handler, options) {
|
|
2164
|
+
return this.$listen("dragleave", handler, options);
|
|
2165
|
+
}
|
|
2166
|
+
/**
|
|
2167
|
+
* @param handler {function (DragEvent)}
|
|
2168
|
+
* @param options {Object | boolean}
|
|
2169
|
+
*/
|
|
2170
|
+
$ondragover(handler, options) {
|
|
2171
|
+
return this.$listen("dragover", handler, options);
|
|
2172
|
+
}
|
|
2173
|
+
/**
|
|
2174
|
+
* @param handler {function (DragEvent)}
|
|
2175
|
+
* @param options {Object | boolean}
|
|
2176
|
+
*/
|
|
2177
|
+
$ondragstart(handler, options) {
|
|
2178
|
+
return this.$listen("dragstart", handler, options);
|
|
2179
|
+
}
|
|
2180
|
+
/**
|
|
2181
|
+
* @param handler {function (DragEvent)}
|
|
2182
|
+
* @param options {Object | boolean}
|
|
2183
|
+
*/
|
|
2184
|
+
$ondrop(handler, options) {
|
|
2185
|
+
return this.$listen("drop", handler, options);
|
|
2186
|
+
}
|
|
2187
|
+
/**
|
|
2188
|
+
* @param handler {function (PointerEvent)}
|
|
2189
|
+
* @param options {Object | boolean}
|
|
2190
|
+
*/
|
|
2191
|
+
$onpointerover(handler, options) {
|
|
2192
|
+
return this.$listen("pointerover", handler, options);
|
|
2193
|
+
}
|
|
2194
|
+
/**
|
|
2195
|
+
* @param handler {function (PointerEvent)}
|
|
2196
|
+
* @param options {Object | boolean}
|
|
2197
|
+
*/
|
|
2198
|
+
$onpointerenter(handler, options) {
|
|
2199
|
+
return this.$listen("pointerenter", handler, options);
|
|
2200
|
+
}
|
|
2201
|
+
/**
|
|
2202
|
+
* @param handler {function (PointerEvent)}
|
|
2203
|
+
* @param options {Object | boolean}
|
|
2204
|
+
*/
|
|
2205
|
+
$onpointerdown(handler, options) {
|
|
2206
|
+
return this.$listen("pointerdown", handler, options);
|
|
2207
|
+
}
|
|
2208
|
+
/**
|
|
2209
|
+
* @param handler {function (PointerEvent)}
|
|
2210
|
+
* @param options {Object | boolean}
|
|
2211
|
+
*/
|
|
2212
|
+
$onpointermove(handler, options) {
|
|
2213
|
+
return this.$listen("pointermove", handler, options);
|
|
2214
|
+
}
|
|
2215
|
+
/**
|
|
2216
|
+
* @param handler {function (PointerEvent)}
|
|
2217
|
+
* @param options {Object | boolean}
|
|
2218
|
+
*/
|
|
2219
|
+
$onpointerup(handler, options) {
|
|
2220
|
+
return this.$listen("pointerup", handler, options);
|
|
2221
|
+
}
|
|
2222
|
+
/**
|
|
2223
|
+
* @param handler {function (PointerEvent)}
|
|
2224
|
+
* @param options {Object | boolean}
|
|
2225
|
+
*/
|
|
2226
|
+
$onpointercancel(handler, options) {
|
|
2227
|
+
return this.$listen("pointercancel", handler, options);
|
|
2228
|
+
}
|
|
2229
|
+
/**
|
|
2230
|
+
* @param handler {function (PointerEvent)}
|
|
2231
|
+
* @param options {Object | boolean}
|
|
2232
|
+
*/
|
|
2233
|
+
$onpointerout(handler, options) {
|
|
2234
|
+
return this.$listen("pointerout", handler, options);
|
|
2235
|
+
}
|
|
2236
|
+
/**
|
|
2237
|
+
* @param handler {function (PointerEvent)}
|
|
2238
|
+
* @param options {Object | boolean}
|
|
2239
|
+
*/
|
|
2240
|
+
$onpointerleave(handler, options) {
|
|
2241
|
+
return this.$listen("pointerleave", handler, options);
|
|
2242
|
+
}
|
|
2243
|
+
/**
|
|
2244
|
+
* @param handler {function (PointerEvent)}
|
|
2245
|
+
* @param options {Object | boolean}
|
|
2246
|
+
*/
|
|
2247
|
+
$ongotpointercapture(handler, options) {
|
|
2248
|
+
return this.$listen("gotpointercapture", handler, options);
|
|
2249
|
+
}
|
|
2250
|
+
/**
|
|
2251
|
+
* @param handler {function (PointerEvent)}
|
|
2252
|
+
* @param options {Object | boolean}
|
|
2253
|
+
*/
|
|
2254
|
+
$onlostpointercapture(handler, options) {
|
|
2255
|
+
return this.$listen("lostpointercapture", handler, options);
|
|
2256
|
+
}
|
|
2257
|
+
/**
|
|
2258
|
+
* @param handler {function (AnimationEvent)}
|
|
2259
|
+
* @param options {Object | boolean}
|
|
2260
|
+
*/
|
|
2261
|
+
$onanimationstart(handler, options) {
|
|
2262
|
+
return this.$listen("animationstart", handler, options);
|
|
2263
|
+
}
|
|
2264
|
+
/**
|
|
2265
|
+
* @param handler {function (AnimationEvent)}
|
|
2266
|
+
* @param options {Object | boolean}
|
|
2267
|
+
*/
|
|
2268
|
+
$onanimationend(handler, options) {
|
|
2269
|
+
return this.$listen("animationend", handler, options);
|
|
2270
|
+
}
|
|
2271
|
+
/**
|
|
2272
|
+
* @param handler {function (AnimationEvent)}
|
|
2273
|
+
* @param options {Object | boolean}
|
|
2274
|
+
*/
|
|
2275
|
+
$onanimationiteraton(handler, options) {
|
|
2276
|
+
return this.$listen("animationiteration", handler, options);
|
|
2277
|
+
}
|
|
2278
|
+
/**
|
|
2279
|
+
* @param handler {function (ClipboardEvent)}
|
|
2280
|
+
* @param options {Object | boolean}
|
|
2281
|
+
*/
|
|
2282
|
+
$onclipboardchange(handler, options) {
|
|
2283
|
+
return this.$listen("clipboardchange", handler, options);
|
|
2284
|
+
}
|
|
2285
|
+
/**
|
|
2286
|
+
* @param handler {function (ClipboardEvent)}
|
|
2287
|
+
* @param options {Object | boolean}
|
|
2288
|
+
*/
|
|
2289
|
+
$oncut(handler, options) {
|
|
2290
|
+
return this.$listen("cut", handler, options);
|
|
2291
|
+
}
|
|
2292
|
+
/**
|
|
2293
|
+
* @param handler {function (ClipboardEvent)}
|
|
2294
|
+
* @param options {Object | boolean}
|
|
2295
|
+
*/
|
|
2296
|
+
$oncopy(handler, options) {
|
|
2297
|
+
return this.$listen("copy", handler, options);
|
|
2298
|
+
}
|
|
2299
|
+
/**
|
|
2300
|
+
* @param handler {function (ClipboardEvent)}
|
|
2301
|
+
* @param options {Object | boolean}
|
|
2302
|
+
*/
|
|
2303
|
+
$onpaste(handler, options) {
|
|
2304
|
+
return this.$listen("paste", handler, options);
|
|
2305
|
+
}
|
|
2306
|
+
$$insertAdjacent(node) {
|
|
2307
|
+
const $ = this.$;
|
|
2308
|
+
$.app.$run.insertBefore($.node, node);
|
|
2309
|
+
}
|
|
2310
|
+
/**
|
|
2311
|
+
* A v-show & ngShow alternative
|
|
2312
|
+
* @param cond {IValue} show condition
|
|
2313
|
+
*/
|
|
2314
|
+
$bindShow(cond) {
|
|
2315
|
+
const $ = this.$;
|
|
2316
|
+
const node = $.node;
|
|
2317
|
+
if (node instanceof HTMLElement) {
|
|
2318
|
+
let lastDisplay = node.style.display;
|
|
2319
|
+
const htmlNode = node;
|
|
2320
|
+
return this.$bindAlive(cond, () => {
|
|
2321
|
+
lastDisplay = htmlNode.style.display;
|
|
2322
|
+
htmlNode.style.display = 'none';
|
|
2323
|
+
}, () => {
|
|
2324
|
+
htmlNode.style.display = lastDisplay;
|
|
2325
|
+
});
|
|
2326
|
+
}
|
|
2327
|
+
else {
|
|
2328
|
+
throw userError('the element must be a html element', 'bind-show');
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
/**
|
|
2332
|
+
* bind HTML
|
|
2333
|
+
* @param value {IValue}
|
|
2334
|
+
*/
|
|
2335
|
+
$html(value) {
|
|
2336
|
+
const $ = this.$;
|
|
2337
|
+
const node = $.node;
|
|
2338
|
+
if (node instanceof HTMLElement) {
|
|
2339
|
+
node.innerHTML = value.$;
|
|
2340
|
+
this.$watch((v) => {
|
|
2341
|
+
node.innerHTML = v;
|
|
2342
|
+
}, value);
|
|
2343
|
+
}
|
|
2344
|
+
else {
|
|
2345
|
+
throw userError("HTML can be bound for HTML nodes only", "dom-error");
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
/**
|
|
2350
|
+
* Represents an Vasille.js HTML element node
|
|
2351
|
+
* @class Tag
|
|
2352
|
+
* @extends INode
|
|
2353
|
+
*/
|
|
2354
|
+
class Tag extends INode {
|
|
2355
|
+
constructor() {
|
|
2356
|
+
super();
|
|
2357
|
+
this.$seal();
|
|
2358
|
+
}
|
|
2359
|
+
$preinit(app, parent, tagName) {
|
|
2360
|
+
if (!tagName || typeof tagName !== "string") {
|
|
2361
|
+
throw internalError('wrong Tag::$preinit call');
|
|
2362
|
+
}
|
|
2363
|
+
const node = document.createElement(tagName);
|
|
2364
|
+
const $ = this.$;
|
|
2365
|
+
$.preinit(app, parent);
|
|
2366
|
+
$.node = node;
|
|
2367
|
+
$.parent.$$appendNode(node);
|
|
2368
|
+
}
|
|
2369
|
+
$$findFirstChild() {
|
|
2370
|
+
return this.$.unmounted ? null : this.$.node;
|
|
2371
|
+
}
|
|
2372
|
+
$$insertAdjacent(node) {
|
|
2373
|
+
if (this.$.unmounted) {
|
|
2374
|
+
if (this.$.next) {
|
|
2375
|
+
this.$.next.$$insertAdjacent(node);
|
|
2376
|
+
}
|
|
2377
|
+
else {
|
|
2378
|
+
this.$.parent.$$appendNode(node);
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
else {
|
|
2382
|
+
super.$$insertAdjacent(node);
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
$$appendNode(node) {
|
|
2386
|
+
const $ = this.$;
|
|
2387
|
+
$.app.$run.appendChild($.node, node);
|
|
2388
|
+
}
|
|
2389
|
+
/**
|
|
2390
|
+
* Mount/Unmount a node
|
|
2391
|
+
* @param cond {IValue} show condition
|
|
2392
|
+
*/
|
|
2393
|
+
$bindMount(cond) {
|
|
2394
|
+
const $ = this.$;
|
|
2395
|
+
return this.$bindAlive(cond, () => {
|
|
2396
|
+
$.node.remove();
|
|
2397
|
+
$.unmounted = true;
|
|
2398
|
+
}, () => {
|
|
2399
|
+
if (!$.unmounted)
|
|
2400
|
+
return;
|
|
2401
|
+
if ($.next) {
|
|
2402
|
+
$.next.$$insertAdjacent($.node);
|
|
2403
|
+
}
|
|
2404
|
+
else {
|
|
2405
|
+
$.parent.$$appendNode($.node);
|
|
2406
|
+
}
|
|
2407
|
+
$.unmounted = false;
|
|
2408
|
+
});
|
|
2409
|
+
}
|
|
2410
|
+
/**
|
|
2411
|
+
* Runs GC
|
|
2412
|
+
*/
|
|
2413
|
+
$destroy() {
|
|
2414
|
+
this.node.remove();
|
|
2415
|
+
super.$destroy();
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
/**
|
|
2419
|
+
* Represents a vasille extension node
|
|
2420
|
+
* @class Extension
|
|
2421
|
+
* @extends INode
|
|
2422
|
+
*/
|
|
2423
|
+
class Extension extends INode {
|
|
2424
|
+
$preinit(app, parent) {
|
|
2425
|
+
if (parent instanceof INode) {
|
|
2426
|
+
const $ = this.$;
|
|
2427
|
+
$.preinit(app, parent);
|
|
2428
|
+
$.node = parent.node;
|
|
2429
|
+
}
|
|
2430
|
+
else {
|
|
2431
|
+
throw internalError("A extension node can be encapsulated only in a tag/extension/component");
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
constructor($) {
|
|
2435
|
+
super($);
|
|
2436
|
+
this.$seal();
|
|
2437
|
+
}
|
|
2438
|
+
$destroy() {
|
|
2439
|
+
super.$destroy();
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
/**
|
|
2443
|
+
* Node which cas has just a child
|
|
2444
|
+
* @class Component
|
|
2445
|
+
* @extends Extension
|
|
2446
|
+
*/
|
|
2447
|
+
class Component extends Extension {
|
|
2448
|
+
constructor() {
|
|
2449
|
+
super();
|
|
2450
|
+
this.$seal();
|
|
2451
|
+
}
|
|
2452
|
+
$mounted() {
|
|
2453
|
+
super.$mounted();
|
|
2454
|
+
if (this.$children.length !== 1) {
|
|
2455
|
+
throw userError("UserNode must have a child only", "dom-error");
|
|
2456
|
+
}
|
|
2457
|
+
const child = this.$children[0];
|
|
2458
|
+
if (child instanceof Tag || child instanceof Component) {
|
|
2459
|
+
const $ = this.$;
|
|
2460
|
+
$.node = child.node;
|
|
2461
|
+
}
|
|
2462
|
+
else {
|
|
2463
|
+
throw userError("UserNode child must be Tag or Component", "dom-error");
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
/**
|
|
2468
|
+
* Private part of switch node
|
|
2469
|
+
* @class SwitchedNodePrivate
|
|
2470
|
+
* @extends INodePrivate
|
|
2471
|
+
*/
|
|
2472
|
+
class SwitchedNodePrivate extends INodePrivate {
|
|
2473
|
+
constructor() {
|
|
2474
|
+
super();
|
|
2475
|
+
this.$seal();
|
|
2476
|
+
}
|
|
2477
|
+
/**
|
|
2478
|
+
* Runs GC
|
|
2479
|
+
*/
|
|
2480
|
+
$destroy() {
|
|
2481
|
+
this.cases.forEach(c => {
|
|
2482
|
+
delete c.cond;
|
|
2483
|
+
delete c.cb;
|
|
2484
|
+
});
|
|
2485
|
+
this.cases.splice(0);
|
|
2486
|
+
super.$destroy();
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2489
|
+
/**
|
|
2490
|
+
* Defines a node witch can switch its children conditionally
|
|
2491
|
+
*/
|
|
2492
|
+
class SwitchedNode extends Fragment {
|
|
2493
|
+
/**
|
|
2494
|
+
* Constructs a switch node and define a sync function
|
|
2495
|
+
*/
|
|
2496
|
+
constructor() {
|
|
2497
|
+
super(new SwitchedNodePrivate);
|
|
2498
|
+
this.$.sync = () => {
|
|
2499
|
+
const $ = this.$;
|
|
2500
|
+
let i = 0;
|
|
2501
|
+
for (; i < $.cases.length; i++) {
|
|
2502
|
+
if ($.cases[i].cond.$) {
|
|
2503
|
+
break;
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
if (i === $.index) {
|
|
2507
|
+
return;
|
|
2508
|
+
}
|
|
2509
|
+
if ($.fragment) {
|
|
2510
|
+
$.fragment.$destroy();
|
|
2511
|
+
this.$children.splice(0);
|
|
2512
|
+
$.fragment = null;
|
|
2513
|
+
}
|
|
2514
|
+
if (i !== $.cases.length) {
|
|
2515
|
+
$.index = i;
|
|
2516
|
+
this.createChild($.cases[i].cb);
|
|
2517
|
+
}
|
|
2518
|
+
else {
|
|
2519
|
+
$.index = -1;
|
|
2520
|
+
}
|
|
2521
|
+
};
|
|
2522
|
+
this.$seal();
|
|
2523
|
+
}
|
|
2524
|
+
/**
|
|
2525
|
+
* Set up switch cases
|
|
2526
|
+
* @param cases {{ cond : IValue, cb : function(Fragment) }}
|
|
2527
|
+
*/
|
|
2528
|
+
setCases(cases) {
|
|
2529
|
+
const $ = this.$;
|
|
2530
|
+
$.cases = [...cases];
|
|
2531
|
+
}
|
|
2532
|
+
/**
|
|
2533
|
+
* Creates a child node
|
|
2534
|
+
* @param cb {function(Fragment)} Call-back
|
|
2535
|
+
*/
|
|
2536
|
+
createChild(cb) {
|
|
2537
|
+
const node = new Fragment();
|
|
2538
|
+
node.$preinit(this.$.app, this);
|
|
2539
|
+
node.$init();
|
|
2540
|
+
node.$ready();
|
|
2541
|
+
this.$.fragment = node;
|
|
2542
|
+
this.$children.push(node);
|
|
2543
|
+
cb(node);
|
|
2544
|
+
}
|
|
2545
|
+
$ready() {
|
|
2546
|
+
const $ = this.$;
|
|
2547
|
+
super.$ready();
|
|
2548
|
+
$.cases.forEach(c => {
|
|
2549
|
+
c.cond.on($.sync);
|
|
2550
|
+
});
|
|
2551
|
+
$.sync();
|
|
2552
|
+
}
|
|
2553
|
+
$destroy() {
|
|
2554
|
+
const $ = this.$;
|
|
2555
|
+
$.cases.forEach(c => {
|
|
2556
|
+
c.cond.off($.sync);
|
|
2557
|
+
});
|
|
2558
|
+
super.$destroy();
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
/**
|
|
2562
|
+
* The private part of a text node
|
|
2563
|
+
*/
|
|
2564
|
+
class DebugPrivate extends FragmentPrivate {
|
|
2565
|
+
constructor() {
|
|
2566
|
+
super();
|
|
2567
|
+
this.$seal();
|
|
2568
|
+
}
|
|
2569
|
+
/**
|
|
2570
|
+
* Pre-initializes a text node
|
|
2571
|
+
* @param app {App} the app node
|
|
2572
|
+
* @param parent {Fragment} parent node
|
|
2573
|
+
* @param text {String | IValue}
|
|
2574
|
+
*/
|
|
2575
|
+
preinitComment(app, parent, text) {
|
|
2576
|
+
super.preinit(app, parent);
|
|
2577
|
+
this.node = document.createComment(text.$);
|
|
2578
|
+
this.bindings.add(new Expression((v) => {
|
|
2579
|
+
this.node.replaceData(0, -1, v);
|
|
2580
|
+
}, true, text));
|
|
2581
|
+
this.parent.$$appendNode(this.node);
|
|
2582
|
+
}
|
|
2583
|
+
/**
|
|
2584
|
+
* Clear node data
|
|
2585
|
+
*/
|
|
2586
|
+
$destroy() {
|
|
2587
|
+
this.node.remove();
|
|
2588
|
+
super.$destroy();
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
/**
|
|
2592
|
+
* Represents a debug node
|
|
2593
|
+
* @class DebugNode
|
|
2594
|
+
* @extends Fragment
|
|
2595
|
+
*/
|
|
2596
|
+
class DebugNode extends Fragment {
|
|
2597
|
+
constructor() {
|
|
2598
|
+
super();
|
|
2599
|
+
/**
|
|
2600
|
+
* private data
|
|
2601
|
+
* @type {DebugNode}
|
|
2602
|
+
*/
|
|
2603
|
+
this.$ = new DebugPrivate();
|
|
2604
|
+
this.$seal();
|
|
2605
|
+
}
|
|
2606
|
+
$preinit(app, parent, text) {
|
|
2607
|
+
const $ = this.$;
|
|
2608
|
+
if (!text) {
|
|
2609
|
+
throw internalError('wrong DebugNode::$preninit call');
|
|
2610
|
+
}
|
|
2611
|
+
$.preinitComment(app, parent, text);
|
|
2612
|
+
}
|
|
2613
|
+
/**
|
|
2614
|
+
* Runs garbage collector
|
|
2615
|
+
*/
|
|
2616
|
+
$destroy() {
|
|
2617
|
+
this.$.$destroy();
|
|
2618
|
+
super.$destroy();
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
|
|
2622
|
+
window.FragmentPrivate = FragmentPrivate;
|
|
2623
|
+
window.Fragment = Fragment;
|
|
2624
|
+
window.TextNodePrivate = TextNodePrivate;
|
|
2625
|
+
window.TextNode = TextNode;
|
|
2626
|
+
window.INodePrivate = INodePrivate;
|
|
2627
|
+
window.INode = INode;
|
|
2628
|
+
window.Tag = Tag;
|
|
2629
|
+
window.Extension = Extension;
|
|
2630
|
+
window.Component = Component;
|
|
2631
|
+
window.SwitchedNodePrivate = SwitchedNodePrivate;
|
|
2632
|
+
window.DebugPrivate = DebugPrivate;
|
|
2633
|
+
window.DebugNode = DebugNode;
|
|
2634
|
+
|
|
2635
|
+
// ./lib/node/app.js
|
|
2636
|
+
/**
|
|
2637
|
+
* Application Node
|
|
2638
|
+
* @class AppNode
|
|
2639
|
+
* @extends INode
|
|
2640
|
+
*/
|
|
2641
|
+
class AppNode extends INode {
|
|
2642
|
+
/**
|
|
2643
|
+
* @param options {Object} Application options
|
|
2644
|
+
*/
|
|
2645
|
+
constructor(options) {
|
|
2646
|
+
super();
|
|
2647
|
+
this.$run = (options === null || options === void 0 ? void 0 : options.executor) || ((options === null || options === void 0 ? void 0 : options.freezeUi) === false ? timeoutExecutor : instantExecutor);
|
|
2648
|
+
this.$debugUi = (options === null || options === void 0 ? void 0 : options.debugUi) || false;
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2651
|
+
/**
|
|
2652
|
+
* Represents a Vasille.js application
|
|
2653
|
+
* @class App
|
|
2654
|
+
* @extends AppNode
|
|
2655
|
+
*/
|
|
2656
|
+
class App extends AppNode {
|
|
2657
|
+
/**
|
|
2658
|
+
* Constructs an app node
|
|
2659
|
+
* @param node {Element} The root of application
|
|
2660
|
+
* @param options {Object} Application options
|
|
2661
|
+
*/
|
|
2662
|
+
constructor(node, options) {
|
|
2663
|
+
super(options);
|
|
2664
|
+
this.$.node = node;
|
|
2665
|
+
this.$preinit(this, this);
|
|
2666
|
+
this.$seal();
|
|
2667
|
+
}
|
|
2668
|
+
$$appendNode(node) {
|
|
2669
|
+
const $ = this.$;
|
|
2670
|
+
$.app.$run.appendChild($.node, node);
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
|
|
2674
|
+
window.AppNode = AppNode;
|
|
2675
|
+
window.App = App;
|
|
2676
|
+
|
|
2677
|
+
// ./lib/node/interceptor.js
|
|
2678
|
+
/**
|
|
2679
|
+
* Interceptor is designed to connect signals & methods of children elements
|
|
2680
|
+
* @class Interceptor
|
|
2681
|
+
* @extends Destroyable
|
|
2682
|
+
*/
|
|
2683
|
+
class Interceptor extends Destroyable {
|
|
2684
|
+
constructor() {
|
|
2685
|
+
super(...arguments);
|
|
2686
|
+
/**
|
|
2687
|
+
* Set of signals
|
|
2688
|
+
* @type Set
|
|
2689
|
+
*/
|
|
2690
|
+
this.signals = new Set;
|
|
2691
|
+
/**
|
|
2692
|
+
* Set of handlers
|
|
2693
|
+
* @type Set
|
|
2694
|
+
*/
|
|
2695
|
+
this.handlers = new Set;
|
|
2696
|
+
}
|
|
2697
|
+
/**
|
|
2698
|
+
* Connect a signal or a handler
|
|
2699
|
+
* @param thing {Signal | function}
|
|
2700
|
+
*/
|
|
2701
|
+
connect(thing) {
|
|
2702
|
+
// interceptor will connect signals and handlers together
|
|
2703
|
+
if (thing instanceof Signal) {
|
|
2704
|
+
this.handlers.forEach(handler => {
|
|
2705
|
+
thing.subscribe(handler);
|
|
2706
|
+
});
|
|
2707
|
+
this.signals.add(thing);
|
|
2708
|
+
}
|
|
2709
|
+
else {
|
|
2710
|
+
this.signals.forEach(signal => {
|
|
2711
|
+
signal.subscribe(thing);
|
|
2712
|
+
});
|
|
2713
|
+
this.handlers.add(thing);
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
/**
|
|
2717
|
+
* Disconnect a handler from signals
|
|
2718
|
+
* @param handler {function}
|
|
2719
|
+
*/
|
|
2720
|
+
disconnect(handler) {
|
|
2721
|
+
this.signals.forEach(signal => {
|
|
2722
|
+
signal.unsubscribe(handler);
|
|
2723
|
+
});
|
|
2724
|
+
}
|
|
2725
|
+
$destroy() {
|
|
2726
|
+
super.$destroy();
|
|
2727
|
+
this.signals.forEach(signal => {
|
|
2728
|
+
this.handlers.forEach(handler => {
|
|
2729
|
+
signal.unsubscribe(handler);
|
|
2730
|
+
});
|
|
2731
|
+
});
|
|
2732
|
+
}
|
|
2733
|
+
}
|
|
2734
|
+
/**
|
|
2735
|
+
* Interceptor node to implement directly to vasille DOM
|
|
2736
|
+
* @class InterceptorNode
|
|
2737
|
+
* @extends Extension
|
|
2738
|
+
*/
|
|
2739
|
+
class InterceptorNode extends Fragment {
|
|
2740
|
+
constructor() {
|
|
2741
|
+
super(...arguments);
|
|
2742
|
+
/**
|
|
2743
|
+
* Internal interceptor
|
|
2744
|
+
* @type Interceptor
|
|
2745
|
+
*/
|
|
2746
|
+
this.interceptor = new Interceptor;
|
|
2747
|
+
/**
|
|
2748
|
+
* The default slot of node
|
|
2749
|
+
* @type Slot
|
|
2750
|
+
*/
|
|
2751
|
+
this.slot = new Slot;
|
|
2752
|
+
}
|
|
2753
|
+
$compose() {
|
|
2754
|
+
this.slot.release(this, this.interceptor);
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
|
|
2758
|
+
window.Interceptor = Interceptor;
|
|
2759
|
+
window.InterceptorNode = InterceptorNode;
|
|
2760
|
+
|
|
2761
|
+
// ./lib/binding/attribute.js
|
|
2762
|
+
/**
|
|
2763
|
+
* Represents an Attribute binding description
|
|
2764
|
+
* @class AttributeBinding
|
|
2765
|
+
* @extends Binding
|
|
2766
|
+
*/
|
|
2767
|
+
class AttributeBinding extends Binding {
|
|
2768
|
+
/**
|
|
2769
|
+
* Constructs an attribute binding description
|
|
2770
|
+
* @param node {INode} the vasille node
|
|
2771
|
+
* @param name {String} the name of attribute
|
|
2772
|
+
* @param value {IValue} value to bind
|
|
2773
|
+
*/
|
|
2774
|
+
constructor(node, name, value) {
|
|
2775
|
+
super(node, name, value);
|
|
2776
|
+
}
|
|
2777
|
+
/**
|
|
2778
|
+
* Generates a function which updates the attribute value
|
|
2779
|
+
* @param name {String} The name of attribute
|
|
2780
|
+
* @returns {Function} a function which will update attribute value
|
|
2781
|
+
*/
|
|
2782
|
+
bound(name) {
|
|
2783
|
+
return function (node, value) {
|
|
2784
|
+
if (value) {
|
|
2785
|
+
node.app.$run.setAttribute(node.node, name, value);
|
|
2786
|
+
}
|
|
2787
|
+
else {
|
|
2788
|
+
node.app.$run.removeAttribute(node.node, name);
|
|
2789
|
+
}
|
|
2790
|
+
};
|
|
2791
|
+
}
|
|
2792
|
+
}
|
|
2793
|
+
|
|
2794
|
+
window.AttributeBinding = AttributeBinding;
|
|
2795
|
+
|
|
2796
|
+
// ./lib/binding/style.js
|
|
2797
|
+
/**
|
|
2798
|
+
* Describes a style attribute binding
|
|
2799
|
+
* @class StyleBinding
|
|
2800
|
+
* @extends Binding
|
|
2801
|
+
*/
|
|
2802
|
+
class StyleBinding extends Binding {
|
|
2803
|
+
/**
|
|
2804
|
+
* Constructs a style binding attribute
|
|
2805
|
+
* @param node {INode} the vasille node
|
|
2806
|
+
* @param name {string} the name of style property
|
|
2807
|
+
* @param value {IValue} the value to bind
|
|
2808
|
+
*/
|
|
2809
|
+
constructor(node, name, value) {
|
|
2810
|
+
super(node, name, value);
|
|
2811
|
+
}
|
|
2812
|
+
/**
|
|
2813
|
+
* Generates a function to update style property value
|
|
2814
|
+
* @param name {string}
|
|
2815
|
+
* @returns {Function} a function to update style property
|
|
2816
|
+
*/
|
|
2817
|
+
bound(name) {
|
|
2818
|
+
return function (node, value) {
|
|
2819
|
+
if (node.node instanceof HTMLElement) {
|
|
2820
|
+
node.app.$run.setStyle(node.node, name, value);
|
|
2821
|
+
}
|
|
2822
|
+
};
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
|
|
2826
|
+
window.StyleBinding = StyleBinding;
|
|
2827
|
+
|
|
2828
|
+
// ./lib/binding/class.js
|
|
2829
|
+
/**
|
|
2830
|
+
* Represents a HTML class binding description
|
|
2831
|
+
* @class ClassBinding
|
|
2832
|
+
* @extends Binding
|
|
2833
|
+
*/
|
|
2834
|
+
class ClassBinding extends Binding {
|
|
2835
|
+
/**
|
|
2836
|
+
* Constructs an HTML class binding description
|
|
2837
|
+
* @param node {INode} the vasille node
|
|
2838
|
+
* @param name {String} the name of class
|
|
2839
|
+
* @param value {IValue} the value to bind
|
|
2840
|
+
*/
|
|
2841
|
+
constructor(node, name, value) {
|
|
2842
|
+
super(node, name, value);
|
|
2843
|
+
this.$seal();
|
|
2844
|
+
}
|
|
2845
|
+
/**
|
|
2846
|
+
* Generates a function which updates the html class value
|
|
2847
|
+
* @param name {String} The name of attribute
|
|
2848
|
+
* @returns {Function} a function which will update attribute value
|
|
2849
|
+
*/
|
|
2850
|
+
bound(name) {
|
|
2851
|
+
let current = null;
|
|
2852
|
+
function addClass(node, cl) {
|
|
2853
|
+
node.app.$run.addClass(node.node, cl);
|
|
2854
|
+
}
|
|
2855
|
+
function removeClass(node, cl) {
|
|
2856
|
+
node.app.$run.removeClass(node.node, cl);
|
|
2857
|
+
}
|
|
2858
|
+
return (node, value) => {
|
|
2859
|
+
if (value !== current) {
|
|
2860
|
+
if (typeof current === "string" && current !== "") {
|
|
2861
|
+
removeClass(node, current);
|
|
2862
|
+
}
|
|
2863
|
+
if (typeof value === "boolean") {
|
|
2864
|
+
if (value) {
|
|
2865
|
+
addClass(node, name);
|
|
2866
|
+
}
|
|
2867
|
+
else {
|
|
2868
|
+
removeClass(node, name);
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
else if (typeof value === "string" && value !== "") {
|
|
2872
|
+
addClass(node, value);
|
|
2873
|
+
}
|
|
2874
|
+
current = value;
|
|
2875
|
+
}
|
|
2876
|
+
};
|
|
2877
|
+
}
|
|
2878
|
+
}
|
|
2879
|
+
|
|
2880
|
+
window.ClassBinding = ClassBinding;
|
|
2881
|
+
|
|
2882
|
+
// ./lib/views/repeat-node.js
|
|
2883
|
+
/**
|
|
2884
|
+
* Private part of repeat node
|
|
2885
|
+
* @class RepeatNodePrivate
|
|
2886
|
+
* @extends INodePrivate
|
|
2887
|
+
*/
|
|
2888
|
+
class RepeatNodePrivate extends INodePrivate {
|
|
2889
|
+
constructor() {
|
|
2890
|
+
super();
|
|
2891
|
+
/**
|
|
2892
|
+
* Children node hash
|
|
2893
|
+
* @type {Map}
|
|
2894
|
+
*/
|
|
2895
|
+
this.nodes = new Map();
|
|
2896
|
+
this.$seal();
|
|
2897
|
+
}
|
|
2898
|
+
$destroy() {
|
|
2899
|
+
this.nodes.clear();
|
|
2900
|
+
super.$destroy();
|
|
2901
|
+
}
|
|
2902
|
+
}
|
|
2903
|
+
/**
|
|
2904
|
+
* Repeat node repeats its children
|
|
2905
|
+
* @class RepeatNode
|
|
2906
|
+
* @extends Fragment
|
|
2907
|
+
*/
|
|
2908
|
+
class RepeatNode extends Fragment {
|
|
2909
|
+
constructor($) {
|
|
2910
|
+
super($ || new RepeatNodePrivate);
|
|
2911
|
+
/**
|
|
2912
|
+
* If false will use timeout executor, otherwise the app executor
|
|
2913
|
+
*/
|
|
2914
|
+
this.freezeUi = true;
|
|
2915
|
+
this.slot = new Slot;
|
|
2916
|
+
}
|
|
2917
|
+
createChild(id, item, before) {
|
|
2918
|
+
// TODO: Refactor: remove @ts-ignore
|
|
2919
|
+
const node = new Fragment();
|
|
2920
|
+
// eslint-disable-next-line
|
|
2921
|
+
// @ts-ignore
|
|
2922
|
+
const $ = node.$;
|
|
2923
|
+
this.destroyChild(id, item);
|
|
2924
|
+
if (before) {
|
|
2925
|
+
$.next = before;
|
|
2926
|
+
// eslint-disable-next-line
|
|
2927
|
+
// @ts-ignore
|
|
2928
|
+
$.prev = before.$.prev;
|
|
2929
|
+
// eslint-disable-next-line
|
|
2930
|
+
// @ts-ignore
|
|
2931
|
+
before.$.prev = node;
|
|
2932
|
+
if ($.prev) {
|
|
2933
|
+
// eslint-disable-next-line
|
|
2934
|
+
// @ts-ignore
|
|
2935
|
+
$.prev.$.next = node;
|
|
2936
|
+
}
|
|
2937
|
+
this.$children.splice(this.$children.indexOf(before), 0, node);
|
|
2938
|
+
}
|
|
2939
|
+
else {
|
|
2940
|
+
const lastChild = this.$children[this.$children.length - 1];
|
|
2941
|
+
if (lastChild) {
|
|
2942
|
+
// eslint-disable-next-line
|
|
2943
|
+
// @ts-ignore
|
|
2944
|
+
lastChild.$.next = node;
|
|
2945
|
+
}
|
|
2946
|
+
$.prev = lastChild;
|
|
2947
|
+
this.$children.push(node);
|
|
2948
|
+
}
|
|
2949
|
+
node.$preinit(this.$.app, this);
|
|
2950
|
+
node.$init();
|
|
2951
|
+
const callback = () => {
|
|
2952
|
+
this.slot.release(node, item, id);
|
|
2953
|
+
node.$ready();
|
|
2954
|
+
};
|
|
2955
|
+
if (this.freezeUi) {
|
|
2956
|
+
this.$.app.$run.callCallback(callback);
|
|
2957
|
+
}
|
|
2958
|
+
else {
|
|
2959
|
+
timeoutExecutor.callCallback(callback);
|
|
2960
|
+
}
|
|
2961
|
+
this.$.nodes.set(id, node);
|
|
2962
|
+
}
|
|
2963
|
+
destroyChild(id, item) {
|
|
2964
|
+
const $ = this.$;
|
|
2965
|
+
const child = $.nodes.get(id);
|
|
2966
|
+
if (child) {
|
|
2967
|
+
// eslint-disable-next-line
|
|
2968
|
+
// @ts-ignore
|
|
2969
|
+
const $ = child.$;
|
|
2970
|
+
if ($.prev) {
|
|
2971
|
+
// eslint-disable-next-line
|
|
2972
|
+
// @ts-ignore
|
|
2973
|
+
$.prev.$.next = $.next;
|
|
2974
|
+
}
|
|
2975
|
+
if ($.next) {
|
|
2976
|
+
// eslint-disable-next-line
|
|
2977
|
+
// @ts-ignore
|
|
2978
|
+
$.next.$.prev = $.prev;
|
|
2979
|
+
}
|
|
2980
|
+
child.$destroy();
|
|
2981
|
+
this.$.nodes.delete(id);
|
|
2982
|
+
this.$children.splice(this.$children.indexOf(child), 1);
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
|
|
2987
|
+
window.RepeatNodePrivate = RepeatNodePrivate;
|
|
2988
|
+
window.RepeatNode = RepeatNode;
|
|
2989
|
+
|
|
2990
|
+
// ./lib/views/repeater.js
|
|
2991
|
+
/**
|
|
2992
|
+
* Private part of repeater
|
|
2993
|
+
* @class RepeaterPrivate
|
|
2994
|
+
* @extends RepeatNodePrivate
|
|
2995
|
+
*/
|
|
2996
|
+
class RepeaterPrivate extends RepeatNodePrivate {
|
|
2997
|
+
constructor() {
|
|
2998
|
+
super();
|
|
2999
|
+
/**
|
|
3000
|
+
* Current count of child nodes
|
|
3001
|
+
*/
|
|
3002
|
+
this.currentCount = 0;
|
|
3003
|
+
this.$seal();
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
/**
|
|
3007
|
+
* The simplest repeat node interpretation, repeat children pack a several times
|
|
3008
|
+
* @class Repeater
|
|
3009
|
+
* @extends RepeatNode
|
|
3010
|
+
*/
|
|
3011
|
+
class Repeater extends RepeatNode {
|
|
3012
|
+
constructor($) {
|
|
3013
|
+
super($ || new RepeaterPrivate);
|
|
3014
|
+
/**
|
|
3015
|
+
* The count of children
|
|
3016
|
+
*/
|
|
3017
|
+
this.count = new Reference(0);
|
|
3018
|
+
this.$seal();
|
|
3019
|
+
}
|
|
3020
|
+
/**
|
|
3021
|
+
* Changes the children count
|
|
3022
|
+
*/
|
|
3023
|
+
changeCount(number) {
|
|
3024
|
+
const $ = this.$;
|
|
3025
|
+
if (number > $.currentCount) {
|
|
3026
|
+
for (let i = $.currentCount; i < number; i++) {
|
|
3027
|
+
this.createChild(i, i);
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
3030
|
+
else {
|
|
3031
|
+
for (let i = $.currentCount - 1; i >= number; i--) {
|
|
3032
|
+
this.destroyChild(i, i);
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
$.currentCount = number;
|
|
3036
|
+
}
|
|
3037
|
+
$created() {
|
|
3038
|
+
const $ = this.$;
|
|
3039
|
+
super.$created();
|
|
3040
|
+
$.updateHandler = this.changeCount.bind(this);
|
|
3041
|
+
this.count.on($.updateHandler);
|
|
3042
|
+
}
|
|
3043
|
+
$ready() {
|
|
3044
|
+
this.changeCount(this.count.$);
|
|
3045
|
+
}
|
|
3046
|
+
$destroy() {
|
|
3047
|
+
const $ = this.$;
|
|
3048
|
+
super.$destroy();
|
|
3049
|
+
this.count.off($.updateHandler);
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
|
|
3053
|
+
window.RepeaterPrivate = RepeaterPrivate;
|
|
3054
|
+
window.Repeater = Repeater;
|
|
3055
|
+
|
|
3056
|
+
// ./lib/views/base-view.js
|
|
3057
|
+
/**
|
|
3058
|
+
* Private part of BaseView
|
|
3059
|
+
* @class BaseViewPrivate
|
|
3060
|
+
* @extends RepeatNodePrivate
|
|
3061
|
+
*/
|
|
3062
|
+
class BaseViewPrivate extends RepeatNodePrivate {
|
|
3063
|
+
constructor() {
|
|
3064
|
+
super();
|
|
3065
|
+
this.$seal();
|
|
3066
|
+
}
|
|
3067
|
+
}
|
|
3068
|
+
/**
|
|
3069
|
+
* Base class of default views
|
|
3070
|
+
* @class BaseView
|
|
3071
|
+
* @extends RepeatNode
|
|
3072
|
+
* @implements IModel
|
|
3073
|
+
*/
|
|
3074
|
+
class BaseView extends RepeatNode {
|
|
3075
|
+
constructor($1) {
|
|
3076
|
+
super($1 || new BaseViewPrivate);
|
|
3077
|
+
const $ = this.$;
|
|
3078
|
+
$.addHandler = (id, item) => {
|
|
3079
|
+
this.createChild(id, item);
|
|
3080
|
+
};
|
|
3081
|
+
$.removeHandler = (id, item) => {
|
|
3082
|
+
this.destroyChild(id, item);
|
|
3083
|
+
};
|
|
3084
|
+
this.$seal();
|
|
3085
|
+
}
|
|
3086
|
+
/**
|
|
3087
|
+
* Handle ready event
|
|
3088
|
+
*/
|
|
3089
|
+
$ready() {
|
|
3090
|
+
const $ = this.$;
|
|
3091
|
+
this.model.listener.onAdd($.addHandler);
|
|
3092
|
+
this.model.listener.onRemove($.removeHandler);
|
|
3093
|
+
super.$ready();
|
|
3094
|
+
}
|
|
3095
|
+
/**
|
|
3096
|
+
* Handles destroy event
|
|
3097
|
+
*/
|
|
3098
|
+
$destroy() {
|
|
3099
|
+
const $ = this.$;
|
|
3100
|
+
this.model.listener.offAdd($.addHandler);
|
|
3101
|
+
this.model.listener.offRemove($.removeHandler);
|
|
3102
|
+
super.$destroy();
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
|
|
3106
|
+
window.BaseViewPrivate = BaseViewPrivate;
|
|
3107
|
+
window.BaseView = BaseView;
|
|
3108
|
+
|
|
3109
|
+
// ./lib/views/array-view.js
|
|
3110
|
+
/**
|
|
3111
|
+
* Represents a view of an array model
|
|
3112
|
+
* @class ArrayView
|
|
3113
|
+
* @extends BaseView
|
|
3114
|
+
*/
|
|
3115
|
+
class ArrayView extends BaseView {
|
|
3116
|
+
constructor(model) {
|
|
3117
|
+
super();
|
|
3118
|
+
this.model = model;
|
|
3119
|
+
}
|
|
3120
|
+
createChild(id, item, before) {
|
|
3121
|
+
super.createChild(item, item, before || this.$.nodes.get(id));
|
|
3122
|
+
}
|
|
3123
|
+
$ready() {
|
|
3124
|
+
this.model.forEach(item => {
|
|
3125
|
+
this.createChild(item, item);
|
|
3126
|
+
});
|
|
3127
|
+
super.$ready();
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3130
|
+
|
|
3131
|
+
window.ArrayView = ArrayView;
|
|
3132
|
+
|
|
3133
|
+
// ./lib/node/watch.js
|
|
3134
|
+
/**
|
|
3135
|
+
* Watch Node
|
|
3136
|
+
* @class Watch
|
|
3137
|
+
* @extends Fragment
|
|
3138
|
+
*/
|
|
3139
|
+
class Watch extends Fragment {
|
|
3140
|
+
constructor() {
|
|
3141
|
+
super();
|
|
3142
|
+
this.slot = new Slot;
|
|
3143
|
+
this.model = this.$ref(null);
|
|
3144
|
+
this.$seal();
|
|
3145
|
+
}
|
|
3146
|
+
$createWatchers() {
|
|
3147
|
+
this.$watch((value) => {
|
|
3148
|
+
this.$children.forEach(child => {
|
|
3149
|
+
child.$destroy();
|
|
3150
|
+
});
|
|
3151
|
+
this.$children.splice(0);
|
|
3152
|
+
this.slot.release(this, value);
|
|
3153
|
+
}, this.model);
|
|
3154
|
+
}
|
|
3155
|
+
$compose() {
|
|
3156
|
+
this.slot.release(this, this.model.$);
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3159
|
+
|
|
3160
|
+
window.Watch = Watch;
|
|
3161
|
+
|
|
3162
|
+
// ./lib/views/object-view.js
|
|
3163
|
+
/**
|
|
3164
|
+
* Create a children pack for each object field
|
|
3165
|
+
* @class ObjectView
|
|
3166
|
+
* @extends BaseView
|
|
3167
|
+
*/
|
|
3168
|
+
class ObjectView extends BaseView {
|
|
3169
|
+
constructor(model) {
|
|
3170
|
+
super();
|
|
3171
|
+
this.model = model;
|
|
3172
|
+
}
|
|
3173
|
+
$ready() {
|
|
3174
|
+
const obj = this.model;
|
|
3175
|
+
for (const key in obj) {
|
|
3176
|
+
this.createChild(key, obj[key]);
|
|
3177
|
+
}
|
|
3178
|
+
super.$ready();
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
|
|
3182
|
+
window.ObjectView = ObjectView;
|
|
3183
|
+
|
|
3184
|
+
// ./lib/views/map-view.js
|
|
3185
|
+
/**
|
|
3186
|
+
* Create a children pack for each map value
|
|
3187
|
+
* @class MapView
|
|
3188
|
+
* @extends BaseView
|
|
3189
|
+
*/
|
|
3190
|
+
class MapView extends BaseView {
|
|
3191
|
+
constructor(model) {
|
|
3192
|
+
super();
|
|
3193
|
+
this.model = model;
|
|
3194
|
+
}
|
|
3195
|
+
$ready() {
|
|
3196
|
+
const map = this.model;
|
|
3197
|
+
map.forEach((value, key) => {
|
|
3198
|
+
this.createChild(key, value);
|
|
3199
|
+
});
|
|
3200
|
+
super.$ready();
|
|
3201
|
+
}
|
|
3202
|
+
}
|
|
3203
|
+
|
|
3204
|
+
window.MapView = MapView;
|
|
3205
|
+
|
|
3206
|
+
// ./lib/views/set-view.js
|
|
3207
|
+
/**
|
|
3208
|
+
* Create a children pack for each set value
|
|
3209
|
+
* @class SetView
|
|
3210
|
+
* @extends BaseView
|
|
3211
|
+
*/
|
|
3212
|
+
class SetView extends BaseView {
|
|
3213
|
+
constructor(model) {
|
|
3214
|
+
super();
|
|
3215
|
+
this.model = model;
|
|
3216
|
+
}
|
|
3217
|
+
$ready() {
|
|
3218
|
+
const $ = this.$;
|
|
3219
|
+
const set = this.model;
|
|
3220
|
+
set.forEach(item => {
|
|
3221
|
+
$.app.$run.callCallback(() => {
|
|
3222
|
+
this.createChild(item, item);
|
|
3223
|
+
});
|
|
3224
|
+
});
|
|
3225
|
+
super.$ready();
|
|
3226
|
+
}
|
|
3227
|
+
}
|
|
3228
|
+
|
|
3229
|
+
window.SetView = SetView;
|
|
3230
|
+
|
|
3231
|
+
})();
|