aberdeen 0.2.4 → 0.5.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.txt +1 -1
- package/README.md +140 -99
- package/dist/aberdeen.d.ts +649 -512
- package/dist/aberdeen.js +1147 -1704
- package/dist/aberdeen.js.map +11 -1
- package/dist/helpers/reverseSortedSet.d.ts +91 -0
- package/dist/prediction.d.ts +7 -3
- package/dist/prediction.js +77 -93
- package/dist/prediction.js.map +10 -1
- package/dist/route.d.ts +44 -13
- package/dist/route.js +138 -112
- package/dist/route.js.map +10 -1
- package/dist/transitions.d.ts +2 -2
- package/dist/transitions.js +30 -63
- package/dist/transitions.js.map +10 -1
- package/dist-min/aberdeen.js +7 -2
- package/dist-min/aberdeen.js.map +11 -1
- package/dist-min/prediction.js +4 -2
- package/dist-min/prediction.js.map +10 -1
- package/dist-min/route.js +4 -2
- package/dist-min/route.js.map +10 -1
- package/dist-min/transitions.js +4 -2
- package/dist-min/transitions.js.map +10 -1
- package/package.json +20 -23
- package/src/aberdeen.ts +1956 -1696
- package/src/helpers/reverseSortedSet.ts +188 -0
- package/src/prediction.ts +14 -9
- package/src/route.ts +149 -69
- package/src/transitions.ts +26 -43
- package/dist-min/aberdeen.d.ts +0 -573
- package/dist-min/prediction.d.ts +0 -29
- package/dist-min/route.d.ts +0 -16
- package/dist-min/transitions.d.ts +0 -18
package/dist/aberdeen.js
CHANGED
|
@@ -1,1756 +1,1199 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
// src/helpers/reverseSortedSet.ts
|
|
2
|
+
class ReverseSortedSet {
|
|
3
|
+
keyProp;
|
|
4
|
+
tail;
|
|
5
|
+
symbols;
|
|
6
|
+
constructor(keyProp) {
|
|
7
|
+
this.keyProp = keyProp;
|
|
8
|
+
this.tail = {};
|
|
9
|
+
this.symbols = [Symbol(0)];
|
|
10
|
+
}
|
|
11
|
+
add(item) {
|
|
12
|
+
if (this.symbols[0] in item)
|
|
13
|
+
return false;
|
|
14
|
+
const level = 1 + (Math.clz32(Math.random() * 4294967295) >> 2);
|
|
15
|
+
for (let l = this.symbols.length;l < level; l++)
|
|
16
|
+
this.symbols.push(Symbol(l));
|
|
17
|
+
const keyProp = this.keyProp;
|
|
18
|
+
const key = item[keyProp];
|
|
19
|
+
let prev;
|
|
20
|
+
let current = this.tail;
|
|
21
|
+
for (let l = this.symbols.length - 1;l >= 0; l--) {
|
|
22
|
+
const symbol = this.symbols[l];
|
|
23
|
+
while ((prev = current[symbol]) && prev[keyProp] > key)
|
|
24
|
+
current = prev;
|
|
25
|
+
if (l < level) {
|
|
26
|
+
item[symbol] = current[symbol];
|
|
27
|
+
current[symbol] = item;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
has(item) {
|
|
33
|
+
return this.symbols[0] in item;
|
|
34
|
+
}
|
|
35
|
+
fetchLast() {
|
|
36
|
+
let item = this.tail[this.symbols[0]];
|
|
37
|
+
if (item) {
|
|
38
|
+
this.remove(item);
|
|
39
|
+
return item;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
isEmpty() {
|
|
43
|
+
return this.tail[this.symbols[0]] === undefined;
|
|
44
|
+
}
|
|
45
|
+
get(indexValue) {
|
|
46
|
+
const keyProp = this.keyProp;
|
|
47
|
+
let current = this.tail;
|
|
48
|
+
let prev;
|
|
49
|
+
for (let l = this.symbols.length - 1;l >= 0; l--) {
|
|
50
|
+
const symbol = this.symbols[l];
|
|
51
|
+
while ((prev = current[symbol]) && prev[keyProp] > indexValue)
|
|
52
|
+
current = prev;
|
|
53
|
+
}
|
|
54
|
+
return current[this.symbols[0]]?.[keyProp] === indexValue ? current[this.symbols[0]] : undefined;
|
|
55
|
+
}
|
|
56
|
+
*[Symbol.iterator]() {
|
|
57
|
+
let symbol = this.symbols[0];
|
|
58
|
+
let node = this.tail[symbol];
|
|
59
|
+
while (node) {
|
|
60
|
+
yield node;
|
|
61
|
+
node = node[symbol];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
prev(item) {
|
|
65
|
+
return item[this.symbols[0]];
|
|
66
|
+
}
|
|
67
|
+
remove(item) {
|
|
68
|
+
if (!(this.symbols[0] in item))
|
|
69
|
+
return false;
|
|
70
|
+
const keyProp = this.keyProp;
|
|
71
|
+
const prop = item[keyProp];
|
|
72
|
+
let prev;
|
|
73
|
+
let current = this.tail;
|
|
74
|
+
for (let l = this.symbols.length - 1;l >= 0; l--) {
|
|
75
|
+
const symbol = this.symbols[l];
|
|
76
|
+
while ((prev = current[symbol]) && prev[keyProp] >= prop && prev !== item)
|
|
77
|
+
current = prev;
|
|
78
|
+
if (prev === item) {
|
|
79
|
+
current[symbol] = prev[symbol];
|
|
80
|
+
delete prev[symbol];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return prev === item;
|
|
84
|
+
}
|
|
85
|
+
clear() {
|
|
86
|
+
const symbol = this.symbols[0];
|
|
87
|
+
let current = this.tail;
|
|
88
|
+
while (current) {
|
|
89
|
+
const prev = current[symbol];
|
|
90
|
+
for (const symbol2 of this.symbols) {
|
|
91
|
+
if (!(symbol2 in current))
|
|
92
|
+
break;
|
|
93
|
+
delete current[symbol2];
|
|
94
|
+
}
|
|
95
|
+
current = prev;
|
|
96
|
+
}
|
|
97
|
+
this.tail = {};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// src/aberdeen.ts
|
|
102
|
+
var sortedQueue;
|
|
103
|
+
var runQueueDepth = 0;
|
|
104
|
+
var topRedrawScope;
|
|
6
105
|
function queue(runner) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
if (
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
queueArray.push(runner);
|
|
19
|
-
queueSet.add(runner);
|
|
106
|
+
if (!sortedQueue) {
|
|
107
|
+
sortedQueue = new ReverseSortedSet("prio");
|
|
108
|
+
setTimeout(runQueue, 0);
|
|
109
|
+
} else if (!(runQueueDepth & 1)) {
|
|
110
|
+
runQueueDepth++;
|
|
111
|
+
if (runQueueDepth > 98) {
|
|
112
|
+
throw new Error("Too many recursive updates from observes");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
sortedQueue.add(runner);
|
|
20
116
|
}
|
|
21
117
|
function runQueue() {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
queueSet.delete(runner);
|
|
37
|
-
runner._queueRun();
|
|
38
|
-
}
|
|
39
|
-
// If new items have been added to the queue while processing the previous
|
|
40
|
-
// batch, we'll need to run this loop again.
|
|
41
|
-
runQueueDepth++;
|
|
42
|
-
}
|
|
43
|
-
queueArray.length = 0;
|
|
44
|
-
queueIndex = undefined;
|
|
45
|
-
runQueueDepth = 0;
|
|
46
|
-
onCreateEnabled = false;
|
|
118
|
+
let time = Date.now();
|
|
119
|
+
while (sortedQueue) {
|
|
120
|
+
const runner = sortedQueue.fetchLast();
|
|
121
|
+
if (!runner)
|
|
122
|
+
break;
|
|
123
|
+
if (runQueueDepth & 1)
|
|
124
|
+
runQueueDepth++;
|
|
125
|
+
runner.queueRun();
|
|
126
|
+
}
|
|
127
|
+
sortedQueue = undefined;
|
|
128
|
+
runQueueDepth = 0;
|
|
129
|
+
time = Date.now() - time;
|
|
130
|
+
if (time > 1)
|
|
131
|
+
console.debug(`Aberdeen queue took ${time}ms`);
|
|
47
132
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
* Unlike `setTimeout` or `requestAnimationFrame`, this mechanism ensures that DOM read
|
|
61
|
-
* operations happen before any DOM writes in the same queue cycle, minimizing layout thrashing.
|
|
62
|
-
*
|
|
63
|
-
* @param func The function to be executed as a DOM read operation.
|
|
64
|
-
*/
|
|
65
|
-
export function scheduleDomReader(func) {
|
|
66
|
-
let order = (queueIndex != null && queueIndex < queueArray.length && queueArray[queueIndex]._queueOrder >= 1000) ? ((queueArray[queueIndex]._queueOrder + 1) & (~1)) : 1000;
|
|
67
|
-
queue({ _queueOrder: order, _queueRun: func });
|
|
133
|
+
function partToStr(part) {
|
|
134
|
+
if (typeof part === "string") {
|
|
135
|
+
return part + "\x01";
|
|
136
|
+
}
|
|
137
|
+
let result = "";
|
|
138
|
+
let num = Math.abs(Math.round(part));
|
|
139
|
+
const negative = part < 0;
|
|
140
|
+
while (num > 0) {
|
|
141
|
+
result += String.fromCharCode(negative ? 65534 - num % 65533 : 2 + num % 65533);
|
|
142
|
+
num = Math.floor(num / 65533);
|
|
143
|
+
}
|
|
144
|
+
return String.fromCharCode(128 + (negative ? -result.length : result.length)) + result;
|
|
68
145
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
*
|
|
76
|
-
* By batching DOM writes separately from DOM reads, this prevents the browser from
|
|
77
|
-
* interleaving layout reads and writes, which can force additional layout recalculations.
|
|
78
|
-
* This helps reduce visual glitches and flashes by ensuring the browser doesn't render
|
|
79
|
-
* intermediate DOM states during updates.
|
|
80
|
-
*
|
|
81
|
-
* Unlike `setTimeout` or `requestAnimationFrame`, this mechanism ensures that DOM write
|
|
82
|
-
* operations happen after all DOM reads in the same queue cycle, minimizing layout thrashing.
|
|
83
|
-
*
|
|
84
|
-
* @param func The function to be executed as a DOM write operation.
|
|
85
|
-
*/
|
|
86
|
-
export function scheduleDomWriter(func) {
|
|
87
|
-
let order = (queueIndex != null && queueIndex < queueArray.length && queueArray[queueIndex]._queueOrder >= 1000) ? (queueArray[queueIndex]._queueOrder | 1) : 1001;
|
|
88
|
-
queue({ _queueOrder: order, _queueRun: func });
|
|
146
|
+
function invertString(input) {
|
|
147
|
+
let result = "";
|
|
148
|
+
for (let i = 0;i < input.length; i++) {
|
|
149
|
+
result += String.fromCodePoint(65535 - input.charCodeAt(i));
|
|
150
|
+
}
|
|
151
|
+
return result;
|
|
89
152
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
return partToStr(key);
|
|
101
|
-
}
|
|
153
|
+
var lastPrio = 0;
|
|
154
|
+
|
|
155
|
+
class Scope {
|
|
156
|
+
prio = --lastPrio;
|
|
157
|
+
remove() {
|
|
158
|
+
const lastNode = this.getLastNode();
|
|
159
|
+
if (lastNode)
|
|
160
|
+
removeNodes(lastNode, this.getPrecedingNode());
|
|
161
|
+
this.delete();
|
|
162
|
+
}
|
|
102
163
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
164
|
+
|
|
165
|
+
class ContentScope extends Scope {
|
|
166
|
+
cleaners;
|
|
167
|
+
constructor(cleaners = []) {
|
|
168
|
+
super();
|
|
169
|
+
this.cleaners = cleaners;
|
|
170
|
+
}
|
|
171
|
+
lastChild;
|
|
172
|
+
redraw() {}
|
|
173
|
+
getLastNode() {
|
|
174
|
+
return findLastNodeInPrevSiblings(this.lastChild);
|
|
175
|
+
}
|
|
176
|
+
delete() {
|
|
177
|
+
for (let cleaner of this.cleaners) {
|
|
178
|
+
if (typeof cleaner === "function")
|
|
179
|
+
cleaner();
|
|
180
|
+
else
|
|
181
|
+
cleaner.delete(this);
|
|
182
|
+
}
|
|
183
|
+
this.cleaners.length = 0;
|
|
184
|
+
sortedQueue?.remove(this);
|
|
185
|
+
this.lastChild = undefined;
|
|
186
|
+
}
|
|
187
|
+
queueRun() {
|
|
188
|
+
this.remove();
|
|
189
|
+
topRedrawScope = this;
|
|
190
|
+
this.redraw();
|
|
191
|
+
topRedrawScope = undefined;
|
|
192
|
+
}
|
|
193
|
+
getInsertAfterNode() {
|
|
194
|
+
return this.getLastNode() || this.getPrecedingNode();
|
|
195
|
+
}
|
|
196
|
+
onChange(index, newData, oldData) {
|
|
197
|
+
queue(this);
|
|
198
|
+
}
|
|
199
|
+
getChildPrevSibling() {
|
|
200
|
+
return this.lastChild;
|
|
201
|
+
}
|
|
112
202
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
203
|
+
|
|
204
|
+
class ChainedScope extends ContentScope {
|
|
205
|
+
parentElement;
|
|
206
|
+
prevSibling;
|
|
207
|
+
constructor(parentElement, useParentCleaners = false) {
|
|
208
|
+
super(useParentCleaners ? currentScope.cleaners : []);
|
|
209
|
+
this.parentElement = parentElement;
|
|
210
|
+
if (parentElement === currentScope.parentElement) {
|
|
211
|
+
this.prevSibling = currentScope.getChildPrevSibling();
|
|
212
|
+
currentScope.lastChild = this;
|
|
213
|
+
}
|
|
214
|
+
if (!useParentCleaners)
|
|
215
|
+
currentScope.cleaners.push(this);
|
|
216
|
+
}
|
|
217
|
+
getPrecedingNode() {
|
|
218
|
+
return findLastNodeInPrevSiblings(this.prevSibling);
|
|
219
|
+
}
|
|
220
|
+
getChildPrevSibling() {
|
|
221
|
+
return this.lastChild || this.prevSibling;
|
|
222
|
+
}
|
|
126
223
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
this._cleaners = [];
|
|
143
|
-
// Set to true after the scope has been cleaned, causing any spurious reruns to
|
|
144
|
-
// be ignored.
|
|
145
|
-
this._isDead = false;
|
|
146
|
-
this._parentElement = parentElement;
|
|
147
|
-
this._precedingSibling = precedingSibling;
|
|
148
|
-
this._queueOrder = queueOrder;
|
|
149
|
-
}
|
|
150
|
-
// Get a reference to the last Node preceding this Scope, or undefined if there is none
|
|
151
|
-
_findPrecedingNode(stopAt = undefined) {
|
|
152
|
-
let cur = this;
|
|
153
|
-
let pre;
|
|
154
|
-
while ((pre = cur._precedingSibling) && pre !== stopAt) {
|
|
155
|
-
if (pre instanceof Node)
|
|
156
|
-
return pre;
|
|
157
|
-
let node = pre._findLastNode();
|
|
158
|
-
if (node)
|
|
159
|
-
return node;
|
|
160
|
-
cur = pre;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
// Get a reference to the last Node within this scope and parentElement
|
|
164
|
-
_findLastNode() {
|
|
165
|
-
if (this._lastChild) {
|
|
166
|
-
if (this._lastChild instanceof Node)
|
|
167
|
-
return this._lastChild;
|
|
168
|
-
else
|
|
169
|
-
return this._lastChild._findLastNode() || this._lastChild._findPrecedingNode(this._precedingSibling);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
_addNode(node) {
|
|
173
|
-
if (!this._parentElement)
|
|
174
|
-
throw new ScopeError(true);
|
|
175
|
-
let prevNode = this._findLastNode() || this._findPrecedingNode();
|
|
176
|
-
this._parentElement.insertBefore(node, prevNode ? prevNode.nextSibling : this._parentElement.firstChild);
|
|
177
|
-
this._lastChild = node;
|
|
178
|
-
}
|
|
179
|
-
_remove() {
|
|
180
|
-
if (this._parentElement) {
|
|
181
|
-
let lastNode = this._findLastNode();
|
|
182
|
-
if (lastNode) {
|
|
183
|
-
// at least one DOM node to be removed
|
|
184
|
-
let nextNode = this._findPrecedingNode();
|
|
185
|
-
nextNode = (nextNode ? nextNode.nextSibling : this._parentElement.firstChild);
|
|
186
|
-
this._lastChild = undefined;
|
|
187
|
-
// Keep removing DOM nodes starting at our first node, until we encounter the last node
|
|
188
|
-
while (true) {
|
|
189
|
-
/* c8 ignore next */
|
|
190
|
-
if (!nextNode)
|
|
191
|
-
return internalError(1);
|
|
192
|
-
const node = nextNode;
|
|
193
|
-
nextNode = node.nextSibling || undefined;
|
|
194
|
-
let onDestroy = onDestroyMap.get(node);
|
|
195
|
-
if (onDestroy && node instanceof Element) {
|
|
196
|
-
if (onDestroy !== true) {
|
|
197
|
-
if (typeof onDestroy === 'function') {
|
|
198
|
-
onDestroy(node);
|
|
199
|
-
}
|
|
200
|
-
else {
|
|
201
|
-
destroyWithClass(node, onDestroy);
|
|
202
|
-
}
|
|
203
|
-
// This causes the element to be ignored from this function from now on:
|
|
204
|
-
onDestroyMap.set(node, true);
|
|
205
|
-
}
|
|
206
|
-
// Ignore the deleting element
|
|
207
|
-
}
|
|
208
|
-
else {
|
|
209
|
-
this._parentElement.removeChild(node);
|
|
210
|
-
}
|
|
211
|
-
if (node === lastNode)
|
|
212
|
-
break;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
// run cleaners
|
|
217
|
-
this._clean();
|
|
218
|
-
}
|
|
219
|
-
_clean() {
|
|
220
|
-
this._isDead = true;
|
|
221
|
-
for (let cleaner of this._cleaners) {
|
|
222
|
-
cleaner._clean(this);
|
|
223
|
-
}
|
|
224
|
-
this._cleaners.length = 0;
|
|
225
|
-
}
|
|
226
|
-
_onChange(index, newData, oldData) {
|
|
227
|
-
queue(this);
|
|
224
|
+
|
|
225
|
+
class RegularScope extends ChainedScope {
|
|
226
|
+
renderer;
|
|
227
|
+
constructor(parentElement, renderer) {
|
|
228
|
+
super(parentElement);
|
|
229
|
+
this.renderer = renderer;
|
|
230
|
+
this.redraw();
|
|
231
|
+
}
|
|
232
|
+
redraw() {
|
|
233
|
+
let savedScope = currentScope;
|
|
234
|
+
currentScope = this;
|
|
235
|
+
try {
|
|
236
|
+
this.renderer();
|
|
237
|
+
} catch (e) {
|
|
238
|
+
handleError(e, true);
|
|
228
239
|
}
|
|
240
|
+
currentScope = savedScope;
|
|
241
|
+
}
|
|
229
242
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
/* c8 ignore next */
|
|
237
|
-
if (currentScope)
|
|
238
|
-
internalError(2);
|
|
239
|
-
if (this._isDead)
|
|
240
|
-
return;
|
|
241
|
-
this._remove();
|
|
242
|
-
this._isDead = false;
|
|
243
|
-
this._update();
|
|
244
|
-
}
|
|
245
|
-
_update() {
|
|
246
|
-
let savedScope = currentScope;
|
|
247
|
-
currentScope = this;
|
|
248
|
-
try {
|
|
249
|
-
this._renderer();
|
|
250
|
-
}
|
|
251
|
-
catch (e) {
|
|
252
|
-
// Throw the error async, so the rest of the rendering can continue
|
|
253
|
-
handleError(e);
|
|
254
|
-
}
|
|
255
|
-
currentScope = savedScope;
|
|
256
|
-
}
|
|
243
|
+
|
|
244
|
+
class RootScope extends ContentScope {
|
|
245
|
+
parentElement = document.body;
|
|
246
|
+
getPrecedingNode() {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
257
249
|
}
|
|
258
|
-
|
|
259
|
-
class
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
250
|
+
|
|
251
|
+
class MountScope extends ContentScope {
|
|
252
|
+
parentElement;
|
|
253
|
+
renderer;
|
|
254
|
+
constructor(parentElement, renderer) {
|
|
255
|
+
super();
|
|
256
|
+
this.parentElement = parentElement;
|
|
257
|
+
this.renderer = renderer;
|
|
258
|
+
this.redraw();
|
|
259
|
+
currentScope.cleaners.push(this);
|
|
260
|
+
}
|
|
261
|
+
redraw() {
|
|
262
|
+
RegularScope.prototype.redraw.call(this);
|
|
263
|
+
}
|
|
264
|
+
getPrecedingNode() {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
delete() {
|
|
268
|
+
removeNodes(this.getLastNode(), this.getPrecedingNode());
|
|
269
|
+
super.delete();
|
|
270
|
+
}
|
|
271
|
+
remove() {
|
|
272
|
+
this.delete();
|
|
273
|
+
}
|
|
263
274
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
}
|
|
283
|
-
finally {
|
|
284
|
-
currentScope = savedScope;
|
|
285
|
-
immediateQueuerRunning = false;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
275
|
+
function removeNodes(node, preNode) {
|
|
276
|
+
while (node && node !== preNode) {
|
|
277
|
+
const prevNode = node.previousSibling;
|
|
278
|
+
let onDestroy = onDestroyMap.get(node);
|
|
279
|
+
if (onDestroy && node instanceof Element) {
|
|
280
|
+
if (onDestroy !== true) {
|
|
281
|
+
if (typeof onDestroy === "function") {
|
|
282
|
+
onDestroy(node);
|
|
283
|
+
} else {
|
|
284
|
+
destroyWithClass(node, onDestroy);
|
|
285
|
+
}
|
|
286
|
+
onDestroyMap.set(node, true);
|
|
287
|
+
}
|
|
288
|
+
} else {
|
|
289
|
+
node.remove();
|
|
290
|
+
}
|
|
291
|
+
node = prevNode;
|
|
292
|
+
}
|
|
288
293
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
294
|
+
function findLastNodeInPrevSiblings(sibling) {
|
|
295
|
+
if (!sibling || sibling instanceof Node)
|
|
296
|
+
return sibling;
|
|
297
|
+
return sibling.getLastNode() || sibling.getPrecedingNode();
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
class ResultScope extends ChainedScope {
|
|
301
|
+
renderer;
|
|
302
|
+
result = optProxy({ value: undefined });
|
|
303
|
+
constructor(parentElement, renderer) {
|
|
304
|
+
super(parentElement);
|
|
305
|
+
this.renderer = renderer;
|
|
306
|
+
this.redraw();
|
|
307
|
+
}
|
|
308
|
+
redraw() {
|
|
309
|
+
let savedScope = currentScope;
|
|
310
|
+
currentScope = this;
|
|
311
|
+
try {
|
|
312
|
+
this.result.value = this.renderer();
|
|
313
|
+
} catch (e) {
|
|
314
|
+
handleError(e, true);
|
|
308
315
|
}
|
|
309
|
-
|
|
310
|
-
|
|
316
|
+
currentScope = savedScope;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
class SetArgScope extends ChainedScope {
|
|
321
|
+
key;
|
|
322
|
+
target;
|
|
323
|
+
constructor(parentElement, key, target) {
|
|
324
|
+
super(parentElement);
|
|
325
|
+
this.key = key;
|
|
326
|
+
this.target = target;
|
|
327
|
+
this.redraw();
|
|
328
|
+
}
|
|
329
|
+
redraw() {
|
|
330
|
+
let savedScope = currentScope;
|
|
331
|
+
currentScope = this;
|
|
332
|
+
applyArg(this.key, this.target.value);
|
|
333
|
+
currentScope = savedScope;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
var immediateQueue = new ReverseSortedSet("prio");
|
|
337
|
+
|
|
338
|
+
class ImmediateScope extends RegularScope {
|
|
339
|
+
onChange(index, newData, oldData) {
|
|
340
|
+
immediateQueue.add(this);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
var immediateQueueRunning = false;
|
|
344
|
+
function runImmediateQueue() {
|
|
345
|
+
for (let count = 0;!immediateQueue.isEmpty() && !immediateQueueRunning; count++) {
|
|
346
|
+
if (count > 42) {
|
|
347
|
+
immediateQueue.clear();
|
|
348
|
+
throw new Error("Too many immediate-mode recursive updates");
|
|
349
|
+
}
|
|
350
|
+
immediateQueueRunning = true;
|
|
351
|
+
let copy = immediateQueue;
|
|
352
|
+
immediateQueue = new ReverseSortedSet("prio");
|
|
353
|
+
try {
|
|
354
|
+
for (const scope of copy) {
|
|
355
|
+
scope.queueRun();
|
|
356
|
+
}
|
|
357
|
+
} finally {
|
|
358
|
+
immediateQueueRunning = false;
|
|
311
359
|
}
|
|
360
|
+
}
|
|
312
361
|
}
|
|
313
|
-
|
|
362
|
+
|
|
314
363
|
class OnEachScope extends Scope {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
this._byIndex.set(itemIndex, scope);
|
|
386
|
-
scope._update();
|
|
387
|
-
// We're not adding a cleaner here, as we'll be calling them from our _clean function
|
|
388
|
-
}
|
|
389
|
-
_removeChild(itemIndex) {
|
|
390
|
-
let scope = this._byIndex.get(itemIndex);
|
|
391
|
-
/* c8 ignore next */
|
|
392
|
-
if (!scope)
|
|
393
|
-
return internalError(6);
|
|
394
|
-
scope._remove();
|
|
395
|
-
this._byIndex.delete(itemIndex);
|
|
396
|
-
this._removeFromPosition(scope);
|
|
397
|
-
}
|
|
398
|
-
_findPosition(sortStr) {
|
|
399
|
-
// In case of duplicate `sortStr`s, this will return the first match.
|
|
400
|
-
let items = this._byPosition;
|
|
401
|
-
let min = 0, max = items.length;
|
|
402
|
-
// Fast-path for elements that are already ordered (as is the case when working with arrays ordered by index)
|
|
403
|
-
if (!max || sortStr > items[max - 1]._sortStr)
|
|
404
|
-
return max;
|
|
405
|
-
// Binary search for the insert position
|
|
406
|
-
while (min < max) {
|
|
407
|
-
let mid = (min + max) >> 1;
|
|
408
|
-
if (items[mid]._sortStr < sortStr) {
|
|
409
|
-
min = mid + 1;
|
|
410
|
-
}
|
|
411
|
-
else {
|
|
412
|
-
max = mid;
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
return min;
|
|
416
|
-
}
|
|
417
|
-
_insertAtPosition(child) {
|
|
418
|
-
let pos = this._findPosition(child._sortStr);
|
|
419
|
-
this._byPosition.splice(pos, 0, child);
|
|
420
|
-
// Based on the position in the list, set the precedingSibling for the new Scope
|
|
421
|
-
// and for the next sibling.
|
|
422
|
-
let nextSibling = this._byPosition[pos + 1];
|
|
423
|
-
if (nextSibling) {
|
|
424
|
-
child._precedingSibling = nextSibling._precedingSibling;
|
|
425
|
-
nextSibling._precedingSibling = child;
|
|
426
|
-
}
|
|
427
|
-
else {
|
|
428
|
-
child._precedingSibling = this._lastChild || this._precedingSibling;
|
|
429
|
-
this._lastChild = child;
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
_removeFromPosition(child) {
|
|
433
|
-
if (child._sortStr === '')
|
|
434
|
-
return;
|
|
435
|
-
let pos = this._findPosition(child._sortStr);
|
|
436
|
-
while (true) {
|
|
437
|
-
if (this._byPosition[pos] === child) {
|
|
438
|
-
// Yep, this is the right scope
|
|
439
|
-
this._byPosition.splice(pos, 1);
|
|
440
|
-
if (pos < this._byPosition.length) {
|
|
441
|
-
let nextSibling = this._byPosition[pos];
|
|
442
|
-
/* c8 ignore next */
|
|
443
|
-
if (!nextSibling)
|
|
444
|
-
return internalError(8);
|
|
445
|
-
/* c8 ignore next */
|
|
446
|
-
if (nextSibling._precedingSibling !== child)
|
|
447
|
-
return internalError(13);
|
|
448
|
-
nextSibling._precedingSibling = child._precedingSibling;
|
|
449
|
-
}
|
|
450
|
-
else {
|
|
451
|
-
/* c8 ignore next */
|
|
452
|
-
if (child !== this._lastChild)
|
|
453
|
-
return internalError(12);
|
|
454
|
-
this._lastChild = child._precedingSibling === this._precedingSibling ? undefined : child._precedingSibling;
|
|
455
|
-
}
|
|
456
|
-
return;
|
|
457
|
-
}
|
|
458
|
-
// There may be another Scope with the same sortStr
|
|
459
|
-
/* c8 ignore next */
|
|
460
|
-
if (++pos >= this._byPosition.length || this._byPosition[pos]._sortStr !== child._sortStr)
|
|
461
|
-
return internalError(5);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
364
|
+
renderer;
|
|
365
|
+
makeSortKey;
|
|
366
|
+
parentElement = currentScope.parentElement;
|
|
367
|
+
prevSibling;
|
|
368
|
+
target;
|
|
369
|
+
byIndex = new Map;
|
|
370
|
+
sortedSet = new ReverseSortedSet("sortKey");
|
|
371
|
+
changedIndexes = new Set;
|
|
372
|
+
constructor(proxy, renderer, makeSortKey) {
|
|
373
|
+
super();
|
|
374
|
+
this.renderer = renderer;
|
|
375
|
+
this.makeSortKey = makeSortKey;
|
|
376
|
+
const target = this.target = proxy[TARGET_SYMBOL] || proxy;
|
|
377
|
+
subscribe(target, ANY_SYMBOL, this);
|
|
378
|
+
this.prevSibling = currentScope.getChildPrevSibling();
|
|
379
|
+
currentScope.lastChild = this;
|
|
380
|
+
currentScope.cleaners.push(this);
|
|
381
|
+
if (target instanceof Array) {
|
|
382
|
+
for (let i = 0;i < target.length; i++) {
|
|
383
|
+
if (target[i] !== undefined) {
|
|
384
|
+
new OnEachItemScope(this, i, false);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
} else {
|
|
388
|
+
for (const key in target) {
|
|
389
|
+
if (target[key] !== undefined) {
|
|
390
|
+
new OnEachItemScope(this, key, false);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
getPrecedingNode() {
|
|
396
|
+
return findLastNodeInPrevSiblings(this.prevSibling);
|
|
397
|
+
}
|
|
398
|
+
onChange(index, newData, oldData) {
|
|
399
|
+
if (!(this.target instanceof Array) || typeof index === "number")
|
|
400
|
+
this.changedIndexes.add(index);
|
|
401
|
+
queue(this);
|
|
402
|
+
}
|
|
403
|
+
queueRun() {
|
|
404
|
+
let indexes = this.changedIndexes;
|
|
405
|
+
this.changedIndexes = new Set;
|
|
406
|
+
for (let index of indexes) {
|
|
407
|
+
const oldScope = this.byIndex.get(index);
|
|
408
|
+
if (oldScope)
|
|
409
|
+
oldScope.remove();
|
|
410
|
+
if (this.target[index] === undefined) {
|
|
411
|
+
this.byIndex.delete(index);
|
|
412
|
+
} else {
|
|
413
|
+
new OnEachItemScope(this, index, true);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
topRedrawScope = undefined;
|
|
417
|
+
}
|
|
418
|
+
delete() {
|
|
419
|
+
for (const scope of this.byIndex.values()) {
|
|
420
|
+
scope.delete();
|
|
421
|
+
}
|
|
422
|
+
this.byIndex.clear();
|
|
423
|
+
setTimeout(() => {
|
|
424
|
+
this.sortedSet.clear();
|
|
425
|
+
}, 1);
|
|
426
|
+
}
|
|
427
|
+
getLastNode() {
|
|
428
|
+
for (let scope of this.sortedSet) {
|
|
429
|
+
const node = scope.getActualLastNode();
|
|
430
|
+
if (node)
|
|
431
|
+
return node;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
464
434
|
}
|
|
465
|
-
|
|
466
|
-
class OnEachItemScope extends
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
435
|
+
|
|
436
|
+
class OnEachItemScope extends ContentScope {
|
|
437
|
+
parent;
|
|
438
|
+
itemIndex;
|
|
439
|
+
sortKey;
|
|
440
|
+
parentElement;
|
|
441
|
+
constructor(parent, itemIndex, topRedraw) {
|
|
442
|
+
super();
|
|
443
|
+
this.parent = parent;
|
|
444
|
+
this.itemIndex = itemIndex;
|
|
445
|
+
this.parentElement = parent.parentElement;
|
|
446
|
+
this.parent.byIndex.set(this.itemIndex, this);
|
|
447
|
+
this.lastChild = this;
|
|
448
|
+
if (topRedraw)
|
|
449
|
+
topRedrawScope = this;
|
|
450
|
+
this.redraw();
|
|
451
|
+
}
|
|
452
|
+
getPrecedingNode() {
|
|
453
|
+
this.parent.sortedSet.add(this);
|
|
454
|
+
const preScope = this.parent.sortedSet.prev(this);
|
|
455
|
+
if (preScope)
|
|
456
|
+
return findLastNodeInPrevSiblings(preScope.lastChild);
|
|
457
|
+
return this.parent.getPrecedingNode();
|
|
458
|
+
}
|
|
459
|
+
getLastNode() {
|
|
460
|
+
return this.getPrecedingNode();
|
|
461
|
+
}
|
|
462
|
+
getActualLastNode() {
|
|
463
|
+
let child = this.lastChild;
|
|
464
|
+
while (child && child !== this) {
|
|
465
|
+
if (child instanceof Node)
|
|
466
|
+
return child;
|
|
467
|
+
const node = child.getLastNode();
|
|
468
|
+
if (node)
|
|
469
|
+
return node;
|
|
470
|
+
child = child.getPrecedingNode();
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
queueRun() {
|
|
474
|
+
if (currentScope !== ROOT_SCOPE)
|
|
475
|
+
internalError(4);
|
|
476
|
+
if (this.sortKey !== undefined) {
|
|
477
|
+
const lastNode = this.getActualLastNode();
|
|
478
|
+
if (lastNode)
|
|
479
|
+
removeNodes(lastNode, this.getPrecedingNode());
|
|
480
|
+
}
|
|
481
|
+
this.delete();
|
|
482
|
+
this.lastChild = this;
|
|
483
|
+
topRedrawScope = this;
|
|
484
|
+
this.redraw();
|
|
485
|
+
topRedrawScope = undefined;
|
|
486
|
+
}
|
|
487
|
+
redraw() {
|
|
488
|
+
const value = optProxy(this.parent.target[this.itemIndex]);
|
|
489
|
+
let savedScope = currentScope;
|
|
490
|
+
currentScope = this;
|
|
491
|
+
let sortKey;
|
|
492
|
+
try {
|
|
493
|
+
if (this.parent.makeSortKey) {
|
|
494
|
+
let rawSortKey = this.parent.makeSortKey(value, this.itemIndex);
|
|
495
|
+
if (rawSortKey != null)
|
|
496
|
+
sortKey = rawSortKey instanceof Array ? rawSortKey.map(partToStr).join("") : rawSortKey;
|
|
497
|
+
} else {
|
|
498
|
+
sortKey = this.itemIndex;
|
|
499
|
+
}
|
|
500
|
+
if (typeof sortKey === "number")
|
|
501
|
+
sortKey = partToStr(sortKey);
|
|
502
|
+
if (this.sortKey !== sortKey) {
|
|
503
|
+
this.parent.sortedSet.remove(this);
|
|
504
|
+
this.sortKey = sortKey;
|
|
505
|
+
}
|
|
506
|
+
if (sortKey != null)
|
|
507
|
+
this.parent.renderer(value, this.itemIndex);
|
|
508
|
+
} catch (e) {
|
|
509
|
+
handleError(e, sortKey != null);
|
|
510
|
+
}
|
|
511
|
+
currentScope = savedScope;
|
|
512
|
+
}
|
|
513
|
+
getInsertAfterNode() {
|
|
514
|
+
if (this.sortKey == null)
|
|
515
|
+
internalError(1);
|
|
516
|
+
return findLastNodeInPrevSiblings(this.lastChild);
|
|
517
|
+
}
|
|
518
|
+
remove() {
|
|
519
|
+
if (this.sortKey !== undefined) {
|
|
520
|
+
const lastNode = this.getActualLastNode();
|
|
521
|
+
if (lastNode)
|
|
522
|
+
removeNodes(lastNode, this.getPrecedingNode());
|
|
523
|
+
this.parent.sortedSet.remove(this);
|
|
524
|
+
this.sortKey = undefined;
|
|
525
|
+
}
|
|
526
|
+
this.delete();
|
|
527
|
+
}
|
|
518
528
|
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
/**
|
|
525
|
-
* A special Node observer index to subscribe to any value in the map changing.
|
|
526
|
-
*/
|
|
527
|
-
const ANY_INDEX = {};
|
|
528
|
-
/** @internal */
|
|
529
|
-
export class ObsCollection {
|
|
530
|
-
constructor() {
|
|
531
|
-
this._observers = new Map();
|
|
532
|
-
}
|
|
533
|
-
// toString(): string {
|
|
534
|
-
// return JSON.stringify(peek(() => this.getRecursive(3)))
|
|
535
|
-
// }
|
|
536
|
-
_addObserver(index, observer) {
|
|
537
|
-
observer = observer;
|
|
538
|
-
let obsSet = this._observers.get(index);
|
|
539
|
-
if (obsSet) {
|
|
540
|
-
if (obsSet.has(observer))
|
|
541
|
-
return false;
|
|
542
|
-
obsSet.add(observer);
|
|
543
|
-
}
|
|
544
|
-
else {
|
|
545
|
-
this._observers.set(index, new Set([observer]));
|
|
546
|
-
}
|
|
547
|
-
return true;
|
|
548
|
-
}
|
|
549
|
-
_removeObserver(index, observer) {
|
|
550
|
-
let obsSet = this._observers.get(index);
|
|
551
|
-
obsSet.delete(observer);
|
|
552
|
-
}
|
|
553
|
-
emitChange(index, newData, oldData) {
|
|
554
|
-
let obsSet = this._observers.get(index);
|
|
555
|
-
if (obsSet)
|
|
556
|
-
obsSet.forEach(observer => observer._onChange(index, newData, oldData));
|
|
557
|
-
obsSet = this._observers.get(ANY_INDEX);
|
|
558
|
-
if (obsSet)
|
|
559
|
-
obsSet.forEach(observer => observer._onChange(index, newData, oldData));
|
|
560
|
-
}
|
|
561
|
-
_clean(observer) {
|
|
562
|
-
this._removeObserver(ANY_INDEX, observer);
|
|
563
|
-
}
|
|
564
|
-
_setIndex(index, newValue, deleteMissing) {
|
|
565
|
-
const curData = this.rawGet(index);
|
|
566
|
-
if (!(curData instanceof ObsCollection) || newValue instanceof Store || !curData._merge(newValue, deleteMissing)) {
|
|
567
|
-
let newData = valueToData(newValue);
|
|
568
|
-
if (newData !== curData) {
|
|
569
|
-
this.rawSet(index, newData);
|
|
570
|
-
this.emitChange(index, newData, curData);
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
}
|
|
529
|
+
function addNode(node) {
|
|
530
|
+
const parentEl = currentScope.parentElement;
|
|
531
|
+
const prevNode = currentScope.getInsertAfterNode();
|
|
532
|
+
parentEl.insertBefore(node, prevNode ? prevNode.nextSibling : parentEl.firstChild);
|
|
533
|
+
currentScope.lastChild = node;
|
|
574
534
|
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
throw new Error(`Invalid array index ${JSON.stringify(index)}`);
|
|
603
|
-
}
|
|
604
|
-
this._data[index] = newData;
|
|
605
|
-
// Remove trailing `undefined`s
|
|
606
|
-
while (this._data.length > 0 && this._data[this._data.length - 1] === undefined) {
|
|
607
|
-
this._data.pop();
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
_merge(newValue, deleteMissing) {
|
|
611
|
-
if (!(newValue instanceof Array)) {
|
|
612
|
-
return false;
|
|
613
|
-
}
|
|
614
|
-
// newValue is an array
|
|
615
|
-
for (let i = 0; i < newValue.length; i++) {
|
|
616
|
-
this._setIndex(i, newValue[i], deleteMissing);
|
|
617
|
-
}
|
|
618
|
-
// Overwriting just the first elements of an array and leaving the rest of
|
|
619
|
-
// the old data in place is just weird and unexpected, so we'll always use
|
|
620
|
-
// 'replace' behavior for arrays.
|
|
621
|
-
if ( /*deleteMissing &&*/this._data.length > newValue.length) {
|
|
622
|
-
for (let i = newValue.length; i < this._data.length; i++) {
|
|
623
|
-
let old = this._data[i];
|
|
624
|
-
if (old !== undefined) {
|
|
625
|
-
this.emitChange(i, undefined, old);
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
this._data.length = newValue.length;
|
|
629
|
-
}
|
|
630
|
-
return true;
|
|
631
|
-
}
|
|
632
|
-
_iterateIndexes(scope) {
|
|
633
|
-
for (let i = 0; i < this._data.length; i++) {
|
|
634
|
-
if (this._data[i] !== undefined) {
|
|
635
|
-
scope._addChild(i);
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
_normalizeIndex(index) {
|
|
640
|
-
if (typeof index === 'number')
|
|
641
|
-
return index;
|
|
642
|
-
if (typeof index === 'string') {
|
|
643
|
-
// Convert to int
|
|
644
|
-
let num = 0 | index;
|
|
645
|
-
// Check if the number is still the same after conversion
|
|
646
|
-
if (index.length && num == index)
|
|
647
|
-
return index;
|
|
648
|
-
}
|
|
649
|
-
throw new Error(`Invalid array index ${JSON.stringify(index)}`);
|
|
650
|
-
}
|
|
651
|
-
_getCount() {
|
|
652
|
-
return this._data.length;
|
|
653
|
-
}
|
|
535
|
+
var ROOT_SCOPE = new RootScope;
|
|
536
|
+
var currentScope = ROOT_SCOPE;
|
|
537
|
+
var ANY_SYMBOL = Symbol("any");
|
|
538
|
+
var TARGET_SYMBOL = Symbol("target");
|
|
539
|
+
var subscribers = new WeakMap;
|
|
540
|
+
var peeking = 0;
|
|
541
|
+
function subscribe(target, index, observer = currentScope) {
|
|
542
|
+
if (observer === ROOT_SCOPE || peeking)
|
|
543
|
+
return;
|
|
544
|
+
let byTarget = subscribers.get(target);
|
|
545
|
+
if (!byTarget)
|
|
546
|
+
subscribers.set(target, byTarget = new Map);
|
|
547
|
+
if (index !== ANY_SYMBOL && byTarget.get(ANY_SYMBOL)?.has(observer))
|
|
548
|
+
return;
|
|
549
|
+
let byIndex = byTarget.get(index);
|
|
550
|
+
if (!byIndex)
|
|
551
|
+
byTarget.set(index, byIndex = new Set);
|
|
552
|
+
if (byIndex.has(observer))
|
|
553
|
+
return;
|
|
554
|
+
byIndex.add(observer);
|
|
555
|
+
if (observer === currentScope) {
|
|
556
|
+
currentScope.cleaners.push(byIndex);
|
|
557
|
+
} else {
|
|
558
|
+
currentScope.cleaners.push(function() {
|
|
559
|
+
byIndex.delete(observer);
|
|
560
|
+
});
|
|
561
|
+
}
|
|
654
562
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
}
|
|
661
|
-
_getType() {
|
|
662
|
-
return "map";
|
|
663
|
-
}
|
|
664
|
-
_getRecursive(depth) {
|
|
665
|
-
if (currentScope) {
|
|
666
|
-
if (this._addObserver(ANY_INDEX, currentScope)) {
|
|
667
|
-
currentScope._cleaners.push(this);
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
let result = new Map();
|
|
671
|
-
this.data.forEach((v, k) => {
|
|
672
|
-
result.set(k, (v instanceof ObsCollection) ? (depth ? v._getRecursive(depth - 1) : new Store(this, k)) : v);
|
|
673
|
-
});
|
|
674
|
-
return result;
|
|
675
|
-
}
|
|
676
|
-
rawGet(index) {
|
|
677
|
-
return this.data.get(index);
|
|
678
|
-
}
|
|
679
|
-
rawSet(index, newData) {
|
|
680
|
-
if (newData === undefined) {
|
|
681
|
-
this.data.delete(index);
|
|
682
|
-
}
|
|
683
|
-
else {
|
|
684
|
-
this.data.set(index, newData);
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
_merge(newValue, deleteMissing) {
|
|
688
|
-
if (!(newValue instanceof Map)) {
|
|
689
|
-
return false;
|
|
690
|
-
}
|
|
691
|
-
// Walk the pairs of the new value map
|
|
692
|
-
newValue.forEach((v, k) => {
|
|
693
|
-
this._setIndex(k, v, deleteMissing);
|
|
694
|
-
});
|
|
695
|
-
if (deleteMissing) {
|
|
696
|
-
this.data.forEach((v, k) => {
|
|
697
|
-
if (!newValue.has(k))
|
|
698
|
-
this._setIndex(k, undefined, false);
|
|
699
|
-
});
|
|
700
|
-
}
|
|
701
|
-
return true;
|
|
702
|
-
}
|
|
703
|
-
_iterateIndexes(scope) {
|
|
704
|
-
this.data.forEach((_, itemIndex) => {
|
|
705
|
-
scope._addChild(itemIndex);
|
|
706
|
-
});
|
|
707
|
-
}
|
|
708
|
-
_normalizeIndex(index) {
|
|
709
|
-
return index;
|
|
710
|
-
}
|
|
711
|
-
_getCount() {
|
|
712
|
-
return this.data.size;
|
|
713
|
-
}
|
|
563
|
+
function onEach(target, render, makeKey) {
|
|
564
|
+
if (!target || typeof target !== "object")
|
|
565
|
+
throw new Error("onEach requires an object");
|
|
566
|
+
target = target[TARGET_SYMBOL] || target;
|
|
567
|
+
new OnEachScope(target, render, makeKey);
|
|
714
568
|
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
}
|
|
720
|
-
_getRecursive(depth) {
|
|
721
|
-
if (currentScope) {
|
|
722
|
-
if (this._addObserver(ANY_INDEX, currentScope)) {
|
|
723
|
-
currentScope._cleaners.push(this);
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
let result = {};
|
|
727
|
-
this.data.forEach((v, k) => {
|
|
728
|
-
result[k] = (v instanceof ObsCollection) ? (depth ? v._getRecursive(depth - 1) : new Store(this, k)) : v;
|
|
729
|
-
});
|
|
730
|
-
return result;
|
|
731
|
-
}
|
|
732
|
-
_merge(newValue, deleteMissing) {
|
|
733
|
-
if (!newValue || newValue.constructor !== Object) {
|
|
734
|
-
return false;
|
|
735
|
-
}
|
|
736
|
-
// Walk the pairs of the new value object
|
|
737
|
-
for (let k in newValue) {
|
|
738
|
-
this._setIndex(k, newValue[k], deleteMissing);
|
|
739
|
-
}
|
|
740
|
-
if (deleteMissing) {
|
|
741
|
-
this.data.forEach((v, k) => {
|
|
742
|
-
if (!newValue.hasOwnProperty(k))
|
|
743
|
-
this._setIndex(k, undefined, false);
|
|
744
|
-
});
|
|
745
|
-
}
|
|
746
|
-
return true;
|
|
747
|
-
}
|
|
748
|
-
_normalizeIndex(index) {
|
|
749
|
-
let type = typeof index;
|
|
750
|
-
if (type === 'string')
|
|
751
|
-
return index;
|
|
752
|
-
if (type === 'number')
|
|
753
|
-
return '' + index;
|
|
754
|
-
throw new Error(`Invalid object index ${JSON.stringify(index)}`);
|
|
755
|
-
}
|
|
756
|
-
_getCount() {
|
|
757
|
-
let cnt = 0;
|
|
758
|
-
for (let key of this.data)
|
|
759
|
-
cnt++;
|
|
760
|
-
return cnt;
|
|
761
|
-
}
|
|
569
|
+
function isObjEmpty(obj) {
|
|
570
|
+
for (let k in obj)
|
|
571
|
+
return false;
|
|
572
|
+
return true;
|
|
762
573
|
}
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
}
|
|
910
|
-
return value === undefined ? opts.defaultValue : value;
|
|
911
|
-
}
|
|
912
|
-
/**
|
|
913
|
-
* Checks if the specified collection is empty, and subscribes the current scope to changes of the emptiness of this collection.
|
|
914
|
-
*
|
|
915
|
-
* @param path Any path terms to resolve before retrieving the value.
|
|
916
|
-
* @returns When the specified collection is not empty `true` is returned. If it is empty or if the value is undefined, `false` is returned.
|
|
917
|
-
* @throws When the value is not a collection and not undefined, an Error will be thrown.
|
|
918
|
-
*/
|
|
919
|
-
isEmpty(...path) {
|
|
920
|
-
let store = this.ref(...path);
|
|
921
|
-
let value = store._observe();
|
|
922
|
-
if (value instanceof ObsCollection) {
|
|
923
|
-
if (currentScope) {
|
|
924
|
-
let observer = new IsEmptyObserver(currentScope, value, false);
|
|
925
|
-
return !observer.count;
|
|
926
|
-
}
|
|
927
|
-
else {
|
|
928
|
-
return !value._getCount();
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
else if (value === undefined) {
|
|
932
|
-
return true;
|
|
933
|
-
}
|
|
934
|
-
else {
|
|
935
|
-
throw new Error(`isEmpty() expects a collection or undefined, but got ${JSON.stringify(value)}`);
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
/**
|
|
939
|
-
* Returns the number of items in the specified collection, and subscribes the current scope to changes in this count.
|
|
940
|
-
*
|
|
941
|
-
* @param path Any path terms to resolve before retrieving the value.
|
|
942
|
-
* @returns The number of items contained in the collection, or 0 if the value is undefined.
|
|
943
|
-
* @throws When the value is not a collection and not undefined, an Error will be thrown.
|
|
944
|
-
*/
|
|
945
|
-
count(...path) {
|
|
946
|
-
let store = this.ref(...path);
|
|
947
|
-
let value = store._observe();
|
|
948
|
-
if (value instanceof ObsCollection) {
|
|
949
|
-
if (currentScope) {
|
|
950
|
-
let observer = new IsEmptyObserver(currentScope, value, true);
|
|
951
|
-
return observer.count;
|
|
952
|
-
}
|
|
953
|
-
else {
|
|
954
|
-
return value._getCount();
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
else if (value === undefined) {
|
|
958
|
-
return 0;
|
|
959
|
-
}
|
|
960
|
-
else {
|
|
961
|
-
throw new Error(`count() expects a collection or undefined, but got ${JSON.stringify(value)}`);
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
/**
|
|
965
|
-
* Returns a strings describing the type of the store value, subscribing to changes of this type.
|
|
966
|
-
* Note: this currently also subscribes to changes of primitive values, so changing a value from 3 to 4
|
|
967
|
-
* would cause the scope to be rerun. This is not great, and may change in the future. This caveat does
|
|
968
|
-
* not apply to changes made *inside* an object, `Array` or `Map`.
|
|
969
|
-
*
|
|
970
|
-
* @param path Any path terms to resolve before retrieving the value.
|
|
971
|
-
* @returns Possible options: "undefined", "null", "boolean", "number", "string", "function", "array", "map" or "object".
|
|
972
|
-
*/
|
|
973
|
-
getType(...path) {
|
|
974
|
-
let store = this.ref(...path);
|
|
975
|
-
let value = store._observe();
|
|
976
|
-
return (value instanceof ObsCollection) ? value._getType() : (value === null ? "null" : typeof value);
|
|
977
|
-
}
|
|
978
|
-
/**
|
|
979
|
-
* Sets the value to the last given argument. Any earlier argument are a Store-path that is first
|
|
980
|
-
* resolved/created using {@link Store.makeRef}.
|
|
981
|
-
*
|
|
982
|
-
* When a `Store` is passed in as the value, its value will be copied (subscribing to changes). In
|
|
983
|
-
* case the value is an object, an `Array` or a `Map`, a *reference* to that data structure will
|
|
984
|
-
* be created, so that changes made through one `Store` will be reflected through the other. Be
|
|
985
|
-
* carefull not to create loops in your `Store` tree that way, as that would cause any future
|
|
986
|
-
* call to {@link Store.get} to throw a `RangeError` (Maximum call stack size exceeded.)
|
|
987
|
-
*
|
|
988
|
-
* If you intent to make a copy instead of a reference, call {@link Store.get} on the origin `Store`.
|
|
989
|
-
*
|
|
990
|
-
*
|
|
991
|
-
* @example
|
|
992
|
-
* ```
|
|
993
|
-
* let store = new Store() // Value is `undefined`
|
|
994
|
-
*
|
|
995
|
-
* store.set('x', 6) // Causes the store to become an object
|
|
996
|
-
* assert(store.get() == {x: 6})
|
|
997
|
-
*
|
|
998
|
-
* store.set('a', 'b', 'c', 'd') // Create parent path as objects
|
|
999
|
-
* assert(store.get() == {x: 6, a: {b: {c: 'd'}}})
|
|
1000
|
-
*
|
|
1001
|
-
* store.set(42) // Overwrites all of the above
|
|
1002
|
-
* assert(store.get() == 42)
|
|
1003
|
-
*
|
|
1004
|
-
* store.set('x', 6) // Throw Error (42 is not a collection)
|
|
1005
|
-
* ```
|
|
1006
|
-
*/
|
|
1007
|
-
set(...pathAndValue) {
|
|
1008
|
-
let newValue = pathAndValue.pop();
|
|
1009
|
-
let store = this.makeRef(...pathAndValue);
|
|
1010
|
-
store._collection._setIndex(store._idx, newValue, true);
|
|
1011
|
-
runImmediateQueue();
|
|
1012
|
-
}
|
|
1013
|
-
/**
|
|
1014
|
-
* Sets the `Store` to the given `mergeValue`, but without deleting any pre-existing
|
|
1015
|
-
* items when a collection overwrites a similarly typed collection. This results in
|
|
1016
|
-
* a deep merge.
|
|
1017
|
-
*
|
|
1018
|
-
* @example
|
|
1019
|
-
* ```
|
|
1020
|
-
* let store = new Store({a: {x: 1}})
|
|
1021
|
-
* store.merge({a: {y: 2}, b: 3})
|
|
1022
|
-
* assert(store.get() == {a: {x: 1, y: 2}, b: 3})
|
|
1023
|
-
* ```
|
|
1024
|
-
*/
|
|
1025
|
-
merge(...pathAndValue) {
|
|
1026
|
-
let mergeValue = pathAndValue.pop();
|
|
1027
|
-
let store = this.makeRef(...pathAndValue);
|
|
1028
|
-
store._collection._setIndex(store._idx, mergeValue, false);
|
|
1029
|
-
runImmediateQueue();
|
|
1030
|
-
}
|
|
1031
|
-
/**
|
|
1032
|
-
* Sets the value for the store to `undefined`, which causes it to be omitted from the map (or array, if it's at the end)
|
|
1033
|
-
*
|
|
1034
|
-
* @example
|
|
1035
|
-
* ```
|
|
1036
|
-
* let store = new Store({a: 1, b: 2})
|
|
1037
|
-
* store.delete('a')
|
|
1038
|
-
* assert(store.get() == {b: 2})
|
|
1039
|
-
*
|
|
1040
|
-
* store = new Store(['a','b','c'])
|
|
1041
|
-
* store.delete(1)
|
|
1042
|
-
* assert(store.get() == ['a', undefined, 'c'])
|
|
1043
|
-
* store.delete(2)
|
|
1044
|
-
* assert(store.get() == ['a'])
|
|
1045
|
-
* ```
|
|
1046
|
-
*/
|
|
1047
|
-
delete(...path) {
|
|
1048
|
-
let store = this.makeRef(...path);
|
|
1049
|
-
store._collection._setIndex(store._idx, undefined, true);
|
|
1050
|
-
runImmediateQueue();
|
|
1051
|
-
}
|
|
1052
|
-
/**
|
|
1053
|
-
* Pushes a value to the end of the Array that is at the specified path in the store.
|
|
1054
|
-
* If that store path is `undefined`, an Array is created first.
|
|
1055
|
-
* The last argument is the value to be added, any earlier arguments indicate the path.
|
|
1056
|
-
*
|
|
1057
|
-
* @example
|
|
1058
|
-
* ```
|
|
1059
|
-
* let store = new Store()
|
|
1060
|
-
* store.push(3) // Creates the array
|
|
1061
|
-
* store.push(6)
|
|
1062
|
-
* assert(store.get() == [3,6])
|
|
1063
|
-
*
|
|
1064
|
-
* store = new Store({myArray: [1,2]})
|
|
1065
|
-
* store.push('myArray', 3)
|
|
1066
|
-
* assert(store.get() == {myArray: [1,2,3]})
|
|
1067
|
-
* ```
|
|
1068
|
-
*/
|
|
1069
|
-
push(...pathAndValue) {
|
|
1070
|
-
let newValue = pathAndValue.pop();
|
|
1071
|
-
let store = this.makeRef(...pathAndValue);
|
|
1072
|
-
let obsArray = store._collection.rawGet(store._idx);
|
|
1073
|
-
if (obsArray === undefined) {
|
|
1074
|
-
obsArray = new ObsArray();
|
|
1075
|
-
store._collection._setIndex(store._idx, obsArray, true);
|
|
1076
|
-
}
|
|
1077
|
-
else if (!(obsArray instanceof ObsArray)) {
|
|
1078
|
-
throw new Error(`push() is only allowed for an array or undefined (which would become an array)`);
|
|
1079
|
-
}
|
|
1080
|
-
let newData = valueToData(newValue);
|
|
1081
|
-
let pos = obsArray._data.length;
|
|
1082
|
-
obsArray._data.push(newData);
|
|
1083
|
-
obsArray.emitChange(pos, newData, undefined);
|
|
1084
|
-
runImmediateQueue();
|
|
1085
|
-
return pos;
|
|
1086
|
-
}
|
|
1087
|
-
/**
|
|
1088
|
-
* {@link Store.peek} the current value, pass it through `func`, and {@link Store.set} the resulting
|
|
1089
|
-
* value.
|
|
1090
|
-
* @param func The function transforming the value.
|
|
1091
|
-
*/
|
|
1092
|
-
modify(func) {
|
|
1093
|
-
this.set(func(this.query({ peek: true })));
|
|
1094
|
-
}
|
|
1095
|
-
/**
|
|
1096
|
-
* Return a `Store` deeper within the tree by resolving the given `path`,
|
|
1097
|
-
* subscribing to every level.
|
|
1098
|
-
* In case `undefined` is encountered while resolving the path, a newly
|
|
1099
|
-
* created `Store` containing `undefined` is returned. In that case, the
|
|
1100
|
-
* `Store`'s {@link Store.isDetached} method will return `true`.
|
|
1101
|
-
* In case something other than a collection is encountered, an error is thrown.
|
|
1102
|
-
*/
|
|
1103
|
-
ref(...path) {
|
|
1104
|
-
let store = this;
|
|
1105
|
-
for (let i = 0; i < path.length; i++) {
|
|
1106
|
-
let value = store._observe();
|
|
1107
|
-
if (value instanceof ObsCollection) {
|
|
1108
|
-
store = new Store(value, value._normalizeIndex(path[i]));
|
|
1109
|
-
}
|
|
1110
|
-
else {
|
|
1111
|
-
if (value !== undefined)
|
|
1112
|
-
throw new Error(`Value ${JSON.stringify(value)} is not a collection (nor undefined) in step ${i} of $(${JSON.stringify(path)})`);
|
|
1113
|
-
return new DetachedStore();
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
return store;
|
|
1117
|
-
}
|
|
1118
|
-
/**
|
|
1119
|
-
* Similar to `ref()`, but instead of returning `undefined`, new objects are created when
|
|
1120
|
-
* a path does not exist yet. An error is still thrown when the path tries to index an invalid
|
|
1121
|
-
* type.
|
|
1122
|
-
* Unlike `ref`, `makeRef` does *not* subscribe to the path levels, as it is intended to be
|
|
1123
|
-
* a write-only operation.
|
|
1124
|
-
*
|
|
1125
|
-
* @example
|
|
1126
|
-
* ```
|
|
1127
|
-
* let store = new Store() // Value is `undefined`
|
|
1128
|
-
*
|
|
1129
|
-
* let ref = store.makeRef('a', 'b', 'c')
|
|
1130
|
-
* assert(store.get() == {a: {b: {}}}
|
|
1131
|
-
*
|
|
1132
|
-
* ref.set(42)
|
|
1133
|
-
* assert(store.get() == {a: {b: {c: 42}}}
|
|
1134
|
-
*
|
|
1135
|
-
* ref.makeRef('d') // Throw Error (42 is not a collection)
|
|
1136
|
-
* ```
|
|
1137
|
-
*/
|
|
1138
|
-
makeRef(...path) {
|
|
1139
|
-
let store = this;
|
|
1140
|
-
for (let i = 0; i < path.length; i++) {
|
|
1141
|
-
let value = store._collection.rawGet(store._idx);
|
|
1142
|
-
if (!(value instanceof ObsCollection)) {
|
|
1143
|
-
if (value !== undefined)
|
|
1144
|
-
throw new Error(`Value ${JSON.stringify(value)} is not a collection (nor undefined) in step ${i} of $(${JSON.stringify(path)})`);
|
|
1145
|
-
value = new ObsObject();
|
|
1146
|
-
store._collection.rawSet(store._idx, value);
|
|
1147
|
-
store._collection.emitChange(store._idx, value, undefined);
|
|
1148
|
-
}
|
|
1149
|
-
store = new Store(value, value._normalizeIndex(path[i]));
|
|
1150
|
-
}
|
|
1151
|
-
runImmediateQueue();
|
|
1152
|
-
return store;
|
|
1153
|
-
}
|
|
1154
|
-
/** @internal */
|
|
1155
|
-
_observe() {
|
|
1156
|
-
if (currentScope) {
|
|
1157
|
-
if (this._collection._addObserver(this._idx, currentScope)) {
|
|
1158
|
-
currentScope._cleaners.push(this);
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
return this._collection.rawGet(this._idx);
|
|
1162
|
-
}
|
|
1163
|
-
/**
|
|
1164
|
-
* Iterate the specified collection (Array, Map or object), running the given code block for each item.
|
|
1165
|
-
* When items are added to the collection at some later point, the code block will be ran for them as well.
|
|
1166
|
-
* When an item is removed, the {@link Store.clean} handlers left by its code block are executed.
|
|
1167
|
-
*
|
|
1168
|
-
*
|
|
1169
|
-
*
|
|
1170
|
-
* @param pathAndFuncs
|
|
1171
|
-
*/
|
|
1172
|
-
onEach(...pathAndFuncs) {
|
|
1173
|
-
let makeSortKey = defaultMakeSortKey;
|
|
1174
|
-
let renderer = pathAndFuncs.pop();
|
|
1175
|
-
if (typeof pathAndFuncs[pathAndFuncs.length - 1] === 'function' && (typeof renderer === 'function' || renderer == null)) {
|
|
1176
|
-
if (renderer != null)
|
|
1177
|
-
makeSortKey = renderer;
|
|
1178
|
-
renderer = pathAndFuncs.pop();
|
|
1179
|
-
}
|
|
1180
|
-
if (typeof renderer !== 'function')
|
|
1181
|
-
throw new Error(`onEach() expects a render function as its last argument but got ${JSON.stringify(renderer)}`);
|
|
1182
|
-
if (!currentScope)
|
|
1183
|
-
throw new ScopeError(false);
|
|
1184
|
-
let store = this.ref(...pathAndFuncs);
|
|
1185
|
-
let val = store._observe();
|
|
1186
|
-
if (val instanceof ObsCollection) {
|
|
1187
|
-
// Subscribe to changes using the specialized OnEachScope
|
|
1188
|
-
let onEachScope = new OnEachScope(currentScope._parentElement, currentScope._lastChild || currentScope._precedingSibling, currentScope._queueOrder + 1, val, renderer, makeSortKey);
|
|
1189
|
-
val._addObserver(ANY_INDEX, onEachScope);
|
|
1190
|
-
currentScope._cleaners.push(onEachScope);
|
|
1191
|
-
currentScope._lastChild = onEachScope;
|
|
1192
|
-
onEachScope._renderInitial();
|
|
1193
|
-
}
|
|
1194
|
-
else if (val !== undefined) {
|
|
1195
|
-
throw new Error(`onEach() attempted on a value that is neither a collection nor undefined`);
|
|
1196
|
-
}
|
|
1197
|
-
}
|
|
1198
|
-
/**
|
|
1199
|
-
* Applies a filter/map function on each item within the `Store`'s collection,
|
|
1200
|
-
* and reactively manages the returned `Map` `Store` to hold any results.
|
|
1201
|
-
*
|
|
1202
|
-
* @param func - Function that transform the given store into an output value or
|
|
1203
|
-
* `undefined` in case this value should be skipped:
|
|
1204
|
-
*
|
|
1205
|
-
* @returns - A map `Store` with the values returned by `func` and the corresponding
|
|
1206
|
-
* keys from the original map, array or object `Store`.
|
|
1207
|
-
*
|
|
1208
|
-
* When items disappear from the `Store` or are changed in a way that `func` depends
|
|
1209
|
-
* upon, the resulting items are removed from the output `Store` as well. When multiple
|
|
1210
|
-
* input items produce the same output keys, this may lead to unexpected results.
|
|
1211
|
-
*/
|
|
1212
|
-
map(func) {
|
|
1213
|
-
let out = new Store(new Map());
|
|
1214
|
-
this.onEach((item) => {
|
|
1215
|
-
let value = func(item);
|
|
1216
|
-
if (value !== undefined) {
|
|
1217
|
-
let key = item.index();
|
|
1218
|
-
out.set(key, value);
|
|
1219
|
-
clean(() => {
|
|
1220
|
-
out.delete(key);
|
|
1221
|
-
});
|
|
1222
|
-
}
|
|
1223
|
-
});
|
|
1224
|
-
return out;
|
|
1225
|
-
}
|
|
1226
|
-
/**
|
|
1227
|
-
* Applies a filter/map function on each item within the `Store`'s collection,
|
|
1228
|
-
* each of which can deliver any number of key/value pairs, and reactively manages the
|
|
1229
|
-
* returned map `Store` to hold any results.
|
|
1230
|
-
*
|
|
1231
|
-
* @param func - Function that transform the given store into output values
|
|
1232
|
-
* that can take one of the following forms:
|
|
1233
|
-
* - an `Object` or a `Map`: Each key/value pair will be added to the output `Store`.
|
|
1234
|
-
* - anything else: No key/value pairs are added to the output `Store`.
|
|
1235
|
-
*
|
|
1236
|
-
* @returns - A map `Store` with the key/value pairs returned by all `func` invocations.
|
|
1237
|
-
*
|
|
1238
|
-
* When items disappear from the `Store` or are changed in a way that `func` depends
|
|
1239
|
-
* upon, the resulting items are removed from the output `Store` as well. When multiple
|
|
1240
|
-
* input items produce the same output keys, this may lead to unexpected results.
|
|
1241
|
-
*/
|
|
1242
|
-
multiMap(func) {
|
|
1243
|
-
let out = new Store(new Map());
|
|
1244
|
-
this.onEach((item) => {
|
|
1245
|
-
let result = func(item);
|
|
1246
|
-
let keys;
|
|
1247
|
-
if (result.constructor === Object) {
|
|
1248
|
-
for (let key in result) {
|
|
1249
|
-
out.set(key, result[key]);
|
|
1250
|
-
}
|
|
1251
|
-
keys = Object.keys(result);
|
|
1252
|
-
}
|
|
1253
|
-
else if (result instanceof Map) {
|
|
1254
|
-
result.forEach((value, key) => {
|
|
1255
|
-
out.set(key, value);
|
|
1256
|
-
});
|
|
1257
|
-
keys = [...result.keys()];
|
|
1258
|
-
}
|
|
1259
|
-
else {
|
|
1260
|
-
return;
|
|
1261
|
-
}
|
|
1262
|
-
if (keys.length) {
|
|
1263
|
-
clean(() => {
|
|
1264
|
-
for (let key of keys) {
|
|
1265
|
-
out.delete(key);
|
|
1266
|
-
}
|
|
1267
|
-
});
|
|
1268
|
-
}
|
|
1269
|
-
});
|
|
1270
|
-
return out;
|
|
1271
|
-
}
|
|
1272
|
-
/**
|
|
1273
|
-
* @returns Returns `true` when the `Store` was created by {@link Store.ref}ing a path that
|
|
1274
|
-
* does not exist.
|
|
1275
|
-
*/
|
|
1276
|
-
isDetached() { return false; }
|
|
1277
|
-
/**
|
|
1278
|
-
* Dump a live view of the `Store` tree as HTML text, `ul` and `li` nodes at
|
|
1279
|
-
* the current mount position. Meant for debugging purposes.
|
|
1280
|
-
*/
|
|
1281
|
-
dump() {
|
|
1282
|
-
let type = this.getType();
|
|
1283
|
-
if (type === 'array' || type === 'object' || type === 'map') {
|
|
1284
|
-
text('<' + type + '>');
|
|
1285
|
-
node('ul', () => {
|
|
1286
|
-
this.onEach((sub) => {
|
|
1287
|
-
node('li', () => {
|
|
1288
|
-
text(JSON.stringify(sub.index()) + ': ');
|
|
1289
|
-
sub.dump();
|
|
1290
|
-
});
|
|
1291
|
-
});
|
|
1292
|
-
});
|
|
1293
|
-
}
|
|
1294
|
-
else {
|
|
1295
|
-
text(JSON.stringify(this.get()));
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
574
|
+
function isEmpty(proxied) {
|
|
575
|
+
const target = proxied[TARGET_SYMBOL] || proxied;
|
|
576
|
+
const scope = currentScope;
|
|
577
|
+
if (target instanceof Array) {
|
|
578
|
+
subscribe(target, "length", function(index, newData, oldData) {
|
|
579
|
+
if (!newData !== !oldData)
|
|
580
|
+
queue(scope);
|
|
581
|
+
});
|
|
582
|
+
return !target.length;
|
|
583
|
+
} else {
|
|
584
|
+
const result = isObjEmpty(target);
|
|
585
|
+
subscribe(target, ANY_SYMBOL, function(index, newData, oldData) {
|
|
586
|
+
if (result ? oldData === undefined : newData === undefined)
|
|
587
|
+
queue(scope);
|
|
588
|
+
});
|
|
589
|
+
return result;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
function count(proxied) {
|
|
593
|
+
if (proxied instanceof Array)
|
|
594
|
+
return ref(proxied, "length");
|
|
595
|
+
const target = proxied[TARGET_SYMBOL] || proxied;
|
|
596
|
+
let cnt = 0;
|
|
597
|
+
for (let k in target)
|
|
598
|
+
if (target[k] !== undefined)
|
|
599
|
+
cnt++;
|
|
600
|
+
const result = proxy(cnt);
|
|
601
|
+
subscribe(target, ANY_SYMBOL, function(index, newData, oldData) {
|
|
602
|
+
if (oldData === newData) {} else if (oldData === undefined)
|
|
603
|
+
result.value = ++cnt;
|
|
604
|
+
else if (newData === undefined)
|
|
605
|
+
result.value = --cnt;
|
|
606
|
+
});
|
|
607
|
+
return result;
|
|
608
|
+
}
|
|
609
|
+
function defaultEmitHandler(target, index, newData, oldData) {
|
|
610
|
+
if (newData === oldData && newData !== undefined)
|
|
611
|
+
return;
|
|
612
|
+
const byTarget = subscribers.get(target);
|
|
613
|
+
if (byTarget === undefined)
|
|
614
|
+
return;
|
|
615
|
+
for (const what of [index, ANY_SYMBOL]) {
|
|
616
|
+
let byIndex = byTarget.get(what);
|
|
617
|
+
if (byIndex) {
|
|
618
|
+
for (let observer of byIndex) {
|
|
619
|
+
if (typeof observer === "function")
|
|
620
|
+
observer(index, newData, oldData);
|
|
621
|
+
else
|
|
622
|
+
observer.onChange(index, newData, oldData);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
var emit = defaultEmitHandler;
|
|
628
|
+
var objectHandler = {
|
|
629
|
+
get(target, prop) {
|
|
630
|
+
if (prop === TARGET_SYMBOL)
|
|
631
|
+
return target;
|
|
632
|
+
subscribe(target, prop);
|
|
633
|
+
return optProxy(target[prop]);
|
|
634
|
+
},
|
|
635
|
+
set(target, prop, newData) {
|
|
636
|
+
if (typeof newData === "object" && newData)
|
|
637
|
+
newData = newData[TARGET_SYMBOL] || newData;
|
|
638
|
+
const oldData = target[prop];
|
|
639
|
+
if (newData !== oldData) {
|
|
640
|
+
target[prop] = newData;
|
|
641
|
+
emit(target, prop, newData, oldData);
|
|
642
|
+
runImmediateQueue();
|
|
643
|
+
}
|
|
644
|
+
return true;
|
|
645
|
+
},
|
|
646
|
+
deleteProperty(target, prop) {
|
|
647
|
+
const old = target[prop];
|
|
648
|
+
delete target[prop];
|
|
649
|
+
emit(target, prop, undefined, old);
|
|
650
|
+
runImmediateQueue();
|
|
651
|
+
return true;
|
|
652
|
+
},
|
|
653
|
+
has(target, prop) {
|
|
654
|
+
const result = prop in target;
|
|
655
|
+
subscribe(target, prop);
|
|
656
|
+
return result;
|
|
657
|
+
},
|
|
658
|
+
ownKeys(target) {
|
|
659
|
+
subscribe(target, ANY_SYMBOL);
|
|
660
|
+
return Reflect.ownKeys(target);
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
function arraySet(target, prop, newData) {
|
|
664
|
+
if (typeof newData === "object" && newData)
|
|
665
|
+
newData = newData[TARGET_SYMBOL] || newData;
|
|
666
|
+
const oldData = target[prop];
|
|
667
|
+
if (newData !== oldData) {
|
|
668
|
+
let oldLength = target.length;
|
|
669
|
+
if (prop === "length") {
|
|
670
|
+
target.length = newData;
|
|
671
|
+
for (let i = newData;i < oldLength; i++) {
|
|
672
|
+
emit(target, i, undefined, target[i]);
|
|
673
|
+
}
|
|
674
|
+
} else {
|
|
675
|
+
const intProp = parseInt(prop);
|
|
676
|
+
if (intProp.toString() === prop)
|
|
677
|
+
prop = intProp;
|
|
678
|
+
target[prop] = newData;
|
|
679
|
+
emit(target, prop, newData, oldData);
|
|
680
|
+
}
|
|
681
|
+
if (target.length !== oldLength) {
|
|
682
|
+
emit(target, "length", target.length, oldLength);
|
|
683
|
+
}
|
|
684
|
+
runImmediateQueue();
|
|
685
|
+
}
|
|
686
|
+
return true;
|
|
687
|
+
}
|
|
688
|
+
var arrayHandler = {
|
|
689
|
+
get(target, prop) {
|
|
690
|
+
if (prop === TARGET_SYMBOL)
|
|
691
|
+
return target;
|
|
692
|
+
let subProp = prop;
|
|
693
|
+
if (typeof prop !== "symbol") {
|
|
694
|
+
const intProp = parseInt(prop);
|
|
695
|
+
if (intProp.toString() === prop)
|
|
696
|
+
subProp = intProp;
|
|
697
|
+
}
|
|
698
|
+
subscribe(target, subProp);
|
|
699
|
+
return optProxy(target[prop]);
|
|
700
|
+
},
|
|
701
|
+
set: arraySet,
|
|
702
|
+
deleteProperty(target, prop) {
|
|
703
|
+
return arraySet(target, prop, undefined);
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
var proxyMap = new WeakMap;
|
|
707
|
+
function optProxy(value) {
|
|
708
|
+
if (typeof value !== "object" || !value || value[TARGET_SYMBOL] !== undefined) {
|
|
709
|
+
return value;
|
|
710
|
+
}
|
|
711
|
+
let proxied = proxyMap.get(value);
|
|
712
|
+
if (proxied)
|
|
713
|
+
return proxied;
|
|
714
|
+
proxied = new Proxy(value, value instanceof Array ? arrayHandler : objectHandler);
|
|
715
|
+
proxyMap.set(value, proxied);
|
|
716
|
+
return proxied;
|
|
717
|
+
}
|
|
718
|
+
function proxy(target) {
|
|
719
|
+
return optProxy(typeof target === "object" && target !== null ? target : { value: target });
|
|
1298
720
|
}
|
|
1299
|
-
|
|
1300
|
-
|
|
721
|
+
function unproxy(target) {
|
|
722
|
+
return target ? target[TARGET_SYMBOL] || target : target;
|
|
1301
723
|
}
|
|
1302
|
-
|
|
1303
|
-
let onDestroyMap = new WeakMap();
|
|
724
|
+
var onDestroyMap = new WeakMap;
|
|
1304
725
|
function destroyWithClass(element, cls) {
|
|
1305
|
-
|
|
1306
|
-
|
|
726
|
+
const classes = cls.split(".").filter((c) => c);
|
|
727
|
+
element.classList.add(...classes);
|
|
728
|
+
setTimeout(() => element.remove(), 2000);
|
|
1307
729
|
}
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
730
|
+
function copy(dst, src, flags = 0) {
|
|
731
|
+
copyRecurse(dst, src, flags);
|
|
732
|
+
runImmediateQueue();
|
|
733
|
+
}
|
|
734
|
+
var MERGE = 1;
|
|
735
|
+
var SHALLOW = 2;
|
|
736
|
+
var COPY_SUBSCRIBE = 32;
|
|
737
|
+
var COPY_EMIT = 64;
|
|
738
|
+
function clone(src, flags = 0) {
|
|
739
|
+
const dst = Object.create(Object.getPrototypeOf(src));
|
|
740
|
+
copyRecurse(dst, src, flags);
|
|
741
|
+
return dst;
|
|
742
|
+
}
|
|
743
|
+
function copyRecurse(dst, src, flags) {
|
|
744
|
+
let unproxied = dst[TARGET_SYMBOL];
|
|
745
|
+
if (unproxied) {
|
|
746
|
+
dst = unproxied;
|
|
747
|
+
flags |= COPY_EMIT;
|
|
748
|
+
}
|
|
749
|
+
unproxied = src[TARGET_SYMBOL];
|
|
750
|
+
if (unproxied) {
|
|
751
|
+
src = unproxied;
|
|
752
|
+
if (currentScope !== ROOT_SCOPE && !peeking)
|
|
753
|
+
flags |= COPY_SUBSCRIBE;
|
|
754
|
+
}
|
|
755
|
+
if (flags & COPY_SUBSCRIBE)
|
|
756
|
+
subscribe(src, ANY_SYMBOL);
|
|
757
|
+
if (src instanceof Array) {
|
|
758
|
+
if (!(dst instanceof Array))
|
|
759
|
+
throw new Error("Cannot copy array into object");
|
|
760
|
+
const dstLen = dst.length;
|
|
761
|
+
const srcLen = src.length;
|
|
762
|
+
for (let i = 0;i < srcLen; i++) {
|
|
763
|
+
copyValue(dst, src, i, flags);
|
|
764
|
+
}
|
|
765
|
+
if (srcLen !== dstLen) {
|
|
766
|
+
if (flags & COPY_EMIT) {
|
|
767
|
+
for (let i = srcLen;i < dstLen; i++) {
|
|
768
|
+
const old = dst[i];
|
|
769
|
+
dst[i] = undefined;
|
|
770
|
+
emit(dst, i, undefined, old);
|
|
771
|
+
}
|
|
772
|
+
dst.length = srcLen;
|
|
773
|
+
emit(dst, "length", srcLen, dstLen);
|
|
774
|
+
} else {
|
|
775
|
+
dst.length = srcLen;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
} else {
|
|
779
|
+
for (let k in src) {
|
|
780
|
+
copyValue(dst, src, k, flags);
|
|
781
|
+
}
|
|
782
|
+
if (!(flags & MERGE)) {
|
|
783
|
+
for (let k in dst) {
|
|
784
|
+
if (!(k in src)) {
|
|
785
|
+
const old = dst[k];
|
|
786
|
+
delete dst[k];
|
|
787
|
+
if (flags & COPY_EMIT && old !== undefined) {
|
|
788
|
+
emit(dst, k, undefined, old);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
function copyValue(dst, src, index, flags) {
|
|
796
|
+
let dstValue = dst[index];
|
|
797
|
+
let srcValue = src[index];
|
|
798
|
+
if (srcValue !== dstValue) {
|
|
799
|
+
if (srcValue && dstValue && typeof srcValue === "object" && typeof dstValue === "object" && (srcValue.constructor === dstValue.constructor || flags & MERGE && dstValue instanceof Array)) {
|
|
800
|
+
copyRecurse(dstValue, srcValue, flags);
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
if (!(flags & SHALLOW) && srcValue && typeof srcValue === "object") {
|
|
804
|
+
let copy2 = Object.create(Object.getPrototypeOf(srcValue));
|
|
805
|
+
copyRecurse(copy2, srcValue, 0);
|
|
806
|
+
srcValue = copy2;
|
|
807
|
+
}
|
|
808
|
+
const old = dst[index];
|
|
809
|
+
if (flags & MERGE && srcValue == null)
|
|
810
|
+
delete dst[index];
|
|
811
|
+
else
|
|
812
|
+
dst[index] = srcValue;
|
|
813
|
+
if (flags & COPY_EMIT)
|
|
814
|
+
emit(dst, index, srcValue, old);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
var refHandler = {
|
|
818
|
+
get(target, prop) {
|
|
819
|
+
if (prop === TARGET_SYMBOL) {
|
|
820
|
+
return ref(unproxy(target.proxy), target.index);
|
|
821
|
+
}
|
|
822
|
+
if (prop === "value") {
|
|
823
|
+
return target.proxy[target.index];
|
|
824
|
+
}
|
|
825
|
+
},
|
|
826
|
+
set(target, prop, value) {
|
|
827
|
+
if (prop === "value") {
|
|
828
|
+
target.proxy[target.index] = value;
|
|
829
|
+
return true;
|
|
830
|
+
}
|
|
831
|
+
return false;
|
|
832
|
+
}
|
|
833
|
+
};
|
|
834
|
+
function ref(target, index) {
|
|
835
|
+
return new Proxy({ proxy: target, index }, refHandler);
|
|
836
|
+
}
|
|
837
|
+
function applyBind(_el, target) {
|
|
838
|
+
const el = _el;
|
|
839
|
+
let onProxyChange;
|
|
840
|
+
let onInputChange;
|
|
841
|
+
let type = el.getAttribute("type");
|
|
842
|
+
let value = unproxy(target).value;
|
|
843
|
+
if (type === "checkbox") {
|
|
844
|
+
if (value === undefined)
|
|
845
|
+
target.value = el.checked;
|
|
846
|
+
onProxyChange = () => el.checked = target.value;
|
|
847
|
+
onInputChange = () => target.value = el.checked;
|
|
848
|
+
} else if (type === "radio") {
|
|
849
|
+
if (value === undefined && el.checked)
|
|
850
|
+
target.value = el.value;
|
|
851
|
+
onProxyChange = () => el.checked = target.value === el.value;
|
|
852
|
+
onInputChange = () => {
|
|
853
|
+
if (el.checked)
|
|
854
|
+
target.value = el.value;
|
|
855
|
+
};
|
|
856
|
+
} else {
|
|
857
|
+
onInputChange = () => target.value = type === "number" || type === "range" ? el.value === "" ? null : +el.value : el.value;
|
|
858
|
+
if (value === undefined)
|
|
859
|
+
onInputChange();
|
|
860
|
+
onProxyChange = () => el.value = target.value;
|
|
861
|
+
}
|
|
862
|
+
observe(onProxyChange);
|
|
863
|
+
el.addEventListener("input", onInputChange);
|
|
864
|
+
clean(() => {
|
|
865
|
+
el.removeEventListener("input", onInputChange);
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
var SPECIAL_PROPS = {
|
|
869
|
+
create: function(value) {
|
|
870
|
+
const el = currentScope.parentElement;
|
|
871
|
+
if (currentScope !== topRedrawScope)
|
|
872
|
+
return;
|
|
873
|
+
if (typeof value === "function") {
|
|
874
|
+
value(el);
|
|
875
|
+
} else {
|
|
876
|
+
const classes = value.split(".").filter((c) => c);
|
|
877
|
+
el.classList.add(...classes);
|
|
878
|
+
(async function() {
|
|
879
|
+
el.offsetHeight;
|
|
880
|
+
el.classList.remove(...classes);
|
|
881
|
+
})();
|
|
882
|
+
}
|
|
883
|
+
},
|
|
884
|
+
destroy: function(value) {
|
|
885
|
+
const el = currentScope.parentElement;
|
|
886
|
+
onDestroyMap.set(el, value);
|
|
887
|
+
},
|
|
888
|
+
html: function(value) {
|
|
889
|
+
let tmpParent = document.createElement(currentScope.parentElement.tagName);
|
|
890
|
+
tmpParent.innerHTML = "" + value;
|
|
891
|
+
while (tmpParent.firstChild)
|
|
892
|
+
addNode(tmpParent.firstChild);
|
|
893
|
+
},
|
|
894
|
+
text: function(value) {
|
|
895
|
+
addNode(document.createTextNode(value));
|
|
896
|
+
},
|
|
897
|
+
element: function(value) {
|
|
898
|
+
if (!(value instanceof Node))
|
|
899
|
+
throw new Error(`Unexpected element-argument: ${JSON.parse(value)}`);
|
|
900
|
+
addNode(value);
|
|
901
|
+
}
|
|
902
|
+
};
|
|
903
|
+
function $(...args) {
|
|
904
|
+
let savedCurrentScope;
|
|
905
|
+
let err;
|
|
906
|
+
for (let arg of args) {
|
|
907
|
+
if (arg == null || arg === false)
|
|
908
|
+
continue;
|
|
909
|
+
if (typeof arg === "string") {
|
|
910
|
+
let text, classes;
|
|
911
|
+
const textPos = arg.indexOf(":");
|
|
912
|
+
if (textPos >= 0) {
|
|
913
|
+
text = arg.substring(textPos + 1);
|
|
914
|
+
arg = arg.substring(0, textPos);
|
|
915
|
+
}
|
|
916
|
+
const classPos = arg.indexOf(".");
|
|
917
|
+
if (classPos >= 0) {
|
|
918
|
+
classes = arg.substring(classPos + 1);
|
|
919
|
+
arg = arg.substring(0, classPos);
|
|
920
|
+
}
|
|
921
|
+
if (arg === "") {
|
|
922
|
+
if (text)
|
|
923
|
+
addNode(document.createTextNode(text));
|
|
1338
924
|
if (classes) {
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
925
|
+
const el = currentScope.parentElement;
|
|
926
|
+
el.classList.add(...classes.split("."));
|
|
927
|
+
if (!savedCurrentScope) {
|
|
928
|
+
clean(() => el.classList.remove(...classes.split(".")));
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
} else if (arg.indexOf(" ") >= 0) {
|
|
932
|
+
err = `Tag '${arg}' cannot contain space`;
|
|
933
|
+
break;
|
|
934
|
+
} else {
|
|
935
|
+
const el = document.createElement(arg);
|
|
936
|
+
if (classes)
|
|
937
|
+
el.className = classes.replaceAll(".", " ");
|
|
938
|
+
if (text)
|
|
939
|
+
el.textContent = text;
|
|
940
|
+
addNode(el);
|
|
941
|
+
if (!savedCurrentScope) {
|
|
942
|
+
savedCurrentScope = currentScope;
|
|
943
|
+
}
|
|
944
|
+
let newScope = new ChainedScope(el, true);
|
|
945
|
+
newScope.lastChild = el.lastChild || undefined;
|
|
946
|
+
if (topRedrawScope === currentScope)
|
|
947
|
+
topRedrawScope = newScope;
|
|
948
|
+
currentScope = newScope;
|
|
949
|
+
}
|
|
950
|
+
} else if (typeof arg === "object") {
|
|
951
|
+
if (arg.constructor !== Object) {
|
|
952
|
+
err = `Unexpected argument: ${arg}`;
|
|
953
|
+
break;
|
|
954
|
+
}
|
|
955
|
+
for (const key in arg) {
|
|
956
|
+
const val = arg[key];
|
|
957
|
+
applyArg(key, val);
|
|
958
|
+
}
|
|
959
|
+
} else if (typeof arg === "function") {
|
|
960
|
+
new RegularScope(currentScope.parentElement, arg);
|
|
961
|
+
} else {
|
|
962
|
+
err = `Unexpected argument: ${arg}`;
|
|
963
|
+
break;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
if (savedCurrentScope) {
|
|
967
|
+
currentScope = savedCurrentScope;
|
|
968
|
+
}
|
|
969
|
+
if (err)
|
|
970
|
+
throw new Error(err);
|
|
1375
971
|
}
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
let tmpParent = document.createElement(currentScope._parentElement.tagName);
|
|
1384
|
-
tmpParent.innerHTML = '' + html;
|
|
1385
|
-
while (tmpParent.firstChild) {
|
|
1386
|
-
currentScope._addNode(tmpParent.firstChild);
|
|
1387
|
-
}
|
|
972
|
+
var cssCount = 0;
|
|
973
|
+
function insertCss(style, global = false) {
|
|
974
|
+
const prefix = global ? "" : ".AbdStl" + ++cssCount;
|
|
975
|
+
let css = styleToCss(style, prefix);
|
|
976
|
+
if (css)
|
|
977
|
+
$("style:" + css);
|
|
978
|
+
return prefix;
|
|
1388
979
|
}
|
|
1389
|
-
function
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
onInputChange();
|
|
1413
|
-
onStoreChange = value => {
|
|
1414
|
-
if (el.value !== value)
|
|
1415
|
-
el.value = value;
|
|
1416
|
-
};
|
|
1417
|
-
}
|
|
1418
|
-
observe(() => {
|
|
1419
|
-
onStoreChange(store.get());
|
|
1420
|
-
});
|
|
1421
|
-
el.addEventListener('input', onInputChange);
|
|
1422
|
-
clean(() => {
|
|
1423
|
-
el.removeEventListener('input', onInputChange);
|
|
1424
|
-
});
|
|
980
|
+
function styleToCss(style, prefix) {
|
|
981
|
+
let props = "";
|
|
982
|
+
let rules = "";
|
|
983
|
+
for (const kOr in style) {
|
|
984
|
+
const v = style[kOr];
|
|
985
|
+
for (const k of kOr.split(/, ?/g)) {
|
|
986
|
+
if (v && typeof v === "object") {
|
|
987
|
+
if (k.startsWith("@")) {
|
|
988
|
+
rules += k + `{
|
|
989
|
+
` + styleToCss(v, prefix) + `}
|
|
990
|
+
`;
|
|
991
|
+
} else {
|
|
992
|
+
rules += styleToCss(v, k.includes("&") ? k.replace(/&/g, prefix) : prefix + " " + k);
|
|
993
|
+
}
|
|
994
|
+
} else {
|
|
995
|
+
props += k.replace(/[A-Z]/g, (letter) => "-" + letter.toLowerCase()) + ":" + v + ";";
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
if (props)
|
|
1000
|
+
rules = (prefix.trimStart() || "*") + "{" + props + `}
|
|
1001
|
+
` + rules;
|
|
1002
|
+
return rules;
|
|
1425
1003
|
}
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1004
|
+
function applyArg(key, value) {
|
|
1005
|
+
const el = currentScope.parentElement;
|
|
1006
|
+
if (typeof value === "object" && value !== null && value[TARGET_SYMBOL]) {
|
|
1007
|
+
if (key === "bind") {
|
|
1008
|
+
applyBind(el, value);
|
|
1009
|
+
} else {
|
|
1010
|
+
new SetArgScope(el, key, value);
|
|
1011
|
+
}
|
|
1012
|
+
} else if (key[0] === ".") {
|
|
1013
|
+
const classes = key.substring(1).split(".");
|
|
1014
|
+
if (value)
|
|
1015
|
+
el.classList.add(...classes);
|
|
1016
|
+
else
|
|
1017
|
+
el.classList.remove(...classes);
|
|
1018
|
+
} else if (key[0] === "$") {
|
|
1019
|
+
const name = key.substring(1);
|
|
1020
|
+
if (value == null || value === false)
|
|
1021
|
+
el.style[name] = "";
|
|
1022
|
+
else
|
|
1023
|
+
el.style[name] = "" + value;
|
|
1024
|
+
} else if (value == null) {} else if (key in SPECIAL_PROPS) {
|
|
1025
|
+
SPECIAL_PROPS[key](value);
|
|
1026
|
+
} else if (typeof value === "function") {
|
|
1027
|
+
el.addEventListener(key, value);
|
|
1028
|
+
clean(() => el.removeEventListener(key, value));
|
|
1029
|
+
} else if (value === true || value === false || key === "value" || key === "selectedIndex") {
|
|
1030
|
+
el[key] = value;
|
|
1031
|
+
} else {
|
|
1032
|
+
el.setAttribute(key, value);
|
|
1033
|
+
}
|
|
1435
1034
|
}
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
if (typeof name === 'object') {
|
|
1440
|
-
for (let k in name) {
|
|
1441
|
-
applyProp(currentScope._parentElement, k, name[k]);
|
|
1442
|
-
}
|
|
1443
|
-
}
|
|
1444
|
-
else {
|
|
1445
|
-
applyProp(currentScope._parentElement, name, value);
|
|
1446
|
-
}
|
|
1035
|
+
function defaultOnError(error) {
|
|
1036
|
+
console.error("Error while in Aberdeen render:", error);
|
|
1037
|
+
return true;
|
|
1447
1038
|
}
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
* usually a better, declarative way. Although there are no hard guarantees on
|
|
1452
|
-
* how your changes interact with Aberdeen, in most cases results will not be
|
|
1453
|
-
* terribly surprising. Be careful within the parent element of onEach() though.
|
|
1454
|
-
*/
|
|
1455
|
-
export function getParentElement() {
|
|
1456
|
-
if (!currentScope || !currentScope._parentElement)
|
|
1457
|
-
throw new ScopeError(true);
|
|
1458
|
-
return currentScope._parentElement;
|
|
1039
|
+
var onError = defaultOnError;
|
|
1040
|
+
function setErrorHandler(handler) {
|
|
1041
|
+
onError = handler || defaultOnError;
|
|
1459
1042
|
}
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
* disappears or redraws.
|
|
1463
|
-
* @param clean - The function to be executed.
|
|
1464
|
-
*/
|
|
1465
|
-
export function clean(clean) {
|
|
1466
|
-
if (!currentScope)
|
|
1467
|
-
throw new ScopeError(false);
|
|
1468
|
-
currentScope._cleaners.push({ _clean: clean });
|
|
1043
|
+
function getParentElement() {
|
|
1044
|
+
return currentScope.parentElement;
|
|
1469
1045
|
}
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
* during its execution is updated.
|
|
1473
|
-
* Calls to `observe` can be nested, such that changes to `Store`s read by the inner function do
|
|
1474
|
-
* no cause the outer function to rerun.
|
|
1475
|
-
*
|
|
1476
|
-
* @param func - The function to be (repeatedly) executed.
|
|
1477
|
-
* @returns The mount id (usable for `unmount`) if this is a top-level observe.
|
|
1478
|
-
* @example
|
|
1479
|
-
* ```
|
|
1480
|
-
* let number = new Store(0)
|
|
1481
|
-
* let doubled = new Store()
|
|
1482
|
-
* setInterval(() => number.set(0|Math.random()*100)), 1000)
|
|
1483
|
-
*
|
|
1484
|
-
* observe(() => {
|
|
1485
|
-
* doubled.set(number.get() * 2)
|
|
1486
|
-
* })
|
|
1487
|
-
*
|
|
1488
|
-
* observe(() => {
|
|
1489
|
-
* console.log(doubled.get())
|
|
1490
|
-
* })
|
|
1491
|
-
*/
|
|
1492
|
-
export function observe(func) {
|
|
1493
|
-
return _mount(undefined, func, SimpleScope);
|
|
1046
|
+
function clean(cleaner) {
|
|
1047
|
+
currentScope.cleaners.push(cleaner);
|
|
1494
1048
|
}
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
* a setTimeout 0, run it immediately and synchronously when a change to one of
|
|
1498
|
-
* the observed `Store`s is made. Use this sparingly, as this prevents Aberdeen
|
|
1499
|
-
* from doing the usual batching and smart ordering of observers, leading to
|
|
1500
|
-
* performance problems and observing of 'weird' partial states.
|
|
1501
|
-
* @param func The function to be (repeatedly) executed.
|
|
1502
|
-
* @returns The mount id (usable for `unmount`) if this is a top-level observe.
|
|
1503
|
-
*/
|
|
1504
|
-
export function immediateObserve(func) {
|
|
1505
|
-
return _mount(undefined, func, ImmediateScope);
|
|
1049
|
+
function observe(func) {
|
|
1050
|
+
return new ResultScope(currentScope.parentElement, func).result;
|
|
1506
1051
|
}
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
* @param func - The function to be (repeatedly) executed, possibly adding DOM elements to `parentElement`.
|
|
1511
|
-
* @param parentElement - A DOM element that will be used as the parent element for calls to `node`.
|
|
1512
|
-
* @returns The mount id (usable for `unmount`) if this is a top-level mount.
|
|
1513
|
-
*
|
|
1514
|
-
* @example
|
|
1515
|
-
* ```
|
|
1516
|
-
* let store = new Store(0)
|
|
1517
|
-
* setInterval(() => store.modify(v => v+1), 1000)
|
|
1518
|
-
*
|
|
1519
|
-
* mount(document.body, () => {
|
|
1520
|
-
* node('h2', `${store.get()} seconds have passed`)
|
|
1521
|
-
* })
|
|
1522
|
-
* ```
|
|
1523
|
-
*
|
|
1524
|
-
* An example nesting {@link Store.observe} within `mount`:
|
|
1525
|
-
* ```
|
|
1526
|
-
* let selected = new Store(0)
|
|
1527
|
-
* let colors = new Store(new Map())
|
|
1528
|
-
*
|
|
1529
|
-
* mount(document.body, () => {
|
|
1530
|
-
* // This function will never rerun (as it does not read any `Store`s)
|
|
1531
|
-
* node('button', '<<', {click: () => selected.modify(n => n-1)})
|
|
1532
|
-
* node('button', '>>', {click: () => selected.modify(n => n+1)})
|
|
1533
|
-
*
|
|
1534
|
-
* observe(() => {
|
|
1535
|
-
* // This will rerun whenever `selected` changes, recreating the <h2> and <input>.
|
|
1536
|
-
* node('h2', '#'+selected.get())
|
|
1537
|
-
* node('input', {type: 'color', value: '#ffffff'}, colors.ref(selected.get()))
|
|
1538
|
-
* })
|
|
1539
|
-
*
|
|
1540
|
-
* observe(() => {
|
|
1541
|
-
* // This function will rerun when `selected` or the selected color changes.
|
|
1542
|
-
* // It will change the <body> background-color.
|
|
1543
|
-
* prop({style: {backgroundColor: colors.get(selected.get()) || 'white'}})
|
|
1544
|
-
* })
|
|
1545
|
-
* })
|
|
1546
|
-
* ```
|
|
1547
|
-
*/
|
|
1548
|
-
export function mount(parentElement, func) {
|
|
1549
|
-
return _mount(parentElement, func, SimpleScope);
|
|
1052
|
+
function immediateObserve(func) {
|
|
1053
|
+
new ImmediateScope(currentScope.parentElement, func);
|
|
1550
1054
|
}
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
function _mount(parentElement, func, MountScope) {
|
|
1554
|
-
let scope;
|
|
1555
|
-
if (parentElement || !currentScope) {
|
|
1556
|
-
scope = new MountScope(parentElement, undefined, 0, func);
|
|
1557
|
-
}
|
|
1558
|
-
else {
|
|
1559
|
-
scope = new MountScope(currentScope._parentElement, currentScope._lastChild || currentScope._precedingSibling, currentScope._queueOrder + 1, func);
|
|
1560
|
-
currentScope._lastChild = scope;
|
|
1561
|
-
}
|
|
1562
|
-
// Do the initial run
|
|
1563
|
-
scope._update();
|
|
1564
|
-
// Add it to our list of cleaners. Even if `scope` currently has
|
|
1565
|
-
// no cleaners, it may get them in a future refresh.
|
|
1566
|
-
if (currentScope) {
|
|
1567
|
-
currentScope._cleaners.push(scope);
|
|
1568
|
-
}
|
|
1569
|
-
else {
|
|
1570
|
-
topScopes.set(++maxTopScopeId, scope);
|
|
1571
|
-
return maxTopScopeId;
|
|
1572
|
-
}
|
|
1055
|
+
function mount(parentElement, func) {
|
|
1056
|
+
new MountScope(parentElement, func);
|
|
1573
1057
|
}
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
* @param id Optional mount number (as returned by `mount`, `observe` or `immediateObserve`). If `undefined`, unmount all.
|
|
1578
|
-
*/
|
|
1579
|
-
export function unmount(id) {
|
|
1580
|
-
if (id == null) {
|
|
1581
|
-
for (let scope of topScopes.values())
|
|
1582
|
-
scope._remove();
|
|
1583
|
-
topScopes.clear();
|
|
1584
|
-
}
|
|
1585
|
-
else {
|
|
1586
|
-
let scope = topScopes.get(id);
|
|
1587
|
-
if (!scope)
|
|
1588
|
-
throw new Error("No such mount " + id);
|
|
1589
|
-
scope._remove();
|
|
1590
|
-
}
|
|
1058
|
+
function unmountAll() {
|
|
1059
|
+
ROOT_SCOPE.remove();
|
|
1060
|
+
cssCount = 0;
|
|
1591
1061
|
}
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
*
|
|
1600
|
-
* let store = new Store(['a', 'b', 'c'])
|
|
1601
|
-
*
|
|
1602
|
-
* mount(document.body, () => {
|
|
1603
|
-
* // Prevent rerender when store changes
|
|
1604
|
-
* let msg = peek(() => `Store has ${store.count()} elements, and the first is ${store.get(0)}`))
|
|
1605
|
-
* text(msg)
|
|
1606
|
-
* })
|
|
1607
|
-
* ```
|
|
1608
|
-
*
|
|
1609
|
-
* In the above example `store.get(0)` could be replaced with `store.peek(0)` to achieve the
|
|
1610
|
-
* same result without `peek()` wrapping everything. There is no non-subscribing equivalent
|
|
1611
|
-
* for `count()` however.
|
|
1612
|
-
*/
|
|
1613
|
-
export function peek(func) {
|
|
1614
|
-
let savedScope = currentScope;
|
|
1615
|
-
currentScope = undefined;
|
|
1616
|
-
try {
|
|
1617
|
-
return func();
|
|
1618
|
-
}
|
|
1619
|
-
finally {
|
|
1620
|
-
currentScope = savedScope;
|
|
1621
|
-
}
|
|
1062
|
+
function peek(func) {
|
|
1063
|
+
peeking++;
|
|
1064
|
+
try {
|
|
1065
|
+
return func();
|
|
1066
|
+
} finally {
|
|
1067
|
+
peeking--;
|
|
1068
|
+
}
|
|
1622
1069
|
}
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
if (
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
}
|
|
1636
|
-
}
|
|
1637
|
-
}
|
|
1638
|
-
else if (prop === 'destroy') {
|
|
1639
|
-
onDestroyMap.set(el, value);
|
|
1640
|
-
}
|
|
1641
|
-
else if (typeof value === 'function') {
|
|
1642
|
-
// Set an event listener; remove it again on clean.
|
|
1643
|
-
el.addEventListener(prop, value);
|
|
1644
|
-
clean(() => el.removeEventListener(prop, value));
|
|
1645
|
-
}
|
|
1646
|
-
else if (prop === 'value' || prop === 'className' || prop === 'selectedIndex' || value === true || value === false) {
|
|
1647
|
-
// All boolean values and a few specific keys should be set as a property
|
|
1648
|
-
el[prop] = value;
|
|
1649
|
-
}
|
|
1650
|
-
else if (prop === 'text') {
|
|
1651
|
-
// `text` is set as textContent
|
|
1652
|
-
el.textContent = value;
|
|
1653
|
-
}
|
|
1654
|
-
else if ((prop === 'class' || prop === 'className') && typeof value === 'object') {
|
|
1655
|
-
// Allow setting classes using an object where the keys are the names and
|
|
1656
|
-
// the values are booleans stating whether to set or remove.
|
|
1657
|
-
for (let name in value) {
|
|
1658
|
-
if (value[name])
|
|
1659
|
-
el.classList.add(name);
|
|
1660
|
-
else
|
|
1661
|
-
el.classList.remove(name);
|
|
1662
|
-
}
|
|
1663
|
-
}
|
|
1664
|
-
else if (prop === 'style' && typeof value === 'object') {
|
|
1665
|
-
// `style` can receive an object
|
|
1666
|
-
Object.assign(el.style, value);
|
|
1667
|
-
}
|
|
1668
|
-
else {
|
|
1669
|
-
// Everything else is an HTML attribute
|
|
1670
|
-
el.setAttribute(prop, value);
|
|
1671
|
-
}
|
|
1070
|
+
function map(source, func) {
|
|
1071
|
+
let out = optProxy(source instanceof Array ? [] : {});
|
|
1072
|
+
onEach(source, (item, key) => {
|
|
1073
|
+
let value = func(item, key);
|
|
1074
|
+
if (value !== undefined) {
|
|
1075
|
+
out[key] = value;
|
|
1076
|
+
clean(() => {
|
|
1077
|
+
delete out[key];
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
});
|
|
1081
|
+
return out;
|
|
1672
1082
|
}
|
|
1673
|
-
function
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1083
|
+
function multiMap(source, func) {
|
|
1084
|
+
let out = optProxy({});
|
|
1085
|
+
onEach(source, (item, key) => {
|
|
1086
|
+
let pairs = func(item, key);
|
|
1087
|
+
if (pairs) {
|
|
1088
|
+
for (let key2 in pairs)
|
|
1089
|
+
out[key2] = pairs[key2];
|
|
1090
|
+
clean(() => {
|
|
1091
|
+
for (let key2 in pairs)
|
|
1092
|
+
delete out[key2];
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
});
|
|
1096
|
+
return out;
|
|
1097
|
+
}
|
|
1098
|
+
function partition(source, func) {
|
|
1099
|
+
const unproxiedOut = {};
|
|
1100
|
+
const out = proxy(unproxiedOut);
|
|
1101
|
+
onEach(source, (item, key) => {
|
|
1102
|
+
let rsp = func(item, key);
|
|
1103
|
+
if (rsp != null) {
|
|
1104
|
+
const buckets = rsp instanceof Array ? rsp : [rsp];
|
|
1105
|
+
if (buckets.length) {
|
|
1106
|
+
for (let bucket of buckets) {
|
|
1107
|
+
if (unproxiedOut[bucket])
|
|
1108
|
+
out[bucket][key] = item;
|
|
1109
|
+
else
|
|
1110
|
+
out[bucket] = { [key]: item };
|
|
1111
|
+
}
|
|
1112
|
+
clean(() => {
|
|
1113
|
+
for (let bucket of buckets) {
|
|
1114
|
+
delete out[bucket][key];
|
|
1115
|
+
if (isObjEmpty(unproxiedOut[bucket]))
|
|
1116
|
+
delete out[bucket];
|
|
1117
|
+
}
|
|
1689
1118
|
});
|
|
1690
|
-
|
|
1691
|
-
}
|
|
1692
|
-
else if (value instanceof Array) {
|
|
1693
|
-
let result = new ObsArray();
|
|
1694
|
-
for (let i = 0; i < value.length; i++) {
|
|
1695
|
-
let d = valueToData(value[i]);
|
|
1696
|
-
if (d !== undefined)
|
|
1697
|
-
result.rawSet(i, d);
|
|
1698
|
-
}
|
|
1699
|
-
return result;
|
|
1700
|
-
}
|
|
1701
|
-
else if (value.constructor === Object) {
|
|
1702
|
-
// A plain (literal) object
|
|
1703
|
-
let result = new ObsObject();
|
|
1704
|
-
for (let k in value) {
|
|
1705
|
-
let d = valueToData(value[k]);
|
|
1706
|
-
if (d !== undefined)
|
|
1707
|
-
result.rawSet(k, d);
|
|
1708
|
-
}
|
|
1709
|
-
return result;
|
|
1710
|
-
}
|
|
1711
|
-
else {
|
|
1712
|
-
// Any other type of object (including ObsCollection)
|
|
1713
|
-
return value;
|
|
1119
|
+
}
|
|
1714
1120
|
}
|
|
1121
|
+
});
|
|
1122
|
+
return out;
|
|
1715
1123
|
}
|
|
1716
|
-
function
|
|
1717
|
-
|
|
1124
|
+
function dump(data) {
|
|
1125
|
+
if (data && typeof data === "object") {
|
|
1126
|
+
$({ text: data instanceof Array ? "<array>" : "<object>" });
|
|
1127
|
+
$("ul", () => {
|
|
1128
|
+
onEach(data, (value, key) => {
|
|
1129
|
+
$("li:" + JSON.stringify(key) + ": ", () => {
|
|
1130
|
+
dump(value);
|
|
1131
|
+
});
|
|
1132
|
+
});
|
|
1133
|
+
});
|
|
1134
|
+
} else {
|
|
1135
|
+
$({ text: JSON.stringify(data) });
|
|
1136
|
+
}
|
|
1137
|
+
return data;
|
|
1718
1138
|
}
|
|
1719
|
-
/* c8 ignore start */
|
|
1720
1139
|
function internalError(code) {
|
|
1721
|
-
|
|
1722
|
-
setTimeout(() => { throw error; }, 0);
|
|
1723
|
-
}
|
|
1724
|
-
/* c8 ignore end */
|
|
1725
|
-
function handleError(e) {
|
|
1726
|
-
// Throw the error async, so the rest of the rendering can continue
|
|
1727
|
-
setTimeout(() => { throw e; }, 0);
|
|
1728
|
-
}
|
|
1729
|
-
class ScopeError extends Error {
|
|
1730
|
-
constructor(mount) {
|
|
1731
|
-
super(`Operation not permitted outside of ${mount ? "a mount" : "an observe"}() scope`);
|
|
1732
|
-
}
|
|
1140
|
+
throw new Error("Aberdeen internal error " + code);
|
|
1733
1141
|
}
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1142
|
+
function handleError(e, showMessage) {
|
|
1143
|
+
try {
|
|
1144
|
+
if (onError(e) === false)
|
|
1145
|
+
showMessage = false;
|
|
1146
|
+
} catch (e2) {
|
|
1147
|
+
console.error(e2);
|
|
1148
|
+
}
|
|
1149
|
+
try {
|
|
1150
|
+
if (showMessage)
|
|
1151
|
+
$("div.aberdeen-error:Error");
|
|
1152
|
+
} catch {}
|
|
1744
1153
|
}
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1154
|
+
function withEmitHandler(handler, func) {
|
|
1155
|
+
const oldEmitHandler = emit;
|
|
1156
|
+
emit = handler;
|
|
1157
|
+
try {
|
|
1158
|
+
func();
|
|
1159
|
+
} finally {
|
|
1160
|
+
emit = oldEmitHandler;
|
|
1161
|
+
}
|
|
1751
1162
|
}
|
|
1752
|
-
// @ts-ignore
|
|
1753
|
-
// c8 ignore next
|
|
1754
1163
|
if (!String.prototype.replaceAll)
|
|
1755
|
-
|
|
1756
|
-
|
|
1164
|
+
String.prototype.replaceAll = function(from, to) {
|
|
1165
|
+
return this.split(from).join(to);
|
|
1166
|
+
};
|
|
1167
|
+
export {
|
|
1168
|
+
withEmitHandler,
|
|
1169
|
+
unproxy,
|
|
1170
|
+
unmountAll,
|
|
1171
|
+
setErrorHandler,
|
|
1172
|
+
runQueue,
|
|
1173
|
+
ref,
|
|
1174
|
+
proxy,
|
|
1175
|
+
peek,
|
|
1176
|
+
partition,
|
|
1177
|
+
onEach,
|
|
1178
|
+
observe,
|
|
1179
|
+
multiMap,
|
|
1180
|
+
mount,
|
|
1181
|
+
map,
|
|
1182
|
+
isEmpty,
|
|
1183
|
+
invertString,
|
|
1184
|
+
insertCss,
|
|
1185
|
+
immediateObserve,
|
|
1186
|
+
getParentElement,
|
|
1187
|
+
dump,
|
|
1188
|
+
defaultEmitHandler,
|
|
1189
|
+
count,
|
|
1190
|
+
copy,
|
|
1191
|
+
clone,
|
|
1192
|
+
clean,
|
|
1193
|
+
SHALLOW,
|
|
1194
|
+
MERGE,
|
|
1195
|
+
$
|
|
1196
|
+
};
|
|
1197
|
+
|
|
1198
|
+
//# debugId=DE860A8F16A3286C64756E2164756E21
|
|
1199
|
+
//# sourceMappingURL=aberdeen.js.map
|