vasille 1.2.9 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/flow-typed/vasille.js +836 -0
- package/img/favicon.svg +17 -204
- package/lib/binding/attribute.js +32 -0
- package/lib/binding/binding.js +39 -0
- package/lib/binding/class.js +51 -0
- package/lib/binding/style.js +29 -0
- package/lib/core/core.js +178 -0
- package/lib/core/destroyable.js +45 -0
- package/lib/core/errors.js +16 -0
- package/lib/core/executor.js +154 -0
- package/lib/core/ivalue.js +56 -0
- package/lib/core/signal.js +50 -0
- package/lib/core/slot.js +47 -0
- package/lib/index.js +27 -22
- package/lib/models/array-model.js +208 -0
- package/lib/models/listener.js +130 -0
- package/lib/models/map-model.js +66 -0
- package/lib/models/model.js +1 -0
- package/lib/models/object-model.js +78 -0
- package/lib/models/set-model.js +62 -0
- package/lib/node/app.js +38 -0
- package/lib/node/interceptor.js +83 -0
- package/lib/node/node.js +1185 -0
- package/lib/node/watch.js +27 -0
- package/lib/value/expression.js +83 -0
- package/lib/value/mirror.js +58 -0
- package/lib/value/pointer.js +26 -0
- package/lib/value/reference.js +55 -0
- package/lib/views/array-view.js +24 -0
- package/lib/views/base-view.js +49 -0
- package/lib/views/map-view.js +20 -0
- package/lib/views/object-view.js +20 -0
- package/lib/views/repeat-node.js +106 -0
- package/lib/views/repeater.js +63 -0
- package/lib/views/set-view.js +23 -0
- package/package.json +16 -15
- package/types/binding/attribute.d.ts +23 -0
- package/types/binding/binding.d.ts +30 -0
- package/types/binding/class.d.ts +23 -0
- package/types/binding/style.d.ts +23 -0
- package/types/core/core.d.ts +140 -0
- package/types/core/destroyable.d.ts +15 -0
- package/types/core/errors.d.ts +4 -0
- package/types/core/executor.d.ts +87 -0
- package/types/core/ivalue.d.ts +45 -0
- package/types/core/signal.d.ts +35 -0
- package/types/core/slot.d.ts +45 -0
- package/types/index.d.ts +27 -21
- package/types/models/array-model.d.ts +103 -0
- package/types/models/listener.d.ts +74 -0
- package/types/models/map-model.d.ts +35 -0
- package/types/models/model.d.ts +19 -0
- package/types/models/object-model.d.ts +36 -0
- package/types/models/set-model.d.ts +34 -0
- package/types/node/app.d.ts +37 -0
- package/types/node/interceptor.d.ts +50 -0
- package/types/node/node.d.ts +739 -0
- package/types/node/watch.d.ts +23 -0
- package/types/value/expression.d.ts +60 -0
- package/types/value/mirror.d.ts +35 -0
- package/types/value/pointer.d.ts +19 -0
- package/types/value/reference.d.ts +30 -0
- package/types/views/array-view.d.ts +13 -0
- package/types/views/base-view.d.ts +44 -0
- package/types/views/map-view.d.ts +11 -0
- package/types/views/object-view.d.ts +11 -0
- package/types/views/repeat-node.d.ts +35 -0
- package/types/views/repeater.d.ts +38 -0
- package/types/views/set-view.d.ts +11 -0
- package/CHANGELOG.md +0 -23
- package/lib/attribute.js +0 -71
- package/lib/attribute.js.map +0 -1
- package/lib/bind.js +0 -286
- package/lib/bind.js.map +0 -1
- package/lib/class.js +0 -97
- package/lib/class.js.map +0 -1
- package/lib/executor.js +0 -167
- package/lib/executor.js.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/interfaces/core.js +0 -247
- package/lib/interfaces/core.js.map +0 -1
- package/lib/interfaces/destroyable.js +0 -39
- package/lib/interfaces/destroyable.js.map +0 -1
- package/lib/interfaces/errors.js +0 -49
- package/lib/interfaces/errors.js.map +0 -1
- package/lib/interfaces/ibind.js +0 -31
- package/lib/interfaces/ibind.js.map +0 -1
- package/lib/interfaces/idefinition.js +0 -64
- package/lib/interfaces/idefinition.js.map +0 -1
- package/lib/interfaces/ivalue.js +0 -60
- package/lib/interfaces/ivalue.js.map +0 -1
- package/lib/models.js +0 -579
- package/lib/models.js.map +0 -1
- package/lib/node.js +0 -2155
- package/lib/node.js.map +0 -1
- package/lib/property.js +0 -38
- package/lib/property.js.map +0 -1
- package/lib/style.js +0 -66
- package/lib/style.js.map +0 -1
- package/lib/value.js +0 -203
- package/lib/value.js.map +0 -1
- package/lib/views.js +0 -688
- package/lib/views.js.map +0 -1
- package/types/attribute.d.ts +0 -18
- package/types/bind.d.ts +0 -72
- package/types/class.d.ts +0 -19
- package/types/data.d.ts +0 -11
- package/types/event.d.ts +0 -10
- package/types/executor.d.ts +0 -57
- package/types/interfaces/core.d.ts +0 -129
- package/types/interfaces/destroyable.d.ts +0 -11
- package/types/interfaces/errors.d.ts +0 -24
- package/types/interfaces/ibind.d.ts +0 -19
- package/types/interfaces/idefinition.d.ts +0 -29
- package/types/interfaces/ivalue.d.ts +0 -40
- package/types/models.d.ts +0 -179
- package/types/node.d.ts +0 -906
- package/types/property.d.ts +0 -9
- package/types/style.d.ts +0 -28
- package/types/value.d.ts +0 -43
- package/types/views.d.ts +0 -135
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { internalError, notOverwritten } from "./errors";
|
|
2
|
+
/**
|
|
3
|
+
* Represents an executor unit interface
|
|
4
|
+
* @class Executor
|
|
5
|
+
*/
|
|
6
|
+
export class Executor {
|
|
7
|
+
/**
|
|
8
|
+
* Adds a CSS class
|
|
9
|
+
* @param el {Element} element to manipulate
|
|
10
|
+
* @param cl {string} class to be added
|
|
11
|
+
*/
|
|
12
|
+
addClass(el, cl) {
|
|
13
|
+
throw notOverwritten();
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Removes a CSS class
|
|
17
|
+
* @param el {Element} element to manipulate
|
|
18
|
+
* @param cl {string} class to be removed
|
|
19
|
+
*/
|
|
20
|
+
removeClass(el, cl) {
|
|
21
|
+
throw notOverwritten();
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Sets a tag attribute
|
|
25
|
+
* @param el {Element} element to manipulate
|
|
26
|
+
* @param name {string} name of attribute
|
|
27
|
+
* @param value {string} value of attribute
|
|
28
|
+
*/
|
|
29
|
+
setAttribute(el, name, value) {
|
|
30
|
+
throw notOverwritten();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Removes a tag attribute
|
|
34
|
+
* @param el {Element} element to manipulate
|
|
35
|
+
* @param name {string} name of attribute
|
|
36
|
+
*/
|
|
37
|
+
removeAttribute(el, name) {
|
|
38
|
+
throw notOverwritten();
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Sets a style attribute
|
|
42
|
+
* @param el {HTMLElement} element to manipulate
|
|
43
|
+
* @param prop {string} property name
|
|
44
|
+
* @param value {string} property value
|
|
45
|
+
*/
|
|
46
|
+
setStyle(el, prop, value) {
|
|
47
|
+
throw notOverwritten();
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Inserts a child before target
|
|
51
|
+
* @param target {Element} target element
|
|
52
|
+
* @param child {Node} element to insert before
|
|
53
|
+
*/
|
|
54
|
+
insertBefore(target, child) {
|
|
55
|
+
throw notOverwritten();
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Appends a child to element
|
|
59
|
+
* @param el {Element} element
|
|
60
|
+
* @param child {Node} child to be inserted
|
|
61
|
+
*/
|
|
62
|
+
appendChild(el, child) {
|
|
63
|
+
throw notOverwritten();
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Calls a call-back function
|
|
67
|
+
* @param cb {function} call-back function
|
|
68
|
+
*/
|
|
69
|
+
callCallback(cb) {
|
|
70
|
+
throw notOverwritten();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Executor which execute any commands immediately
|
|
75
|
+
* @class InstantExecutor
|
|
76
|
+
* @extends Executor
|
|
77
|
+
*/
|
|
78
|
+
export class InstantExecutor extends Executor {
|
|
79
|
+
addClass(el, cl) {
|
|
80
|
+
el.classList.add(cl);
|
|
81
|
+
}
|
|
82
|
+
removeClass(el, cl) {
|
|
83
|
+
el.classList.remove(cl);
|
|
84
|
+
}
|
|
85
|
+
setAttribute(el, name, value) {
|
|
86
|
+
el.setAttribute(name, value);
|
|
87
|
+
}
|
|
88
|
+
removeAttribute(el, name) {
|
|
89
|
+
el.removeAttribute(name);
|
|
90
|
+
}
|
|
91
|
+
setStyle(el, prop, value) {
|
|
92
|
+
el.style.setProperty(prop, value);
|
|
93
|
+
}
|
|
94
|
+
insertBefore(target, child) {
|
|
95
|
+
const parent = target.parentNode;
|
|
96
|
+
if (!parent) {
|
|
97
|
+
throw internalError('element don\'t have a parent node');
|
|
98
|
+
}
|
|
99
|
+
parent.insertBefore(child, target);
|
|
100
|
+
}
|
|
101
|
+
appendChild(el, child) {
|
|
102
|
+
el.appendChild(child);
|
|
103
|
+
}
|
|
104
|
+
callCallback(cb) {
|
|
105
|
+
cb();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Executor which execute any commands over timeout
|
|
110
|
+
* @class TimeoutExecutor
|
|
111
|
+
* @extends InstantExecutor
|
|
112
|
+
*/
|
|
113
|
+
export class TimeoutExecutor extends InstantExecutor {
|
|
114
|
+
addClass(el, cl) {
|
|
115
|
+
setTimeout(() => {
|
|
116
|
+
super.addClass(el, cl);
|
|
117
|
+
}, 0);
|
|
118
|
+
}
|
|
119
|
+
removeClass(el, cl) {
|
|
120
|
+
setTimeout(() => {
|
|
121
|
+
super.removeClass(el, cl);
|
|
122
|
+
}, 0);
|
|
123
|
+
}
|
|
124
|
+
setAttribute(el, name, value) {
|
|
125
|
+
setTimeout(() => {
|
|
126
|
+
super.setAttribute(el, name, value);
|
|
127
|
+
}, 0);
|
|
128
|
+
}
|
|
129
|
+
removeAttribute(el, name) {
|
|
130
|
+
setTimeout(() => {
|
|
131
|
+
super.removeAttribute(el, name);
|
|
132
|
+
}, 0);
|
|
133
|
+
}
|
|
134
|
+
setStyle(el, prop, value) {
|
|
135
|
+
setTimeout(() => {
|
|
136
|
+
super.setStyle(el, prop, value);
|
|
137
|
+
}, 0);
|
|
138
|
+
}
|
|
139
|
+
insertBefore(target, child) {
|
|
140
|
+
setTimeout(() => {
|
|
141
|
+
super.insertBefore(target, child);
|
|
142
|
+
}, 0);
|
|
143
|
+
}
|
|
144
|
+
appendChild(el, child) {
|
|
145
|
+
setTimeout(() => {
|
|
146
|
+
super.appendChild(el, child);
|
|
147
|
+
}, 0);
|
|
148
|
+
}
|
|
149
|
+
callCallback(cb) {
|
|
150
|
+
setTimeout(cb, 0);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
export const instantExecutor = new InstantExecutor();
|
|
154
|
+
export const timeoutExecutor = new TimeoutExecutor();
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Destroyable } from "./destroyable.js";
|
|
2
|
+
import { notOverwritten } from "./errors";
|
|
3
|
+
/**
|
|
4
|
+
* Interface which describes a value
|
|
5
|
+
* @class IValue
|
|
6
|
+
* @extends Destroyable
|
|
7
|
+
*/
|
|
8
|
+
export class IValue extends Destroyable {
|
|
9
|
+
/**
|
|
10
|
+
* @param isEnabled {boolean} initial is enabled state
|
|
11
|
+
*/
|
|
12
|
+
constructor(isEnabled) {
|
|
13
|
+
super();
|
|
14
|
+
this.isEnabled = isEnabled;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Get the encapsulated value
|
|
18
|
+
* @return {*} the encapsulated value
|
|
19
|
+
*/
|
|
20
|
+
get $() {
|
|
21
|
+
throw notOverwritten();
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Sets the encapsulated value
|
|
25
|
+
* @param value {*} value to encapsulate
|
|
26
|
+
*/
|
|
27
|
+
set $(value) {
|
|
28
|
+
throw notOverwritten();
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Add a new handler to value change
|
|
32
|
+
* @param handler {function(value : *)} the handler to add
|
|
33
|
+
*/
|
|
34
|
+
on(handler) {
|
|
35
|
+
throw notOverwritten();
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Removes a handler of value change
|
|
39
|
+
* @param handler {function(value : *)} the handler to remove
|
|
40
|
+
*/
|
|
41
|
+
off(handler) {
|
|
42
|
+
throw notOverwritten();
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Enable update handlers triggering
|
|
46
|
+
*/
|
|
47
|
+
enable() {
|
|
48
|
+
throw notOverwritten();
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* disable update handlers triggering
|
|
52
|
+
*/
|
|
53
|
+
disable() {
|
|
54
|
+
throw notOverwritten();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signal is an event generator
|
|
3
|
+
* @class Signal
|
|
4
|
+
*/
|
|
5
|
+
export class Signal {
|
|
6
|
+
constructor() {
|
|
7
|
+
/**
|
|
8
|
+
* Handler of event
|
|
9
|
+
* @type {Set}
|
|
10
|
+
* @private
|
|
11
|
+
*/
|
|
12
|
+
this.handlers = new Set;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Emit event
|
|
16
|
+
* @param a1 {*} argument
|
|
17
|
+
* @param a2 {*} argument
|
|
18
|
+
* @param a3 {*} argument
|
|
19
|
+
* @param a4 {*} argument
|
|
20
|
+
* @param a5 {*} argument
|
|
21
|
+
* @param a6 {*} argument
|
|
22
|
+
* @param a7 {*} argument
|
|
23
|
+
* @param a8 {*} argument
|
|
24
|
+
* @param a9 {*} argument
|
|
25
|
+
*/
|
|
26
|
+
emit(a1, a2, a3, a4, a5, a6, a7, a8, a9) {
|
|
27
|
+
this.handlers.forEach(handler => {
|
|
28
|
+
try {
|
|
29
|
+
handler(a1, a2, a3, a4, a5, a6, a7, a8, a9);
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
console.error(`Vasille.js: Handler throw exception: `, e);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Subscribe to event
|
|
38
|
+
* @param func {function} handler
|
|
39
|
+
*/
|
|
40
|
+
subscribe(func) {
|
|
41
|
+
this.handlers.add(func);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Unsubscribe from event
|
|
45
|
+
* @param func {function} handler
|
|
46
|
+
*/
|
|
47
|
+
unsubscribe(func) {
|
|
48
|
+
this.handlers.delete(func);
|
|
49
|
+
}
|
|
50
|
+
}
|
package/lib/core/slot.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component slot
|
|
3
|
+
* @class Slot
|
|
4
|
+
*/
|
|
5
|
+
export class Slot {
|
|
6
|
+
/**
|
|
7
|
+
* Sets the runner
|
|
8
|
+
* @param func {function} the function to run
|
|
9
|
+
*/
|
|
10
|
+
insert(func) {
|
|
11
|
+
this.runner = func;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* @param a0 {Fragment} node to paste content
|
|
15
|
+
* @param a1 {*} 1st argument
|
|
16
|
+
* @param a2 {*} 2nd argument
|
|
17
|
+
* @param a3 {*} 3rd argument
|
|
18
|
+
* @param a4 {*} 4th argument
|
|
19
|
+
* @param a5 {*} 5th argument
|
|
20
|
+
* @param a6 {*} 6th argument
|
|
21
|
+
* @param a7 {*} 7th argument
|
|
22
|
+
* @param a8 {*} 8th argument
|
|
23
|
+
* @param a9 {*} 9th argument
|
|
24
|
+
*/
|
|
25
|
+
release(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) {
|
|
26
|
+
if (this.runner) {
|
|
27
|
+
this.runner(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Predefine a handler for a slot
|
|
32
|
+
* @param func {function(node : Fragment)} Function to run if no handler specified
|
|
33
|
+
* @param a0 {Fragment} node to paste content
|
|
34
|
+
* @param a1 {*} 1st argument
|
|
35
|
+
* @param a2 {*} 2nd argument
|
|
36
|
+
* @param a3 {*} 3rd argument
|
|
37
|
+
* @param a4 {*} 4th argument
|
|
38
|
+
* @param a5 {*} 5th argument
|
|
39
|
+
* @param a6 {*} 6th argument
|
|
40
|
+
* @param a7 {*} 7th argument
|
|
41
|
+
* @param a8 {*} 8th argument
|
|
42
|
+
* @param a9 {*} 9th argument
|
|
43
|
+
*/
|
|
44
|
+
predefine(func, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) {
|
|
45
|
+
(this.runner || func)(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
|
|
46
|
+
}
|
|
47
|
+
}
|
package/lib/index.js
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { IValue }
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
import { Destroyable } from "./core/destroyable";
|
|
2
|
+
import { Executor, InstantExecutor, TimeoutExecutor } from "./core/executor";
|
|
3
|
+
import { Reactive } from "./core/core";
|
|
4
|
+
import { IValue } from "./core/ivalue";
|
|
5
|
+
import { Signal } from "./core/signal";
|
|
6
|
+
import { Slot } from "./core/slot";
|
|
7
|
+
import { ArrayModel } from "./models/array-model";
|
|
8
|
+
import { Listener } from "./models/listener";
|
|
9
|
+
import { MapModel } from "./models/map-model";
|
|
10
|
+
import { ObjectModel } from "./models/object-model";
|
|
11
|
+
import { SetModel } from "./models/set-model";
|
|
12
|
+
import { App, AppNode } from "./node/app";
|
|
13
|
+
import { Interceptor, InterceptorNode } from "./node/interceptor";
|
|
14
|
+
import { Component, Extension, Fragment, INode, Tag } from "./node/node";
|
|
15
|
+
import { Expression } from "./value/expression";
|
|
16
|
+
import { Mirror } from "./value/mirror";
|
|
17
|
+
import { Pointer } from "./value/pointer";
|
|
18
|
+
import { Reference } from "./value/reference";
|
|
19
|
+
import { ArrayView } from "./views/array-view";
|
|
20
|
+
import { BaseView } from "./views/base-view";
|
|
21
|
+
import { MapView } from "./views/map-view";
|
|
22
|
+
import { ObjectView } from "./views/object-view";
|
|
23
|
+
import { RepeatNode } from "./views/repeat-node";
|
|
24
|
+
import { Repeater } from "./views/repeater";
|
|
25
|
+
import { SetView } from "./views/set-view";
|
|
26
|
+
import { Binding } from "./binding/binding";
|
|
27
|
+
export { Destroyable, IValue, Reference, Mirror, Pointer, ArrayModel, MapModel, ObjectModel, SetModel, RepeatNode, Repeater, BaseView, Listener, ArrayView, MapView, ObjectView, SetView, Fragment, INode, Tag, Component, Extension, AppNode, App, Executor, InstantExecutor, TimeoutExecutor, Signal, Slot, Interceptor, InterceptorNode, Expression, Binding, Reactive, };
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { Listener } from "./listener";
|
|
2
|
+
/**
|
|
3
|
+
* Model based on Array class
|
|
4
|
+
* @extends Array
|
|
5
|
+
* @implements IModel
|
|
6
|
+
*/
|
|
7
|
+
export class ArrayModel extends Array {
|
|
8
|
+
/**
|
|
9
|
+
* @param data {Array} input data
|
|
10
|
+
*/
|
|
11
|
+
constructor(data = []) {
|
|
12
|
+
super();
|
|
13
|
+
Object.defineProperty(this, 'listener', {
|
|
14
|
+
value: new Listener,
|
|
15
|
+
writable: false,
|
|
16
|
+
configurable: false
|
|
17
|
+
});
|
|
18
|
+
for (let i = 0; i < data.length; i++) {
|
|
19
|
+
super.push(data[i]);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/* Array members */
|
|
23
|
+
/**
|
|
24
|
+
* Gets the last item of array
|
|
25
|
+
* @return {*} the last item of array
|
|
26
|
+
*/
|
|
27
|
+
get last() {
|
|
28
|
+
return this.length ? this[this.length - 1] : null;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Calls Array.fill and notify about changes
|
|
32
|
+
* @param value {*} value to fill with
|
|
33
|
+
* @param start {?number} begin index
|
|
34
|
+
* @param end {?number} end index
|
|
35
|
+
*/
|
|
36
|
+
fill(value, start, end) {
|
|
37
|
+
if (!start) {
|
|
38
|
+
start = 0;
|
|
39
|
+
}
|
|
40
|
+
if (!end) {
|
|
41
|
+
end = this.length;
|
|
42
|
+
}
|
|
43
|
+
for (let i = start; i < end; i++) {
|
|
44
|
+
this.listener.emitRemoved(this[i], this[i]);
|
|
45
|
+
this[i] = value;
|
|
46
|
+
this.listener.emitAdded(value, value);
|
|
47
|
+
}
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Calls Array.pop and notify about changes
|
|
52
|
+
* @return {*} removed value
|
|
53
|
+
*/
|
|
54
|
+
pop() {
|
|
55
|
+
const v = super.pop();
|
|
56
|
+
if (v !== undefined) {
|
|
57
|
+
this.listener.emitRemoved(v, v);
|
|
58
|
+
}
|
|
59
|
+
return v;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Calls Array.push and notify about changes
|
|
63
|
+
* @param items {...*} values to push
|
|
64
|
+
* @return {number} new length of array
|
|
65
|
+
*/
|
|
66
|
+
push(...items) {
|
|
67
|
+
items.forEach(item => {
|
|
68
|
+
this.listener.emitAdded(item, item);
|
|
69
|
+
super.push(item);
|
|
70
|
+
});
|
|
71
|
+
return this.length;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Calls Array.shift and notify about changed
|
|
75
|
+
* @return {*} the shifted value
|
|
76
|
+
*/
|
|
77
|
+
shift() {
|
|
78
|
+
const v = super.shift();
|
|
79
|
+
if (v !== undefined) {
|
|
80
|
+
this.listener.emitRemoved(v, v);
|
|
81
|
+
}
|
|
82
|
+
return v;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Calls Array.splice and notify about changed
|
|
86
|
+
* @param start {number} start index
|
|
87
|
+
* @param deleteCount {?number} delete count
|
|
88
|
+
* @param items {...*}
|
|
89
|
+
* @return {ArrayModel} a pointer to this
|
|
90
|
+
*/
|
|
91
|
+
splice(start, deleteCount, ...items) {
|
|
92
|
+
start = Math.min(start, this.length);
|
|
93
|
+
deleteCount = deleteCount || this.length - start;
|
|
94
|
+
const before = this[start + deleteCount];
|
|
95
|
+
for (let i = 0; i < deleteCount; i++) {
|
|
96
|
+
const index = start + deleteCount - i - 1;
|
|
97
|
+
if (this[index] !== undefined) {
|
|
98
|
+
this.listener.emitRemoved(this[index], this[index]);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
for (let i = 0; i < items.length; i++) {
|
|
102
|
+
this.listener.emitAdded(before, items[i]);
|
|
103
|
+
}
|
|
104
|
+
return new ArrayModel(super.splice(start, deleteCount, ...items));
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Calls Array.unshift and notify about changed
|
|
108
|
+
* @param items {...*} values to insert
|
|
109
|
+
* @return {number} the length after prepend
|
|
110
|
+
*/
|
|
111
|
+
unshift(...items) {
|
|
112
|
+
for (let i = 0; i < items.length; i++) {
|
|
113
|
+
this.listener.emitAdded(this[i], items[i]);
|
|
114
|
+
}
|
|
115
|
+
return super.unshift(...items);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Inserts a value to the end of array
|
|
119
|
+
* @param v {*} value to insert
|
|
120
|
+
*/
|
|
121
|
+
append(v) {
|
|
122
|
+
this.listener.emitAdded(null, v);
|
|
123
|
+
super.push(v);
|
|
124
|
+
return this;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Clears array
|
|
128
|
+
* @return {this} a pointer to this
|
|
129
|
+
*/
|
|
130
|
+
clear() {
|
|
131
|
+
this.forEach(v => {
|
|
132
|
+
this.listener.emitRemoved(v, v);
|
|
133
|
+
});
|
|
134
|
+
super.splice(0);
|
|
135
|
+
return this;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Inserts a value to position `index`
|
|
139
|
+
* @param index {number} index to insert value
|
|
140
|
+
* @param v {*} value to insert
|
|
141
|
+
* @return {this} a pointer to this
|
|
142
|
+
*/
|
|
143
|
+
insert(index, v) {
|
|
144
|
+
this.listener.emitAdded(this[index], v);
|
|
145
|
+
super.splice(index, 0, v);
|
|
146
|
+
return this;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Inserts a value to the beginning of array
|
|
150
|
+
* @param v {*} value to insert
|
|
151
|
+
* @return {this} a pointer to this
|
|
152
|
+
*/
|
|
153
|
+
prepend(v) {
|
|
154
|
+
this.listener.emitAdded(this[0], v);
|
|
155
|
+
super.unshift(v);
|
|
156
|
+
return this;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Removes a value from an index
|
|
160
|
+
* @param index {number} index of value to remove
|
|
161
|
+
* @return {this} a pointer to this
|
|
162
|
+
*/
|
|
163
|
+
removeAt(index) {
|
|
164
|
+
if (index > 0 && index < this.length) {
|
|
165
|
+
this.listener.emitRemoved(this[index], this[index]);
|
|
166
|
+
super.splice(index, 1);
|
|
167
|
+
}
|
|
168
|
+
return this;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Removes the first value of array
|
|
172
|
+
* @return {this} a pointer to this
|
|
173
|
+
*/
|
|
174
|
+
removeFirst() {
|
|
175
|
+
if (this.length) {
|
|
176
|
+
this.listener.emitRemoved(this[0], this[0]);
|
|
177
|
+
super.shift();
|
|
178
|
+
}
|
|
179
|
+
return this;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Removes the ast value of array
|
|
183
|
+
* @return {this} a pointer to this
|
|
184
|
+
*/
|
|
185
|
+
removeLast() {
|
|
186
|
+
const last = this.last;
|
|
187
|
+
if (last != null) {
|
|
188
|
+
this.listener.emitRemoved(this[this.length - 1], last);
|
|
189
|
+
super.pop();
|
|
190
|
+
}
|
|
191
|
+
return this;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Remove the first occurrence of value
|
|
195
|
+
* @param v {*} value to remove
|
|
196
|
+
* @return {this}
|
|
197
|
+
*/
|
|
198
|
+
removeOne(v) {
|
|
199
|
+
this.removeAt(this.indexOf(v));
|
|
200
|
+
return this;
|
|
201
|
+
}
|
|
202
|
+
enableReactivity() {
|
|
203
|
+
this.listener.enableReactivity();
|
|
204
|
+
}
|
|
205
|
+
disableReactivity() {
|
|
206
|
+
this.listener.disableReactivity();
|
|
207
|
+
}
|
|
208
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represent a listener for a model
|
|
3
|
+
* @class Listener
|
|
4
|
+
*/
|
|
5
|
+
export class Listener {
|
|
6
|
+
constructor() {
|
|
7
|
+
Object.defineProperties(this, {
|
|
8
|
+
onAdded: {
|
|
9
|
+
value: new Set,
|
|
10
|
+
writable: false,
|
|
11
|
+
configurable: false
|
|
12
|
+
},
|
|
13
|
+
onRemoved: {
|
|
14
|
+
value: new Set,
|
|
15
|
+
writable: false,
|
|
16
|
+
configurable: false
|
|
17
|
+
},
|
|
18
|
+
frozen: {
|
|
19
|
+
value: false,
|
|
20
|
+
writable: true,
|
|
21
|
+
configurable: false
|
|
22
|
+
},
|
|
23
|
+
queue: {
|
|
24
|
+
value: [],
|
|
25
|
+
writable: false,
|
|
26
|
+
configurable: false
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Exclude the repeated operation in queue
|
|
32
|
+
* @private
|
|
33
|
+
*/
|
|
34
|
+
excludeRepeat(index) {
|
|
35
|
+
this.queue.forEach((item, i) => {
|
|
36
|
+
if (item.index === index) {
|
|
37
|
+
this.queue.splice(i, 1);
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Emits added event to listeners
|
|
45
|
+
* @param index {*} index of value
|
|
46
|
+
* @param value {*} value of added item
|
|
47
|
+
*/
|
|
48
|
+
emitAdded(index, value) {
|
|
49
|
+
if (this.frozen) {
|
|
50
|
+
if (!this.excludeRepeat(index)) {
|
|
51
|
+
this.queue.push({ sign: true, index, value });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
this.onAdded.forEach(handler => {
|
|
56
|
+
handler(index, value);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Emits removed event to listeners
|
|
62
|
+
* @param index {*} index of removed value
|
|
63
|
+
* @param value {*} value of removed item
|
|
64
|
+
*/
|
|
65
|
+
emitRemoved(index, value) {
|
|
66
|
+
if (this.frozen) {
|
|
67
|
+
if (!this.excludeRepeat(index)) {
|
|
68
|
+
this.queue.push({ sign: false, index, value });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
this.onRemoved.forEach(handler => {
|
|
73
|
+
handler(index, value);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Adds a handler to added event
|
|
79
|
+
* @param handler {function} function to run on event emitting
|
|
80
|
+
*/
|
|
81
|
+
onAdd(handler) {
|
|
82
|
+
this.onAdded.add(handler);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Adds a handler to removed event
|
|
86
|
+
* @param handler {function} function to run on event emitting
|
|
87
|
+
*/
|
|
88
|
+
onRemove(handler) {
|
|
89
|
+
this.onRemoved.add(handler);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Removes an handler from added event
|
|
93
|
+
* @param handler {function} handler to remove
|
|
94
|
+
*/
|
|
95
|
+
offAdd(handler) {
|
|
96
|
+
this.onAdded.delete(handler);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Removes an handler form removed event
|
|
100
|
+
* @param handler {function} handler to remove
|
|
101
|
+
*/
|
|
102
|
+
offRemove(handler) {
|
|
103
|
+
this.onRemoved.delete(handler);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Run all queued operation and enable reactivity
|
|
107
|
+
*/
|
|
108
|
+
enableReactivity() {
|
|
109
|
+
this.queue.forEach(item => {
|
|
110
|
+
if (item.sign) {
|
|
111
|
+
this.onAdded.forEach(handler => {
|
|
112
|
+
handler(item.index, item.value);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
this.onRemoved.forEach(handler => {
|
|
117
|
+
handler(item.index, item.value);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
this.queue.splice(0);
|
|
122
|
+
this.frozen = false;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Disable the reactivity and enable the queue
|
|
126
|
+
*/
|
|
127
|
+
disableReactivity() {
|
|
128
|
+
this.frozen = true;
|
|
129
|
+
}
|
|
130
|
+
}
|