vasille 1.2.9 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/LICENSE.md +21 -0
  2. package/flow-typed/vasille.js +836 -0
  3. package/img/favicon.svg +17 -204
  4. package/lib/binding/attribute.js +32 -0
  5. package/lib/binding/binding.js +39 -0
  6. package/lib/binding/class.js +51 -0
  7. package/lib/binding/style.js +29 -0
  8. package/lib/core/core.js +178 -0
  9. package/lib/core/destroyable.js +45 -0
  10. package/lib/core/errors.js +16 -0
  11. package/lib/core/executor.js +154 -0
  12. package/lib/core/ivalue.js +56 -0
  13. package/lib/core/signal.js +50 -0
  14. package/lib/core/slot.js +47 -0
  15. package/lib/index.js +27 -22
  16. package/lib/models/array-model.js +208 -0
  17. package/lib/models/listener.js +130 -0
  18. package/lib/models/map-model.js +66 -0
  19. package/lib/models/model.js +1 -0
  20. package/lib/models/object-model.js +78 -0
  21. package/lib/models/set-model.js +62 -0
  22. package/lib/node/app.js +38 -0
  23. package/lib/node/interceptor.js +83 -0
  24. package/lib/node/node.js +1185 -0
  25. package/lib/node/watch.js +27 -0
  26. package/lib/value/expression.js +83 -0
  27. package/lib/value/mirror.js +58 -0
  28. package/lib/value/pointer.js +26 -0
  29. package/lib/value/reference.js +55 -0
  30. package/lib/views/array-view.js +24 -0
  31. package/lib/views/base-view.js +49 -0
  32. package/lib/views/map-view.js +20 -0
  33. package/lib/views/object-view.js +20 -0
  34. package/lib/views/repeat-node.js +106 -0
  35. package/lib/views/repeater.js +63 -0
  36. package/lib/views/set-view.js +23 -0
  37. package/package.json +16 -15
  38. package/types/binding/attribute.d.ts +23 -0
  39. package/types/binding/binding.d.ts +30 -0
  40. package/types/binding/class.d.ts +23 -0
  41. package/types/binding/style.d.ts +23 -0
  42. package/types/core/core.d.ts +140 -0
  43. package/types/core/destroyable.d.ts +15 -0
  44. package/types/core/errors.d.ts +4 -0
  45. package/types/core/executor.d.ts +87 -0
  46. package/types/core/ivalue.d.ts +45 -0
  47. package/types/core/signal.d.ts +35 -0
  48. package/types/core/slot.d.ts +45 -0
  49. package/types/index.d.ts +27 -21
  50. package/types/models/array-model.d.ts +103 -0
  51. package/types/models/listener.d.ts +74 -0
  52. package/types/models/map-model.d.ts +35 -0
  53. package/types/models/model.d.ts +19 -0
  54. package/types/models/object-model.d.ts +36 -0
  55. package/types/models/set-model.d.ts +34 -0
  56. package/types/node/app.d.ts +37 -0
  57. package/types/node/interceptor.d.ts +50 -0
  58. package/types/node/node.d.ts +739 -0
  59. package/types/node/watch.d.ts +23 -0
  60. package/types/value/expression.d.ts +60 -0
  61. package/types/value/mirror.d.ts +35 -0
  62. package/types/value/pointer.d.ts +19 -0
  63. package/types/value/reference.d.ts +30 -0
  64. package/types/views/array-view.d.ts +13 -0
  65. package/types/views/base-view.d.ts +44 -0
  66. package/types/views/map-view.d.ts +11 -0
  67. package/types/views/object-view.d.ts +11 -0
  68. package/types/views/repeat-node.d.ts +35 -0
  69. package/types/views/repeater.d.ts +38 -0
  70. package/types/views/set-view.d.ts +11 -0
  71. package/CHANGELOG.md +0 -23
  72. package/lib/attribute.js +0 -71
  73. package/lib/attribute.js.map +0 -1
  74. package/lib/bind.js +0 -286
  75. package/lib/bind.js.map +0 -1
  76. package/lib/class.js +0 -97
  77. package/lib/class.js.map +0 -1
  78. package/lib/executor.js +0 -167
  79. package/lib/executor.js.map +0 -1
  80. package/lib/index.js.map +0 -1
  81. package/lib/interfaces/core.js +0 -247
  82. package/lib/interfaces/core.js.map +0 -1
  83. package/lib/interfaces/destroyable.js +0 -39
  84. package/lib/interfaces/destroyable.js.map +0 -1
  85. package/lib/interfaces/errors.js +0 -49
  86. package/lib/interfaces/errors.js.map +0 -1
  87. package/lib/interfaces/ibind.js +0 -31
  88. package/lib/interfaces/ibind.js.map +0 -1
  89. package/lib/interfaces/idefinition.js +0 -64
  90. package/lib/interfaces/idefinition.js.map +0 -1
  91. package/lib/interfaces/ivalue.js +0 -60
  92. package/lib/interfaces/ivalue.js.map +0 -1
  93. package/lib/models.js +0 -579
  94. package/lib/models.js.map +0 -1
  95. package/lib/node.js +0 -2155
  96. package/lib/node.js.map +0 -1
  97. package/lib/property.js +0 -38
  98. package/lib/property.js.map +0 -1
  99. package/lib/style.js +0 -66
  100. package/lib/style.js.map +0 -1
  101. package/lib/value.js +0 -203
  102. package/lib/value.js.map +0 -1
  103. package/lib/views.js +0 -688
  104. package/lib/views.js.map +0 -1
  105. package/types/attribute.d.ts +0 -18
  106. package/types/bind.d.ts +0 -72
  107. package/types/class.d.ts +0 -19
  108. package/types/data.d.ts +0 -11
  109. package/types/event.d.ts +0 -10
  110. package/types/executor.d.ts +0 -57
  111. package/types/interfaces/core.d.ts +0 -129
  112. package/types/interfaces/destroyable.d.ts +0 -11
  113. package/types/interfaces/errors.d.ts +0 -24
  114. package/types/interfaces/ibind.d.ts +0 -19
  115. package/types/interfaces/idefinition.d.ts +0 -29
  116. package/types/interfaces/ivalue.d.ts +0 -40
  117. package/types/models.d.ts +0 -179
  118. package/types/node.d.ts +0 -906
  119. package/types/property.d.ts +0 -9
  120. package/types/style.d.ts +0 -28
  121. package/types/value.d.ts +0 -43
  122. package/types/views.d.ts +0 -135
@@ -0,0 +1,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
+ }
@@ -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 { Expression } from "./bind.js";
2
- import { Destroyable } from "./interfaces/destroyable.js";
3
- import { IBind } from "./interfaces/ibind.js";
4
- import { IValue } from "./interfaces/ivalue.js";
5
- import { ArrayModel, MapModel, ObjectModel, SetModel } from "./models.js";
6
- import { App, INode, Extension, Tag, TextNode, Component } from "./node.js";
7
- import { Pointer, Reference } from "./value.js";
8
- import { ArrayView, MapView, ObjectView, SetView } from "./views.js";
9
-
10
-
11
-
12
- export {
13
- Expression,
14
- Destroyable,
15
- IBind, IValue,
16
- ArrayModel, MapModel, ObjectModel, SetModel,
17
- App, INode, Tag, Extension, TextNode, Component,
18
- Pointer, Reference,
19
- ArrayView, MapView, ObjectView, SetView
20
- };
21
-
22
- //# sourceMappingURL=index.js.map
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
+ }