aberdeen 0.1.2 → 0.2.2
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/README.md +11 -5
- package/dist/aberdeen.d.ts +28 -153
- package/dist/aberdeen.js +358 -444
- package/dist/aberdeen.js.map +1 -0
- package/dist/prediction.d.ts +29 -0
- package/dist/prediction.js +110 -0
- package/dist/prediction.js.map +1 -0
- package/dist/route.d.ts +16 -0
- package/dist/route.js +119 -0
- package/dist/route.js.map +1 -0
- package/dist/transitions.d.ts +18 -0
- package/dist/transitions.js +67 -0
- package/dist/transitions.js.map +1 -0
- package/dist-min/aberdeen.d.ts +573 -0
- package/dist-min/aberdeen.js +2 -0
- package/dist-min/aberdeen.js.map +1 -0
- package/dist-min/prediction.d.ts +29 -0
- package/dist-min/prediction.js +2 -0
- package/dist-min/prediction.js.map +1 -0
- package/dist-min/route.d.ts +16 -0
- package/dist-min/route.js +2 -0
- package/dist-min/route.js.map +1 -0
- package/dist-min/transitions.d.ts +18 -0
- package/dist-min/transitions.js +2 -0
- package/dist-min/transitions.js.map +1 -0
- package/package.json +35 -24
- package/dist/aberdeen.min.js +0 -1
package/dist/aberdeen.js
CHANGED
|
@@ -3,7 +3,6 @@ let queueSet = new Set();
|
|
|
3
3
|
let queueOrdered = true;
|
|
4
4
|
let runQueueDepth = 0;
|
|
5
5
|
let queueIndex;
|
|
6
|
-
let recordingPatch;
|
|
7
6
|
function queue(runner) {
|
|
8
7
|
if (queueSet.has(runner))
|
|
9
8
|
return;
|
|
@@ -13,7 +12,7 @@ function queue(runner) {
|
|
|
13
12
|
if (!queueArray.length) {
|
|
14
13
|
setTimeout(runQueue, 0);
|
|
15
14
|
}
|
|
16
|
-
else if (runner.
|
|
15
|
+
else if (runner._queueOrder < queueArray[queueArray.length - 1]._queueOrder) {
|
|
17
16
|
queueOrdered = false;
|
|
18
17
|
}
|
|
19
18
|
queueArray.push(runner);
|
|
@@ -27,7 +26,7 @@ function runQueue() {
|
|
|
27
26
|
queueArray.splice(0, queueIndex);
|
|
28
27
|
queueIndex = 0;
|
|
29
28
|
// Order queued observers by depth, lowest first.
|
|
30
|
-
queueArray.sort((a, b) => a.
|
|
29
|
+
queueArray.sort((a, b) => a._queueOrder - b._queueOrder);
|
|
31
30
|
queueOrdered = true;
|
|
32
31
|
}
|
|
33
32
|
// Process the rest of what's currently in the queue.
|
|
@@ -35,7 +34,7 @@ function runQueue() {
|
|
|
35
34
|
for (; queueIndex < batchEndIndex && queueOrdered; queueIndex++) {
|
|
36
35
|
let runner = queueArray[queueIndex];
|
|
37
36
|
queueSet.delete(runner);
|
|
38
|
-
runner.
|
|
37
|
+
runner._queueRun();
|
|
39
38
|
}
|
|
40
39
|
// If new items have been added to the queue while processing the previous
|
|
41
40
|
// batch, we'll need to run this loop again.
|
|
@@ -46,7 +45,6 @@ function runQueue() {
|
|
|
46
45
|
runQueueDepth = 0;
|
|
47
46
|
onCreateEnabled = false;
|
|
48
47
|
}
|
|
49
|
-
let scheduleOrder = 1000;
|
|
50
48
|
/**
|
|
51
49
|
* Schedule a DOM read operation to be executed in Aberdeen's internal task queue.
|
|
52
50
|
*
|
|
@@ -65,8 +63,8 @@ let scheduleOrder = 1000;
|
|
|
65
63
|
* @param func The function to be executed as a DOM read operation.
|
|
66
64
|
*/
|
|
67
65
|
export function scheduleDomReader(func) {
|
|
68
|
-
let order = (queueIndex != null && queueIndex < queueArray.length && queueArray[queueIndex].
|
|
69
|
-
queue({
|
|
66
|
+
let order = (queueIndex != null && queueIndex < queueArray.length && queueArray[queueIndex]._queueOrder >= 1000) ? ((queueArray[queueIndex]._queueOrder + 1) & (~1)) : 1000;
|
|
67
|
+
queue({ _queueOrder: order, _queueRun: func });
|
|
70
68
|
}
|
|
71
69
|
/**
|
|
72
70
|
* Schedule a DOM write operation to be executed in Aberdeen's internal task queue.
|
|
@@ -86,8 +84,8 @@ export function scheduleDomReader(func) {
|
|
|
86
84
|
* @param func The function to be executed as a DOM write operation.
|
|
87
85
|
*/
|
|
88
86
|
export function scheduleDomWriter(func) {
|
|
89
|
-
let order = (queueIndex != null && queueIndex < queueArray.length && queueArray[queueIndex].
|
|
90
|
-
queue({
|
|
87
|
+
let order = (queueIndex != null && queueIndex < queueArray.length && queueArray[queueIndex]._queueOrder >= 1000) ? (queueArray[queueIndex]._queueOrder | 1) : 1001;
|
|
88
|
+
queue({ _queueOrder: order, _queueRun: func });
|
|
91
89
|
}
|
|
92
90
|
/**
|
|
93
91
|
* Given an integer number, a string or an array of these, this function returns a string that can be used
|
|
@@ -128,6 +126,7 @@ function numToString(num, neg) {
|
|
|
128
126
|
}
|
|
129
127
|
/*
|
|
130
128
|
* Scope
|
|
129
|
+
* @internal
|
|
131
130
|
*
|
|
132
131
|
* A `Scope` is created with a `render` function that is run initially,
|
|
133
132
|
* and again when any of the `Store`s that this function reads are changed. Any
|
|
@@ -140,54 +139,54 @@ class Scope {
|
|
|
140
139
|
constructor(parentElement, precedingSibling, queueOrder) {
|
|
141
140
|
// The list of clean functions to be called when this scope is cleaned. These can
|
|
142
141
|
// be for child scopes, subscriptions as well as `clean(..)` hooks.
|
|
143
|
-
this.
|
|
142
|
+
this._cleaners = [];
|
|
144
143
|
// Set to true after the scope has been cleaned, causing any spurious reruns to
|
|
145
144
|
// be ignored.
|
|
146
|
-
this.
|
|
147
|
-
this.
|
|
148
|
-
this.
|
|
149
|
-
this.
|
|
145
|
+
this._isDead = false;
|
|
146
|
+
this._parentElement = parentElement;
|
|
147
|
+
this._precedingSibling = precedingSibling;
|
|
148
|
+
this._queueOrder = queueOrder;
|
|
150
149
|
}
|
|
151
150
|
// Get a reference to the last Node preceding this Scope, or undefined if there is none
|
|
152
|
-
|
|
151
|
+
_findPrecedingNode(stopAt = undefined) {
|
|
153
152
|
let cur = this;
|
|
154
153
|
let pre;
|
|
155
|
-
while ((pre = cur.
|
|
154
|
+
while ((pre = cur._precedingSibling) && pre !== stopAt) {
|
|
156
155
|
if (pre instanceof Node)
|
|
157
156
|
return pre;
|
|
158
|
-
let node = pre.
|
|
157
|
+
let node = pre._findLastNode();
|
|
159
158
|
if (node)
|
|
160
159
|
return node;
|
|
161
160
|
cur = pre;
|
|
162
161
|
}
|
|
163
162
|
}
|
|
164
163
|
// Get a reference to the last Node within this scope and parentElement
|
|
165
|
-
|
|
166
|
-
if (this.
|
|
167
|
-
if (this.
|
|
168
|
-
return this.
|
|
164
|
+
_findLastNode() {
|
|
165
|
+
if (this._lastChild) {
|
|
166
|
+
if (this._lastChild instanceof Node)
|
|
167
|
+
return this._lastChild;
|
|
169
168
|
else
|
|
170
|
-
return this.
|
|
169
|
+
return this._lastChild._findLastNode() || this._lastChild._findPrecedingNode(this._precedingSibling);
|
|
171
170
|
}
|
|
172
171
|
}
|
|
173
|
-
|
|
174
|
-
if (!this.
|
|
172
|
+
_addNode(node) {
|
|
173
|
+
if (!this._parentElement)
|
|
175
174
|
throw new ScopeError(true);
|
|
176
|
-
let prevNode = this.
|
|
177
|
-
this.
|
|
178
|
-
this.
|
|
175
|
+
let prevNode = this._findLastNode() || this._findPrecedingNode();
|
|
176
|
+
this._parentElement.insertBefore(node, prevNode ? prevNode.nextSibling : this._parentElement.firstChild);
|
|
177
|
+
this._lastChild = node;
|
|
179
178
|
}
|
|
180
|
-
|
|
181
|
-
if (this.
|
|
182
|
-
let lastNode = this.
|
|
179
|
+
_remove() {
|
|
180
|
+
if (this._parentElement) {
|
|
181
|
+
let lastNode = this._findLastNode();
|
|
183
182
|
if (lastNode) {
|
|
184
183
|
// at least one DOM node to be removed
|
|
185
|
-
let nextNode = this.
|
|
186
|
-
nextNode = (nextNode ? nextNode.nextSibling : this.
|
|
187
|
-
this.
|
|
184
|
+
let nextNode = this._findPrecedingNode();
|
|
185
|
+
nextNode = (nextNode ? nextNode.nextSibling : this._parentElement.firstChild);
|
|
186
|
+
this._lastChild = undefined;
|
|
188
187
|
// Keep removing DOM nodes starting at our first node, until we encounter the last node
|
|
189
188
|
while (true) {
|
|
190
|
-
/*
|
|
189
|
+
/* c8 ignore next */
|
|
191
190
|
if (!nextNode)
|
|
192
191
|
return internalError(1);
|
|
193
192
|
const node = nextNode;
|
|
@@ -207,7 +206,7 @@ class Scope {
|
|
|
207
206
|
// Ignore the deleting element
|
|
208
207
|
}
|
|
209
208
|
else {
|
|
210
|
-
this.
|
|
209
|
+
this._parentElement.removeChild(node);
|
|
211
210
|
}
|
|
212
211
|
if (node === lastNode)
|
|
213
212
|
break;
|
|
@@ -218,37 +217,36 @@ class Scope {
|
|
|
218
217
|
this._clean();
|
|
219
218
|
}
|
|
220
219
|
_clean() {
|
|
221
|
-
this.
|
|
222
|
-
for (let cleaner of this.
|
|
220
|
+
this._isDead = true;
|
|
221
|
+
for (let cleaner of this._cleaners) {
|
|
223
222
|
cleaner._clean(this);
|
|
224
223
|
}
|
|
225
|
-
this.
|
|
224
|
+
this._cleaners.length = 0;
|
|
226
225
|
}
|
|
227
|
-
|
|
226
|
+
_onChange(index, newData, oldData) {
|
|
228
227
|
queue(this);
|
|
229
228
|
}
|
|
230
229
|
}
|
|
231
230
|
class SimpleScope extends Scope {
|
|
232
231
|
constructor(parentElement, precedingSibling, queueOrder, renderer) {
|
|
233
232
|
super(parentElement, precedingSibling, queueOrder);
|
|
234
|
-
this.
|
|
233
|
+
this._renderer = renderer;
|
|
235
234
|
}
|
|
236
|
-
|
|
237
|
-
/*
|
|
238
|
-
if (currentScope)
|
|
235
|
+
_queueRun() {
|
|
236
|
+
/* c8 ignore next */
|
|
237
|
+
if (currentScope)
|
|
239
238
|
internalError(2);
|
|
240
|
-
|
|
241
|
-
if (this.isDead)
|
|
239
|
+
if (this._isDead)
|
|
242
240
|
return;
|
|
243
|
-
this.
|
|
244
|
-
this.
|
|
245
|
-
this.
|
|
241
|
+
this._remove();
|
|
242
|
+
this._isDead = false;
|
|
243
|
+
this._update();
|
|
246
244
|
}
|
|
247
|
-
|
|
245
|
+
_update() {
|
|
248
246
|
let savedScope = currentScope;
|
|
249
247
|
currentScope = this;
|
|
250
248
|
try {
|
|
251
|
-
this.
|
|
249
|
+
this._renderer();
|
|
252
250
|
}
|
|
253
251
|
catch (e) {
|
|
254
252
|
// Throw the error async, so the rest of the rendering can continue
|
|
@@ -257,16 +255,47 @@ class SimpleScope extends Scope {
|
|
|
257
255
|
currentScope = savedScope;
|
|
258
256
|
}
|
|
259
257
|
}
|
|
258
|
+
let immediateQueue = new Set();
|
|
259
|
+
class ImmediateScope extends SimpleScope {
|
|
260
|
+
_onChange(index, newData, oldData) {
|
|
261
|
+
immediateQueue.add(this);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
let immediateQueuerRunning = false;
|
|
265
|
+
function runImmediateQueue() {
|
|
266
|
+
if (immediateQueuerRunning)
|
|
267
|
+
return;
|
|
268
|
+
for (let count = 0; immediateQueue.size; count++) {
|
|
269
|
+
if (count > 42) {
|
|
270
|
+
immediateQueue.clear();
|
|
271
|
+
throw new Error("Too many recursive updates from immediate-mode observes");
|
|
272
|
+
}
|
|
273
|
+
immediateQueuerRunning = true;
|
|
274
|
+
let copy = immediateQueue;
|
|
275
|
+
immediateQueue = new Set();
|
|
276
|
+
let savedScope = currentScope;
|
|
277
|
+
currentScope = undefined;
|
|
278
|
+
try {
|
|
279
|
+
for (const scope of copy) {
|
|
280
|
+
scope._queueRun();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
finally {
|
|
284
|
+
currentScope = savedScope;
|
|
285
|
+
immediateQueuerRunning = false;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
260
289
|
class IsEmptyObserver {
|
|
261
290
|
constructor(scope, collection, triggerCount) {
|
|
262
291
|
this.scope = scope;
|
|
263
292
|
this.collection = collection;
|
|
264
293
|
this.triggerCount = triggerCount;
|
|
265
|
-
this.count = collection.
|
|
266
|
-
collection.
|
|
267
|
-
scope.
|
|
294
|
+
this.count = collection._getCount();
|
|
295
|
+
collection._addObserver(ANY_INDEX, this);
|
|
296
|
+
scope._cleaners.push(this);
|
|
268
297
|
}
|
|
269
|
-
|
|
298
|
+
_onChange(index, newData, oldData) {
|
|
270
299
|
if (newData === undefined) {
|
|
271
300
|
// oldData is guaranteed not to be undefined
|
|
272
301
|
if (this.triggerCount || !--this.count)
|
|
@@ -278,106 +307,105 @@ class IsEmptyObserver {
|
|
|
278
307
|
}
|
|
279
308
|
}
|
|
280
309
|
_clean() {
|
|
281
|
-
this.collection.
|
|
310
|
+
this.collection._removeObserver(ANY_INDEX, this);
|
|
282
311
|
}
|
|
283
312
|
}
|
|
313
|
+
/** @internal */
|
|
284
314
|
class OnEachScope extends Scope {
|
|
285
315
|
constructor(parentElement, precedingSibling, queueOrder, collection, renderer, makeSortKey) {
|
|
286
316
|
super(parentElement, precedingSibling, queueOrder);
|
|
287
317
|
/** The ordered list of currently item scopes */
|
|
288
|
-
this.
|
|
318
|
+
this._byPosition = [];
|
|
289
319
|
/** The item scopes in a Map by index */
|
|
290
|
-
this.
|
|
320
|
+
this._byIndex = new Map();
|
|
291
321
|
/** Indexes that have been created/removed and need to be handled in the next `queueRun` */
|
|
292
|
-
this.
|
|
293
|
-
this.
|
|
294
|
-
this.
|
|
295
|
-
this.
|
|
296
|
-
this.
|
|
322
|
+
this._newIndexes = new Set();
|
|
323
|
+
this._removedIndexes = new Set();
|
|
324
|
+
this._collection = collection;
|
|
325
|
+
this._renderer = renderer;
|
|
326
|
+
this._makeSortKey = makeSortKey;
|
|
297
327
|
}
|
|
298
328
|
// toString(): string {
|
|
299
329
|
// return `OnEachScope(collection=${this.collection})`
|
|
300
330
|
// }
|
|
301
|
-
|
|
331
|
+
_onChange(index, newData, oldData) {
|
|
302
332
|
if (oldData === undefined) {
|
|
303
|
-
if (this.
|
|
304
|
-
this.
|
|
333
|
+
if (this._removedIndexes.has(index)) {
|
|
334
|
+
this._removedIndexes.delete(index);
|
|
305
335
|
}
|
|
306
336
|
else {
|
|
307
|
-
this.
|
|
337
|
+
this._newIndexes.add(index);
|
|
308
338
|
queue(this);
|
|
309
339
|
}
|
|
310
340
|
}
|
|
311
341
|
else if (newData === undefined) {
|
|
312
|
-
if (this.
|
|
313
|
-
this.
|
|
342
|
+
if (this._newIndexes.has(index)) {
|
|
343
|
+
this._newIndexes.delete(index);
|
|
314
344
|
}
|
|
315
345
|
else {
|
|
316
|
-
this.
|
|
346
|
+
this._removedIndexes.add(index);
|
|
317
347
|
queue(this);
|
|
318
348
|
}
|
|
319
349
|
}
|
|
320
350
|
}
|
|
321
|
-
|
|
322
|
-
if (this.
|
|
351
|
+
_queueRun() {
|
|
352
|
+
if (this._isDead)
|
|
323
353
|
return;
|
|
324
|
-
let indexes = this.
|
|
325
|
-
this.
|
|
354
|
+
let indexes = this._removedIndexes;
|
|
355
|
+
this._removedIndexes = new Set();
|
|
326
356
|
indexes.forEach(index => {
|
|
327
|
-
this.
|
|
357
|
+
this._removeChild(index);
|
|
328
358
|
});
|
|
329
|
-
indexes = this.
|
|
330
|
-
this.
|
|
359
|
+
indexes = this._newIndexes;
|
|
360
|
+
this._newIndexes = new Set();
|
|
331
361
|
indexes.forEach(index => {
|
|
332
|
-
this.
|
|
362
|
+
this._addChild(index);
|
|
333
363
|
});
|
|
334
364
|
}
|
|
335
365
|
_clean() {
|
|
336
366
|
super._clean();
|
|
337
|
-
this.
|
|
338
|
-
for (const [index, scope] of this.
|
|
367
|
+
this._collection._observers.delete(this);
|
|
368
|
+
for (const [index, scope] of this._byIndex) {
|
|
339
369
|
scope._clean();
|
|
340
370
|
}
|
|
341
371
|
// Help garbage collection:
|
|
342
|
-
this.
|
|
343
|
-
this.
|
|
372
|
+
this._byPosition.length = 0;
|
|
373
|
+
this._byIndex.clear();
|
|
344
374
|
}
|
|
345
|
-
|
|
346
|
-
/*
|
|
347
|
-
if (!currentScope)
|
|
375
|
+
_renderInitial() {
|
|
376
|
+
/* c8 ignore next */
|
|
377
|
+
if (!currentScope)
|
|
348
378
|
return internalError(3);
|
|
349
|
-
}
|
|
350
379
|
let parentScope = currentScope;
|
|
351
|
-
this.
|
|
380
|
+
this._collection._iterateIndexes(this);
|
|
352
381
|
currentScope = parentScope;
|
|
353
382
|
}
|
|
354
|
-
|
|
355
|
-
let scope = new OnEachItemScope(this.
|
|
356
|
-
this.
|
|
357
|
-
scope.
|
|
383
|
+
_addChild(itemIndex) {
|
|
384
|
+
let scope = new OnEachItemScope(this._parentElement, undefined, this._queueOrder + 1, this, itemIndex);
|
|
385
|
+
this._byIndex.set(itemIndex, scope);
|
|
386
|
+
scope._update();
|
|
358
387
|
// We're not adding a cleaner here, as we'll be calling them from our _clean function
|
|
359
388
|
}
|
|
360
|
-
|
|
361
|
-
let scope = this.
|
|
362
|
-
/*
|
|
363
|
-
if (!scope)
|
|
389
|
+
_removeChild(itemIndex) {
|
|
390
|
+
let scope = this._byIndex.get(itemIndex);
|
|
391
|
+
/* c8 ignore next */
|
|
392
|
+
if (!scope)
|
|
364
393
|
return internalError(6);
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
this.
|
|
368
|
-
this.removeFromPosition(scope);
|
|
394
|
+
scope._remove();
|
|
395
|
+
this._byIndex.delete(itemIndex);
|
|
396
|
+
this._removeFromPosition(scope);
|
|
369
397
|
}
|
|
370
|
-
|
|
398
|
+
_findPosition(sortStr) {
|
|
371
399
|
// In case of duplicate `sortStr`s, this will return the first match.
|
|
372
|
-
let items = this.
|
|
400
|
+
let items = this._byPosition;
|
|
373
401
|
let min = 0, max = items.length;
|
|
374
402
|
// Fast-path for elements that are already ordered (as is the case when working with arrays ordered by index)
|
|
375
|
-
if (!max || sortStr > items[max - 1].
|
|
403
|
+
if (!max || sortStr > items[max - 1]._sortStr)
|
|
376
404
|
return max;
|
|
377
405
|
// Binary search for the insert position
|
|
378
406
|
while (min < max) {
|
|
379
407
|
let mid = (min + max) >> 1;
|
|
380
|
-
if (items[mid].
|
|
408
|
+
if (items[mid]._sortStr < sortStr) {
|
|
381
409
|
min = mid + 1;
|
|
382
410
|
}
|
|
383
411
|
else {
|
|
@@ -386,101 +414,100 @@ class OnEachScope extends Scope {
|
|
|
386
414
|
}
|
|
387
415
|
return min;
|
|
388
416
|
}
|
|
389
|
-
|
|
390
|
-
let pos = this.
|
|
391
|
-
this.
|
|
417
|
+
_insertAtPosition(child) {
|
|
418
|
+
let pos = this._findPosition(child._sortStr);
|
|
419
|
+
this._byPosition.splice(pos, 0, child);
|
|
392
420
|
// Based on the position in the list, set the precedingSibling for the new Scope
|
|
393
421
|
// and for the next sibling.
|
|
394
|
-
let nextSibling = this.
|
|
422
|
+
let nextSibling = this._byPosition[pos + 1];
|
|
395
423
|
if (nextSibling) {
|
|
396
|
-
child.
|
|
397
|
-
nextSibling.
|
|
424
|
+
child._precedingSibling = nextSibling._precedingSibling;
|
|
425
|
+
nextSibling._precedingSibling = child;
|
|
398
426
|
}
|
|
399
427
|
else {
|
|
400
|
-
child.
|
|
401
|
-
this.
|
|
428
|
+
child._precedingSibling = this._lastChild || this._precedingSibling;
|
|
429
|
+
this._lastChild = child;
|
|
402
430
|
}
|
|
403
431
|
}
|
|
404
|
-
|
|
405
|
-
if (child.
|
|
432
|
+
_removeFromPosition(child) {
|
|
433
|
+
if (child._sortStr === '')
|
|
406
434
|
return;
|
|
407
|
-
let pos = this.
|
|
435
|
+
let pos = this._findPosition(child._sortStr);
|
|
408
436
|
while (true) {
|
|
409
|
-
if (this.
|
|
437
|
+
if (this._byPosition[pos] === child) {
|
|
410
438
|
// Yep, this is the right scope
|
|
411
|
-
this.
|
|
412
|
-
if (pos < this.
|
|
413
|
-
let nextSibling = this.
|
|
414
|
-
/*
|
|
439
|
+
this._byPosition.splice(pos, 1);
|
|
440
|
+
if (pos < this._byPosition.length) {
|
|
441
|
+
let nextSibling = this._byPosition[pos];
|
|
442
|
+
/* c8 ignore next */
|
|
415
443
|
if (!nextSibling)
|
|
416
444
|
return internalError(8);
|
|
417
|
-
/*
|
|
418
|
-
if (nextSibling.
|
|
445
|
+
/* c8 ignore next */
|
|
446
|
+
if (nextSibling._precedingSibling !== child)
|
|
419
447
|
return internalError(13);
|
|
420
|
-
nextSibling.
|
|
448
|
+
nextSibling._precedingSibling = child._precedingSibling;
|
|
421
449
|
}
|
|
422
450
|
else {
|
|
423
|
-
/*
|
|
424
|
-
if (child !== this.
|
|
451
|
+
/* c8 ignore next */
|
|
452
|
+
if (child !== this._lastChild)
|
|
425
453
|
return internalError(12);
|
|
426
|
-
this.
|
|
454
|
+
this._lastChild = child._precedingSibling === this._precedingSibling ? undefined : child._precedingSibling;
|
|
427
455
|
}
|
|
428
456
|
return;
|
|
429
457
|
}
|
|
430
458
|
// There may be another Scope with the same sortStr
|
|
431
|
-
/*
|
|
432
|
-
if (++pos >= this.
|
|
459
|
+
/* c8 ignore next */
|
|
460
|
+
if (++pos >= this._byPosition.length || this._byPosition[pos]._sortStr !== child._sortStr)
|
|
433
461
|
return internalError(5);
|
|
434
|
-
}
|
|
435
462
|
}
|
|
436
463
|
}
|
|
437
464
|
}
|
|
465
|
+
/** @internal */
|
|
438
466
|
class OnEachItemScope extends Scope {
|
|
439
467
|
constructor(parentElement, precedingSibling, queueOrder, parent, itemIndex) {
|
|
440
468
|
super(parentElement, precedingSibling, queueOrder);
|
|
441
|
-
this.
|
|
442
|
-
this.
|
|
443
|
-
this.
|
|
469
|
+
this._sortStr = "";
|
|
470
|
+
this._parent = parent;
|
|
471
|
+
this._itemIndex = itemIndex;
|
|
444
472
|
}
|
|
445
473
|
// toString(): string {
|
|
446
474
|
// return `OnEachItemScope(itemIndex=${this.itemIndex} parentElement=${this.parentElement} parent=${this.parent} precedingSibling=${this.precedingSibling} lastChild=${this.lastChild})`
|
|
447
475
|
// }
|
|
448
|
-
|
|
449
|
-
/*
|
|
450
|
-
if (currentScope)
|
|
476
|
+
_queueRun() {
|
|
477
|
+
/* c8 ignore next */
|
|
478
|
+
if (currentScope)
|
|
451
479
|
internalError(4);
|
|
452
|
-
|
|
453
|
-
if (this.isDead)
|
|
480
|
+
if (this._isDead)
|
|
454
481
|
return;
|
|
455
|
-
this.
|
|
456
|
-
this.
|
|
457
|
-
this.
|
|
482
|
+
this._remove();
|
|
483
|
+
this._isDead = false;
|
|
484
|
+
this._update();
|
|
458
485
|
}
|
|
459
|
-
|
|
486
|
+
_update() {
|
|
460
487
|
// Have the makeSortKey function return an ordering int/string/array.
|
|
461
488
|
// Since makeSortKey may get() the Store, we'll need to set currentScope first.
|
|
462
489
|
let savedScope = currentScope;
|
|
463
490
|
currentScope = this;
|
|
464
|
-
let itemStore = new Store(this.
|
|
491
|
+
let itemStore = new Store(this._parent._collection, this._itemIndex);
|
|
465
492
|
let sortKey;
|
|
466
493
|
try {
|
|
467
|
-
sortKey = this.
|
|
494
|
+
sortKey = this._parent._makeSortKey(itemStore);
|
|
468
495
|
}
|
|
469
496
|
catch (e) {
|
|
470
497
|
handleError(e);
|
|
471
498
|
}
|
|
472
|
-
let oldSortStr = this.
|
|
499
|
+
let oldSortStr = this._sortStr;
|
|
473
500
|
let newSortStr = sortKey == null ? '' : sortKeyToString(sortKey);
|
|
474
501
|
if (oldSortStr !== '' && oldSortStr !== newSortStr) {
|
|
475
|
-
this.
|
|
502
|
+
this._parent._removeFromPosition(this);
|
|
476
503
|
}
|
|
477
|
-
this.
|
|
504
|
+
this._sortStr = newSortStr;
|
|
478
505
|
if (newSortStr !== '') {
|
|
479
506
|
if (newSortStr !== oldSortStr) {
|
|
480
|
-
this.
|
|
507
|
+
this._parent._insertAtPosition(this);
|
|
481
508
|
}
|
|
482
509
|
try {
|
|
483
|
-
this.
|
|
510
|
+
this._parent._renderer(itemStore);
|
|
484
511
|
}
|
|
485
512
|
catch (e) {
|
|
486
513
|
handleError(e);
|
|
@@ -498,49 +525,45 @@ let currentScope;
|
|
|
498
525
|
* A special Node observer index to subscribe to any value in the map changing.
|
|
499
526
|
*/
|
|
500
527
|
const ANY_INDEX = {};
|
|
501
|
-
|
|
528
|
+
/** @internal */
|
|
529
|
+
export class ObsCollection {
|
|
502
530
|
constructor() {
|
|
503
|
-
this.
|
|
531
|
+
this._observers = new Map();
|
|
504
532
|
}
|
|
505
533
|
// toString(): string {
|
|
506
534
|
// return JSON.stringify(peek(() => this.getRecursive(3)))
|
|
507
535
|
// }
|
|
508
|
-
|
|
536
|
+
_addObserver(index, observer) {
|
|
509
537
|
observer = observer;
|
|
510
|
-
let obsSet = this.
|
|
538
|
+
let obsSet = this._observers.get(index);
|
|
511
539
|
if (obsSet) {
|
|
512
540
|
if (obsSet.has(observer))
|
|
513
541
|
return false;
|
|
514
542
|
obsSet.add(observer);
|
|
515
543
|
}
|
|
516
544
|
else {
|
|
517
|
-
this.
|
|
545
|
+
this._observers.set(index, new Set([observer]));
|
|
518
546
|
}
|
|
519
547
|
return true;
|
|
520
548
|
}
|
|
521
|
-
|
|
522
|
-
let obsSet = this.
|
|
549
|
+
_removeObserver(index, observer) {
|
|
550
|
+
let obsSet = this._observers.get(index);
|
|
523
551
|
obsSet.delete(observer);
|
|
524
552
|
}
|
|
525
553
|
emitChange(index, newData, oldData) {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
obsSet.forEach(observer => observer.onChange(index, newData, oldData));
|
|
533
|
-
obsSet = this.observers.get(ANY_INDEX);
|
|
534
|
-
if (obsSet)
|
|
535
|
-
obsSet.forEach(observer => observer.onChange(index, newData, oldData));
|
|
536
|
-
}
|
|
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));
|
|
537
560
|
}
|
|
538
561
|
_clean(observer) {
|
|
539
|
-
this.
|
|
562
|
+
this._removeObserver(ANY_INDEX, observer);
|
|
540
563
|
}
|
|
541
|
-
|
|
564
|
+
_setIndex(index, newValue, deleteMissing) {
|
|
542
565
|
const curData = this.rawGet(index);
|
|
543
|
-
if (!(curData instanceof ObsCollection) || newValue instanceof Store || !curData.
|
|
566
|
+
if (!(curData instanceof ObsCollection) || newValue instanceof Store || !curData._merge(newValue, deleteMissing)) {
|
|
544
567
|
let newData = valueToData(newValue);
|
|
545
568
|
if (newData !== curData) {
|
|
546
569
|
this.rawSet(index, newData);
|
|
@@ -549,67 +572,71 @@ class ObsCollection {
|
|
|
549
572
|
}
|
|
550
573
|
}
|
|
551
574
|
}
|
|
575
|
+
/** @internal */
|
|
552
576
|
class ObsArray extends ObsCollection {
|
|
553
577
|
constructor() {
|
|
554
578
|
super(...arguments);
|
|
555
|
-
this.
|
|
579
|
+
this._data = [];
|
|
556
580
|
}
|
|
557
|
-
|
|
581
|
+
_getType() {
|
|
558
582
|
return "array";
|
|
559
583
|
}
|
|
560
|
-
|
|
584
|
+
_getRecursive(depth) {
|
|
561
585
|
if (currentScope) {
|
|
562
|
-
if (this.
|
|
563
|
-
currentScope.
|
|
586
|
+
if (this._addObserver(ANY_INDEX, currentScope)) {
|
|
587
|
+
currentScope._cleaners.push(this);
|
|
564
588
|
}
|
|
565
589
|
}
|
|
566
590
|
let result = [];
|
|
567
|
-
for (let i = 0; i < this.
|
|
568
|
-
let v = this.
|
|
569
|
-
result.push(v instanceof ObsCollection ? (depth ? v.
|
|
591
|
+
for (let i = 0; i < this._data.length; i++) {
|
|
592
|
+
let v = this._data[i];
|
|
593
|
+
result.push(v instanceof ObsCollection ? (depth ? v._getRecursive(depth - 1) : new Store(this, i)) : v);
|
|
570
594
|
}
|
|
571
595
|
return result;
|
|
572
596
|
}
|
|
573
597
|
rawGet(index) {
|
|
574
|
-
return this.
|
|
598
|
+
return this._data[index];
|
|
575
599
|
}
|
|
576
600
|
rawSet(index, newData) {
|
|
577
601
|
if (index !== (0 | index) || index < 0 || index > 999999) {
|
|
578
602
|
throw new Error(`Invalid array index ${JSON.stringify(index)}`);
|
|
579
603
|
}
|
|
580
|
-
this.
|
|
604
|
+
this._data[index] = newData;
|
|
581
605
|
// Remove trailing `undefined`s
|
|
582
|
-
while (this.
|
|
583
|
-
this.
|
|
606
|
+
while (this._data.length > 0 && this._data[this._data.length - 1] === undefined) {
|
|
607
|
+
this._data.pop();
|
|
584
608
|
}
|
|
585
609
|
}
|
|
586
|
-
|
|
610
|
+
_merge(newValue, deleteMissing) {
|
|
587
611
|
if (!(newValue instanceof Array)) {
|
|
588
612
|
return false;
|
|
589
613
|
}
|
|
590
614
|
// newValue is an array
|
|
591
615
|
for (let i = 0; i < newValue.length; i++) {
|
|
592
|
-
this.
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
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];
|
|
597
624
|
if (old !== undefined) {
|
|
598
625
|
this.emitChange(i, undefined, old);
|
|
599
626
|
}
|
|
600
627
|
}
|
|
601
|
-
this.
|
|
628
|
+
this._data.length = newValue.length;
|
|
602
629
|
}
|
|
603
630
|
return true;
|
|
604
631
|
}
|
|
605
|
-
|
|
606
|
-
for (let i = 0; i < this.
|
|
607
|
-
if (this.
|
|
608
|
-
scope.
|
|
632
|
+
_iterateIndexes(scope) {
|
|
633
|
+
for (let i = 0; i < this._data.length; i++) {
|
|
634
|
+
if (this._data[i] !== undefined) {
|
|
635
|
+
scope._addChild(i);
|
|
609
636
|
}
|
|
610
637
|
}
|
|
611
638
|
}
|
|
612
|
-
|
|
639
|
+
_normalizeIndex(index) {
|
|
613
640
|
if (typeof index === 'number')
|
|
614
641
|
return index;
|
|
615
642
|
if (typeof index === 'string') {
|
|
@@ -621,27 +648,28 @@ class ObsArray extends ObsCollection {
|
|
|
621
648
|
}
|
|
622
649
|
throw new Error(`Invalid array index ${JSON.stringify(index)}`);
|
|
623
650
|
}
|
|
624
|
-
|
|
625
|
-
return this.
|
|
651
|
+
_getCount() {
|
|
652
|
+
return this._data.length;
|
|
626
653
|
}
|
|
627
654
|
}
|
|
655
|
+
/** @internal */
|
|
628
656
|
class ObsMap extends ObsCollection {
|
|
629
657
|
constructor() {
|
|
630
658
|
super(...arguments);
|
|
631
659
|
this.data = new Map();
|
|
632
660
|
}
|
|
633
|
-
|
|
661
|
+
_getType() {
|
|
634
662
|
return "map";
|
|
635
663
|
}
|
|
636
|
-
|
|
664
|
+
_getRecursive(depth) {
|
|
637
665
|
if (currentScope) {
|
|
638
|
-
if (this.
|
|
639
|
-
currentScope.
|
|
666
|
+
if (this._addObserver(ANY_INDEX, currentScope)) {
|
|
667
|
+
currentScope._cleaners.push(this);
|
|
640
668
|
}
|
|
641
669
|
}
|
|
642
670
|
let result = new Map();
|
|
643
671
|
this.data.forEach((v, k) => {
|
|
644
|
-
result.set(k, (v instanceof ObsCollection) ? (depth ? v.
|
|
672
|
+
result.set(k, (v instanceof ObsCollection) ? (depth ? v._getRecursive(depth - 1) : new Store(this, k)) : v);
|
|
645
673
|
});
|
|
646
674
|
return result;
|
|
647
675
|
}
|
|
@@ -656,67 +684,68 @@ class ObsMap extends ObsCollection {
|
|
|
656
684
|
this.data.set(index, newData);
|
|
657
685
|
}
|
|
658
686
|
}
|
|
659
|
-
|
|
687
|
+
_merge(newValue, deleteMissing) {
|
|
660
688
|
if (!(newValue instanceof Map)) {
|
|
661
689
|
return false;
|
|
662
690
|
}
|
|
663
691
|
// Walk the pairs of the new value map
|
|
664
692
|
newValue.forEach((v, k) => {
|
|
665
|
-
this.
|
|
693
|
+
this._setIndex(k, v, deleteMissing);
|
|
666
694
|
});
|
|
667
695
|
if (deleteMissing) {
|
|
668
696
|
this.data.forEach((v, k) => {
|
|
669
697
|
if (!newValue.has(k))
|
|
670
|
-
this.
|
|
698
|
+
this._setIndex(k, undefined, false);
|
|
671
699
|
});
|
|
672
700
|
}
|
|
673
701
|
return true;
|
|
674
702
|
}
|
|
675
|
-
|
|
703
|
+
_iterateIndexes(scope) {
|
|
676
704
|
this.data.forEach((_, itemIndex) => {
|
|
677
|
-
scope.
|
|
705
|
+
scope._addChild(itemIndex);
|
|
678
706
|
});
|
|
679
707
|
}
|
|
680
|
-
|
|
708
|
+
_normalizeIndex(index) {
|
|
681
709
|
return index;
|
|
682
710
|
}
|
|
683
|
-
|
|
711
|
+
_getCount() {
|
|
684
712
|
return this.data.size;
|
|
685
713
|
}
|
|
686
714
|
}
|
|
715
|
+
/** @internal */
|
|
687
716
|
class ObsObject extends ObsMap {
|
|
688
|
-
|
|
717
|
+
_getType() {
|
|
689
718
|
return "object";
|
|
690
719
|
}
|
|
691
|
-
|
|
720
|
+
_getRecursive(depth) {
|
|
692
721
|
if (currentScope) {
|
|
693
|
-
if (this.
|
|
694
|
-
currentScope.
|
|
722
|
+
if (this._addObserver(ANY_INDEX, currentScope)) {
|
|
723
|
+
currentScope._cleaners.push(this);
|
|
695
724
|
}
|
|
696
725
|
}
|
|
697
726
|
let result = {};
|
|
698
727
|
this.data.forEach((v, k) => {
|
|
699
|
-
result[k] = (v instanceof ObsCollection) ? (depth ? v.
|
|
728
|
+
result[k] = (v instanceof ObsCollection) ? (depth ? v._getRecursive(depth - 1) : new Store(this, k)) : v;
|
|
700
729
|
});
|
|
701
730
|
return result;
|
|
702
731
|
}
|
|
703
|
-
|
|
732
|
+
_merge(newValue, deleteMissing) {
|
|
704
733
|
if (!newValue || newValue.constructor !== Object) {
|
|
705
734
|
return false;
|
|
706
735
|
}
|
|
707
736
|
// Walk the pairs of the new value object
|
|
708
737
|
for (let k in newValue) {
|
|
709
|
-
this.
|
|
738
|
+
this._setIndex(k, newValue[k], deleteMissing);
|
|
710
739
|
}
|
|
711
740
|
if (deleteMissing) {
|
|
712
741
|
this.data.forEach((v, k) => {
|
|
713
742
|
if (!newValue.hasOwnProperty(k))
|
|
714
|
-
this.
|
|
743
|
+
this._setIndex(k, undefined, false);
|
|
715
744
|
});
|
|
716
745
|
}
|
|
717
746
|
return true;
|
|
718
747
|
}
|
|
719
|
-
|
|
748
|
+
_normalizeIndex(index) {
|
|
720
749
|
let type = typeof index;
|
|
721
750
|
if (type === 'string')
|
|
722
751
|
return index;
|
|
@@ -724,7 +753,7 @@ class ObsObject extends ObsMap {
|
|
|
724
753
|
return '' + index;
|
|
725
754
|
throw new Error(`Invalid object index ${JSON.stringify(index)}`);
|
|
726
755
|
}
|
|
727
|
-
|
|
756
|
+
_getCount() {
|
|
728
757
|
let cnt = 0;
|
|
729
758
|
for (let key of this.data)
|
|
730
759
|
cnt++;
|
|
@@ -742,18 +771,18 @@ class ObsObject extends ObsMap {
|
|
|
742
771
|
export class Store {
|
|
743
772
|
constructor(value = undefined, index = undefined) {
|
|
744
773
|
if (index === undefined) {
|
|
745
|
-
this.
|
|
746
|
-
this.
|
|
774
|
+
this._collection = new ObsArray();
|
|
775
|
+
this._idx = 0;
|
|
747
776
|
if (value !== undefined) {
|
|
748
|
-
this.
|
|
777
|
+
this._collection.rawSet(0, valueToData(value));
|
|
749
778
|
}
|
|
750
779
|
}
|
|
751
780
|
else {
|
|
752
781
|
if (!(value instanceof ObsCollection)) {
|
|
753
782
|
throw new Error("1st parameter should be an ObsCollection if the 2nd is also given");
|
|
754
783
|
}
|
|
755
|
-
this.
|
|
756
|
-
this.
|
|
784
|
+
this._collection = value;
|
|
785
|
+
this._idx = index;
|
|
757
786
|
}
|
|
758
787
|
}
|
|
759
788
|
/**
|
|
@@ -771,11 +800,11 @@ export class Store {
|
|
|
771
800
|
* ```
|
|
772
801
|
*/
|
|
773
802
|
index() {
|
|
774
|
-
return this.
|
|
803
|
+
return this._idx;
|
|
775
804
|
}
|
|
776
805
|
/** @internal */
|
|
777
806
|
_clean(scope) {
|
|
778
|
-
this.
|
|
807
|
+
this._collection._removeObserver(this._idx, scope);
|
|
779
808
|
}
|
|
780
809
|
/**
|
|
781
810
|
* @returns Resolves `path` and then retrieves the value that is there, subscribing
|
|
@@ -871,12 +900,12 @@ export class Store {
|
|
|
871
900
|
let store = opts.path && opts.path.length ? this.ref(...opts.path) : this;
|
|
872
901
|
let value = store._observe();
|
|
873
902
|
if (opts.type && (value !== undefined || opts.defaultValue === undefined)) {
|
|
874
|
-
let type = (value instanceof ObsCollection) ? value.
|
|
903
|
+
let type = (value instanceof ObsCollection) ? value._getType() : (value === null ? "null" : typeof value);
|
|
875
904
|
if (type !== opts.type)
|
|
876
905
|
throw new TypeError(`Expecting ${opts.type} but got ${type}`);
|
|
877
906
|
}
|
|
878
907
|
if (value instanceof ObsCollection) {
|
|
879
|
-
return value.
|
|
908
|
+
return value._getRecursive(opts.depth == null ? -1 : opts.depth - 1);
|
|
880
909
|
}
|
|
881
910
|
return value === undefined ? opts.defaultValue : value;
|
|
882
911
|
}
|
|
@@ -896,7 +925,7 @@ export class Store {
|
|
|
896
925
|
return !observer.count;
|
|
897
926
|
}
|
|
898
927
|
else {
|
|
899
|
-
return !value.
|
|
928
|
+
return !value._getCount();
|
|
900
929
|
}
|
|
901
930
|
}
|
|
902
931
|
else if (value === undefined) {
|
|
@@ -922,7 +951,7 @@ export class Store {
|
|
|
922
951
|
return observer.count;
|
|
923
952
|
}
|
|
924
953
|
else {
|
|
925
|
-
return value.
|
|
954
|
+
return value._getCount();
|
|
926
955
|
}
|
|
927
956
|
}
|
|
928
957
|
else if (value === undefined) {
|
|
@@ -944,7 +973,7 @@ export class Store {
|
|
|
944
973
|
getType(...path) {
|
|
945
974
|
let store = this.ref(...path);
|
|
946
975
|
let value = store._observe();
|
|
947
|
-
return (value instanceof ObsCollection) ? value.
|
|
976
|
+
return (value instanceof ObsCollection) ? value._getType() : (value === null ? "null" : typeof value);
|
|
948
977
|
}
|
|
949
978
|
/**
|
|
950
979
|
* Sets the value to the last given argument. Any earlier argument are a Store-path that is first
|
|
@@ -978,7 +1007,8 @@ export class Store {
|
|
|
978
1007
|
set(...pathAndValue) {
|
|
979
1008
|
let newValue = pathAndValue.pop();
|
|
980
1009
|
let store = this.makeRef(...pathAndValue);
|
|
981
|
-
store.
|
|
1010
|
+
store._collection._setIndex(store._idx, newValue, true);
|
|
1011
|
+
runImmediateQueue();
|
|
982
1012
|
}
|
|
983
1013
|
/**
|
|
984
1014
|
* Sets the `Store` to the given `mergeValue`, but without deleting any pre-existing
|
|
@@ -995,7 +1025,8 @@ export class Store {
|
|
|
995
1025
|
merge(...pathAndValue) {
|
|
996
1026
|
let mergeValue = pathAndValue.pop();
|
|
997
1027
|
let store = this.makeRef(...pathAndValue);
|
|
998
|
-
store.
|
|
1028
|
+
store._collection._setIndex(store._idx, mergeValue, false);
|
|
1029
|
+
runImmediateQueue();
|
|
999
1030
|
}
|
|
1000
1031
|
/**
|
|
1001
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)
|
|
@@ -1015,7 +1046,8 @@ export class Store {
|
|
|
1015
1046
|
*/
|
|
1016
1047
|
delete(...path) {
|
|
1017
1048
|
let store = this.makeRef(...path);
|
|
1018
|
-
store.
|
|
1049
|
+
store._collection._setIndex(store._idx, undefined, true);
|
|
1050
|
+
runImmediateQueue();
|
|
1019
1051
|
}
|
|
1020
1052
|
/**
|
|
1021
1053
|
* Pushes a value to the end of the Array that is at the specified path in the store.
|
|
@@ -1037,18 +1069,19 @@ export class Store {
|
|
|
1037
1069
|
push(...pathAndValue) {
|
|
1038
1070
|
let newValue = pathAndValue.pop();
|
|
1039
1071
|
let store = this.makeRef(...pathAndValue);
|
|
1040
|
-
let obsArray = store.
|
|
1072
|
+
let obsArray = store._collection.rawGet(store._idx);
|
|
1041
1073
|
if (obsArray === undefined) {
|
|
1042
1074
|
obsArray = new ObsArray();
|
|
1043
|
-
store.
|
|
1075
|
+
store._collection._setIndex(store._idx, obsArray, true);
|
|
1044
1076
|
}
|
|
1045
1077
|
else if (!(obsArray instanceof ObsArray)) {
|
|
1046
1078
|
throw new Error(`push() is only allowed for an array or undefined (which would become an array)`);
|
|
1047
1079
|
}
|
|
1048
1080
|
let newData = valueToData(newValue);
|
|
1049
|
-
let pos = obsArray.
|
|
1050
|
-
obsArray.
|
|
1081
|
+
let pos = obsArray._data.length;
|
|
1082
|
+
obsArray._data.push(newData);
|
|
1051
1083
|
obsArray.emitChange(pos, newData, undefined);
|
|
1084
|
+
runImmediateQueue();
|
|
1052
1085
|
return pos;
|
|
1053
1086
|
}
|
|
1054
1087
|
/**
|
|
@@ -1072,7 +1105,7 @@ export class Store {
|
|
|
1072
1105
|
for (let i = 0; i < path.length; i++) {
|
|
1073
1106
|
let value = store._observe();
|
|
1074
1107
|
if (value instanceof ObsCollection) {
|
|
1075
|
-
store = new Store(value, value.
|
|
1108
|
+
store = new Store(value, value._normalizeIndex(path[i]));
|
|
1076
1109
|
}
|
|
1077
1110
|
else {
|
|
1078
1111
|
if (value !== undefined)
|
|
@@ -1105,26 +1138,27 @@ export class Store {
|
|
|
1105
1138
|
makeRef(...path) {
|
|
1106
1139
|
let store = this;
|
|
1107
1140
|
for (let i = 0; i < path.length; i++) {
|
|
1108
|
-
let value = store.
|
|
1141
|
+
let value = store._collection.rawGet(store._idx);
|
|
1109
1142
|
if (!(value instanceof ObsCollection)) {
|
|
1110
1143
|
if (value !== undefined)
|
|
1111
1144
|
throw new Error(`Value ${JSON.stringify(value)} is not a collection (nor undefined) in step ${i} of $(${JSON.stringify(path)})`);
|
|
1112
1145
|
value = new ObsObject();
|
|
1113
|
-
store.
|
|
1114
|
-
store.
|
|
1146
|
+
store._collection.rawSet(store._idx, value);
|
|
1147
|
+
store._collection.emitChange(store._idx, value, undefined);
|
|
1115
1148
|
}
|
|
1116
|
-
store = new Store(value, value.
|
|
1149
|
+
store = new Store(value, value._normalizeIndex(path[i]));
|
|
1117
1150
|
}
|
|
1151
|
+
runImmediateQueue();
|
|
1118
1152
|
return store;
|
|
1119
1153
|
}
|
|
1120
1154
|
/** @internal */
|
|
1121
1155
|
_observe() {
|
|
1122
1156
|
if (currentScope) {
|
|
1123
|
-
if (this.
|
|
1124
|
-
currentScope.
|
|
1157
|
+
if (this._collection._addObserver(this._idx, currentScope)) {
|
|
1158
|
+
currentScope._cleaners.push(this);
|
|
1125
1159
|
}
|
|
1126
1160
|
}
|
|
1127
|
-
return this.
|
|
1161
|
+
return this._collection.rawGet(this._idx);
|
|
1128
1162
|
}
|
|
1129
1163
|
/**
|
|
1130
1164
|
* Iterate the specified collection (Array, Map or object), running the given code block for each item.
|
|
@@ -1151,11 +1185,11 @@ export class Store {
|
|
|
1151
1185
|
let val = store._observe();
|
|
1152
1186
|
if (val instanceof ObsCollection) {
|
|
1153
1187
|
// Subscribe to changes using the specialized OnEachScope
|
|
1154
|
-
let onEachScope = new OnEachScope(currentScope.
|
|
1155
|
-
val.
|
|
1156
|
-
currentScope.
|
|
1157
|
-
currentScope.
|
|
1158
|
-
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();
|
|
1159
1193
|
}
|
|
1160
1194
|
else if (val !== undefined) {
|
|
1161
1195
|
throw new Error(`onEach() attempted on a value that is neither a collection nor undefined`);
|
|
@@ -1240,7 +1274,7 @@ export class Store {
|
|
|
1240
1274
|
* does not exist.
|
|
1241
1275
|
*/
|
|
1242
1276
|
isDetached() { return false; }
|
|
1243
|
-
|
|
1277
|
+
/**
|
|
1244
1278
|
* Dump a live view of the `Store` tree as HTML text, `ul` and `li` nodes at
|
|
1245
1279
|
* the current mount position. Meant for debugging purposes.
|
|
1246
1280
|
*/
|
|
@@ -1306,22 +1340,22 @@ export function node(tag = "", ...rest) {
|
|
|
1306
1340
|
el.className = classes.replaceAll('.', ' ');
|
|
1307
1341
|
}
|
|
1308
1342
|
}
|
|
1309
|
-
currentScope.
|
|
1343
|
+
currentScope._addNode(el);
|
|
1310
1344
|
for (let item of rest) {
|
|
1311
1345
|
let type = typeof item;
|
|
1312
1346
|
if (type === 'function') {
|
|
1313
|
-
let scope = new SimpleScope(el, undefined, currentScope.
|
|
1347
|
+
let scope = new SimpleScope(el, undefined, currentScope._queueOrder + 1, item);
|
|
1314
1348
|
if (onCreateEnabled) {
|
|
1315
1349
|
onCreateEnabled = false;
|
|
1316
|
-
scope.
|
|
1350
|
+
scope._update();
|
|
1317
1351
|
onCreateEnabled = true;
|
|
1318
1352
|
}
|
|
1319
1353
|
else {
|
|
1320
|
-
scope.
|
|
1354
|
+
scope._update();
|
|
1321
1355
|
}
|
|
1322
1356
|
// Add it to our list of cleaners. Even if `scope` currently has
|
|
1323
1357
|
// no cleaners, it may get them in a future refresh.
|
|
1324
|
-
currentScope.
|
|
1358
|
+
currentScope._cleaners.push(scope);
|
|
1325
1359
|
}
|
|
1326
1360
|
else if (type === 'string' || type === 'number') {
|
|
1327
1361
|
el.textContent = item;
|
|
@@ -1344,12 +1378,12 @@ export function node(tag = "", ...rest) {
|
|
|
1344
1378
|
* @param html - The HTML string. For example `"<section><h2>Test</h2><p>Info..</p></section>"`.
|
|
1345
1379
|
*/
|
|
1346
1380
|
export function html(html) {
|
|
1347
|
-
if (!currentScope || !currentScope.
|
|
1381
|
+
if (!currentScope || !currentScope._parentElement)
|
|
1348
1382
|
throw new ScopeError(true);
|
|
1349
|
-
let tmpParent = document.createElement(currentScope.
|
|
1383
|
+
let tmpParent = document.createElement(currentScope._parentElement.tagName);
|
|
1350
1384
|
tmpParent.innerHTML = '' + html;
|
|
1351
1385
|
while (tmpParent.firstChild) {
|
|
1352
|
-
currentScope.
|
|
1386
|
+
currentScope._addNode(tmpParent.firstChild);
|
|
1353
1387
|
}
|
|
1354
1388
|
}
|
|
1355
1389
|
function bindInput(el, store) {
|
|
@@ -1397,18 +1431,18 @@ export function text(text) {
|
|
|
1397
1431
|
throw new ScopeError(true);
|
|
1398
1432
|
if (text == null)
|
|
1399
1433
|
return;
|
|
1400
|
-
currentScope.
|
|
1434
|
+
currentScope._addNode(document.createTextNode(text));
|
|
1401
1435
|
}
|
|
1402
1436
|
export function prop(name, value = undefined) {
|
|
1403
|
-
if (!currentScope || !currentScope.
|
|
1437
|
+
if (!currentScope || !currentScope._parentElement)
|
|
1404
1438
|
throw new ScopeError(true);
|
|
1405
1439
|
if (typeof name === 'object') {
|
|
1406
1440
|
for (let k in name) {
|
|
1407
|
-
applyProp(currentScope.
|
|
1441
|
+
applyProp(currentScope._parentElement, k, name[k]);
|
|
1408
1442
|
}
|
|
1409
1443
|
}
|
|
1410
1444
|
else {
|
|
1411
|
-
applyProp(currentScope.
|
|
1445
|
+
applyProp(currentScope._parentElement, name, value);
|
|
1412
1446
|
}
|
|
1413
1447
|
}
|
|
1414
1448
|
/**
|
|
@@ -1419,9 +1453,9 @@ export function prop(name, value = undefined) {
|
|
|
1419
1453
|
* terribly surprising. Be careful within the parent element of onEach() though.
|
|
1420
1454
|
*/
|
|
1421
1455
|
export function getParentElement() {
|
|
1422
|
-
if (!currentScope || !currentScope.
|
|
1456
|
+
if (!currentScope || !currentScope._parentElement)
|
|
1423
1457
|
throw new ScopeError(true);
|
|
1424
|
-
return currentScope.
|
|
1458
|
+
return currentScope._parentElement;
|
|
1425
1459
|
}
|
|
1426
1460
|
/**
|
|
1427
1461
|
* Register a function that is to be executed right before the current reactive scope
|
|
@@ -1431,7 +1465,7 @@ export function getParentElement() {
|
|
|
1431
1465
|
export function clean(clean) {
|
|
1432
1466
|
if (!currentScope)
|
|
1433
1467
|
throw new ScopeError(false);
|
|
1434
|
-
currentScope.
|
|
1468
|
+
currentScope._cleaners.push({ _clean: clean });
|
|
1435
1469
|
}
|
|
1436
1470
|
/**
|
|
1437
1471
|
* Reactively run a function, meaning the function will rerun when any `Store` that was read
|
|
@@ -1440,6 +1474,7 @@ export function clean(clean) {
|
|
|
1440
1474
|
* no cause the outer function to rerun.
|
|
1441
1475
|
*
|
|
1442
1476
|
* @param func - The function to be (repeatedly) executed.
|
|
1477
|
+
* @returns The mount id (usable for `unmount`) if this is a top-level observe.
|
|
1443
1478
|
* @example
|
|
1444
1479
|
* ```
|
|
1445
1480
|
* let number = new Store(0)
|
|
@@ -1455,13 +1490,26 @@ export function clean(clean) {
|
|
|
1455
1490
|
* })
|
|
1456
1491
|
*/
|
|
1457
1492
|
export function observe(func) {
|
|
1458
|
-
|
|
1493
|
+
return _mount(undefined, func, SimpleScope);
|
|
1494
|
+
}
|
|
1495
|
+
/**
|
|
1496
|
+
* Like `observe`, but instead of deferring running the observer function until
|
|
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);
|
|
1459
1506
|
}
|
|
1460
1507
|
/**
|
|
1461
1508
|
* Like {@link Store.observe}, but allow the function to create DOM elements using {@link Store.node}.
|
|
1462
1509
|
|
|
1463
1510
|
* @param func - The function to be (repeatedly) executed, possibly adding DOM elements to `parentElement`.
|
|
1464
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.
|
|
1465
1513
|
*
|
|
1466
1514
|
* @example
|
|
1467
1515
|
* ```
|
|
@@ -1498,20 +1546,47 @@ export function observe(func) {
|
|
|
1498
1546
|
* ```
|
|
1499
1547
|
*/
|
|
1500
1548
|
export function mount(parentElement, func) {
|
|
1549
|
+
return _mount(parentElement, func, SimpleScope);
|
|
1550
|
+
}
|
|
1551
|
+
let maxTopScopeId = 0;
|
|
1552
|
+
const topScopes = new Map();
|
|
1553
|
+
function _mount(parentElement, func, MountScope) {
|
|
1501
1554
|
let scope;
|
|
1502
1555
|
if (parentElement || !currentScope) {
|
|
1503
|
-
scope = new
|
|
1556
|
+
scope = new MountScope(parentElement, undefined, 0, func);
|
|
1504
1557
|
}
|
|
1505
1558
|
else {
|
|
1506
|
-
scope = new
|
|
1507
|
-
currentScope.
|
|
1559
|
+
scope = new MountScope(currentScope._parentElement, currentScope._lastChild || currentScope._precedingSibling, currentScope._queueOrder + 1, func);
|
|
1560
|
+
currentScope._lastChild = scope;
|
|
1508
1561
|
}
|
|
1509
1562
|
// Do the initial run
|
|
1510
|
-
scope.
|
|
1563
|
+
scope._update();
|
|
1511
1564
|
// Add it to our list of cleaners. Even if `scope` currently has
|
|
1512
1565
|
// no cleaners, it may get them in a future refresh.
|
|
1513
1566
|
if (currentScope) {
|
|
1514
|
-
currentScope.
|
|
1567
|
+
currentScope._cleaners.push(scope);
|
|
1568
|
+
}
|
|
1569
|
+
else {
|
|
1570
|
+
topScopes.set(++maxTopScopeId, scope);
|
|
1571
|
+
return maxTopScopeId;
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
/**
|
|
1575
|
+
* Unmount one specific or all top-level mounts or observes, meaning those that were created outside of the scope
|
|
1576
|
+
* of any other mount or observe.
|
|
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();
|
|
1515
1590
|
}
|
|
1516
1591
|
}
|
|
1517
1592
|
/** Runs the given function, while not subscribing the current scope when reading {@link Store.Store} values.
|
|
@@ -1641,11 +1716,12 @@ function valueToData(value) {
|
|
|
1641
1716
|
function defaultMakeSortKey(store) {
|
|
1642
1717
|
return store.index();
|
|
1643
1718
|
}
|
|
1644
|
-
/*
|
|
1719
|
+
/* c8 ignore start */
|
|
1645
1720
|
function internalError(code) {
|
|
1646
1721
|
let error = new Error("Aberdeen internal error " + code);
|
|
1647
1722
|
setTimeout(() => { throw error; }, 0);
|
|
1648
1723
|
}
|
|
1724
|
+
/* c8 ignore end */
|
|
1649
1725
|
function handleError(e) {
|
|
1650
1726
|
// Throw the error async, so the rest of the rendering can continue
|
|
1651
1727
|
setTimeout(() => { throw e; }, 0);
|
|
@@ -1655,188 +1731,26 @@ class ScopeError extends Error {
|
|
|
1655
1731
|
super(`Operation not permitted outside of ${mount ? "a mount" : "an observe"}() scope`);
|
|
1656
1732
|
}
|
|
1657
1733
|
}
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
const isHorizontal = parentStyle.display === 'flex' && (parentStyle.flexDirection || '').startsWith('row');
|
|
1663
|
-
return isHorizontal ?
|
|
1664
|
-
{ marginLeft: `-${el.offsetWidth / 2}px`, marginRight: `-${el.offsetWidth / 2}px`, transform: "scaleX(0)" } :
|
|
1665
|
-
{ marginBottom: `-${el.offsetHeight / 2}px`, marginTop: `-${el.offsetHeight / 2}px`, transform: "scaleY(0)" };
|
|
1666
|
-
}
|
|
1667
|
-
/** Do a grow transition for the given element. This is meant to be used as a
|
|
1668
|
-
* handler for the `create` property.
|
|
1669
|
-
*
|
|
1670
|
-
* @param el The element to transition.
|
|
1671
|
-
*
|
|
1672
|
-
* The transition doesn't look great for table elements, and may have problems
|
|
1673
|
-
* for other specific cases as well.
|
|
1674
|
-
*/
|
|
1675
|
-
export function grow(el) {
|
|
1676
|
-
// This timeout is to await all other elements having been added to the Dom
|
|
1677
|
-
scheduleDomReader(() => {
|
|
1678
|
-
// Make the element size 0 using transforms and negative margins.
|
|
1679
|
-
// This causes a browser layout, as we're querying el.offset<>.
|
|
1680
|
-
let props = getGrowShrinkProps(el);
|
|
1681
|
-
// The timeout is in order to batch all reads and then all writes when there
|
|
1682
|
-
// are multiple simultaneous grow transitions.
|
|
1683
|
-
scheduleDomWriter(() => {
|
|
1684
|
-
Object.assign(el.style, props);
|
|
1685
|
-
// This timeout is to combine multiple transitions into a single browser layout
|
|
1686
|
-
scheduleDomReader(() => {
|
|
1687
|
-
// Make sure the layouting has been performed, to cause transitions to trigger
|
|
1688
|
-
el.offsetHeight;
|
|
1689
|
-
scheduleDomWriter(() => {
|
|
1690
|
-
// Do the transitions
|
|
1691
|
-
el.style.transition = GROW_SHRINK_TRANSITION;
|
|
1692
|
-
for (let prop in props)
|
|
1693
|
-
el.style[prop] = "";
|
|
1694
|
-
setTimeout(() => {
|
|
1695
|
-
// Reset the element to a clean state
|
|
1696
|
-
el.style.transition = "";
|
|
1697
|
-
}, FADE_TIME);
|
|
1698
|
-
});
|
|
1699
|
-
});
|
|
1700
|
-
});
|
|
1701
|
-
});
|
|
1702
|
-
}
|
|
1703
|
-
/** Do a shrink transition for the given element, and remove it from the DOM
|
|
1704
|
-
* afterwards. This is meant to be used as a handler for the `destroy` property.
|
|
1705
|
-
*
|
|
1706
|
-
* @param el The element to transition and remove.
|
|
1707
|
-
*
|
|
1708
|
-
* The transition doesn't look great for table elements, and may have problems
|
|
1709
|
-
* for other specific cases as well.
|
|
1710
|
-
*/
|
|
1711
|
-
export function shrink(el) {
|
|
1712
|
-
scheduleDomReader(() => {
|
|
1713
|
-
const props = getGrowShrinkProps(el);
|
|
1714
|
-
// The timeout is in order to batch all reads and then all writes when there
|
|
1715
|
-
// are multiple simultaneous shrink transitions.
|
|
1716
|
-
scheduleDomWriter(() => {
|
|
1717
|
-
el.style.transition = GROW_SHRINK_TRANSITION;
|
|
1718
|
-
Object.assign(el.style, props);
|
|
1719
|
-
setTimeout(() => el.remove(), FADE_TIME);
|
|
1720
|
-
});
|
|
1721
|
-
});
|
|
1722
|
-
}
|
|
1723
|
-
function recordPatch(func) {
|
|
1724
|
-
if (recordingPatch)
|
|
1725
|
-
throw new Error(`already recording a patch`);
|
|
1726
|
-
recordingPatch = new Map();
|
|
1734
|
+
/** @internal */
|
|
1735
|
+
export function withEmitHandler(handler, func) {
|
|
1736
|
+
const oldEmitHandler = ObsCollection.prototype.emitChange;
|
|
1737
|
+
ObsCollection.prototype.emitChange = handler;
|
|
1727
1738
|
try {
|
|
1728
1739
|
func();
|
|
1729
1740
|
}
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
throw e;
|
|
1733
|
-
}
|
|
1734
|
-
const result = recordingPatch;
|
|
1735
|
-
recordingPatch = undefined;
|
|
1736
|
-
return result;
|
|
1737
|
-
}
|
|
1738
|
-
function addToPatch(patch, collection, index, newData, oldData) {
|
|
1739
|
-
let collectionMap = patch.get(collection);
|
|
1740
|
-
if (!collectionMap) {
|
|
1741
|
-
collectionMap = new Map();
|
|
1742
|
-
patch.set(collection, collectionMap);
|
|
1743
|
-
}
|
|
1744
|
-
let prev = collectionMap.get(index);
|
|
1745
|
-
if (prev)
|
|
1746
|
-
oldData = prev[1];
|
|
1747
|
-
if (newData === oldData)
|
|
1748
|
-
collectionMap.delete(index);
|
|
1749
|
-
else
|
|
1750
|
-
collectionMap.set(index, [newData, oldData]);
|
|
1751
|
-
}
|
|
1752
|
-
function emitPatch(patch) {
|
|
1753
|
-
for (let [collection, collectionMap] of patch) {
|
|
1754
|
-
for (let [index, [newData, oldData]] of collectionMap) {
|
|
1755
|
-
collection.emitChange(index, newData, oldData);
|
|
1756
|
-
}
|
|
1757
|
-
}
|
|
1758
|
-
}
|
|
1759
|
-
function mergePatch(target, source, reverse = false) {
|
|
1760
|
-
for (let [collection, collectionMap] of source) {
|
|
1761
|
-
for (let [index, [newData, oldData]] of collectionMap) {
|
|
1762
|
-
addToPatch(target, collection, index, reverse ? oldData : newData, reverse ? newData : oldData);
|
|
1763
|
-
}
|
|
1764
|
-
}
|
|
1765
|
-
}
|
|
1766
|
-
function silentlyApplyPatch(patch, force = false) {
|
|
1767
|
-
for (let [collection, collectionMap] of patch) {
|
|
1768
|
-
for (let [index, [newData, oldData]] of collectionMap) {
|
|
1769
|
-
let actualData = collection.rawGet(index);
|
|
1770
|
-
if (actualData !== oldData) {
|
|
1771
|
-
if (force)
|
|
1772
|
-
handleError(new Error(`Applying invalid patch: data ${actualData} is unequal to expected old data ${oldData} for index ${index}`));
|
|
1773
|
-
else
|
|
1774
|
-
return false;
|
|
1775
|
-
}
|
|
1776
|
-
}
|
|
1777
|
-
}
|
|
1778
|
-
for (let [collection, collectionMap] of patch) {
|
|
1779
|
-
for (let [index, [newData, oldData]] of collectionMap) {
|
|
1780
|
-
collection.rawSet(index, newData);
|
|
1781
|
-
}
|
|
1741
|
+
finally {
|
|
1742
|
+
ObsCollection.prototype.emitChange = oldEmitHandler;
|
|
1782
1743
|
}
|
|
1783
|
-
return true;
|
|
1784
1744
|
}
|
|
1785
|
-
const appliedPredictions = [];
|
|
1786
1745
|
/**
|
|
1787
|
-
* Run
|
|
1788
|
-
*
|
|
1789
|
-
* async source).
|
|
1790
|
-
* @param predictFunc The function to run. It will generally modify some Observables
|
|
1791
|
-
* to immediately reflect state (as closely as possible) that we expect the server
|
|
1792
|
-
* to communicate back to us later on.
|
|
1793
|
-
* @returns A `Patch` object. Don't modify it. This is only meant to be passed to `applyCanon`.
|
|
1746
|
+
* Run a function, while *not* causing reactive effects for any changes it makes to `Store`s.
|
|
1747
|
+
* @param func The function to be executed once immediately.
|
|
1794
1748
|
*/
|
|
1795
|
-
export function
|
|
1796
|
-
|
|
1797
|
-
appliedPredictions.push(patch);
|
|
1798
|
-
emitPatch(patch);
|
|
1799
|
-
return patch;
|
|
1800
|
-
}
|
|
1801
|
-
/**
|
|
1802
|
-
* Temporarily revert all outstanding predictions, optionally run the provided function
|
|
1803
|
-
* (which will generally make authoritative changes to the data based on a server response),
|
|
1804
|
-
* and then attempt to reapply the predictions on top of the new canonical state, dropping
|
|
1805
|
-
* any predictions that can no longer be applied cleanly (the data has been modified) or
|
|
1806
|
-
* that were specified in `dropPredictions`.
|
|
1807
|
-
*
|
|
1808
|
-
* All of this is done such that redraws are only triggered if the overall effect is an
|
|
1809
|
-
* actual change to an `Observable`.
|
|
1810
|
-
* @param canonFunc The function to run without any predictions applied. This will typically
|
|
1811
|
-
* make authoritative changes to the data, based on a server response.
|
|
1812
|
-
* @param dropPredictions An optional list of predictions (as returned by `applyPrediction`)
|
|
1813
|
-
* to undo. Typically, when a server response for a certain request is being handled,
|
|
1814
|
-
* you'd want to drop the prediction that was done for that request.
|
|
1815
|
-
*/
|
|
1816
|
-
export function applyCanon(canonFunc, dropPredictions = []) {
|
|
1817
|
-
let resultPatch = new Map();
|
|
1818
|
-
for (let prediction of appliedPredictions)
|
|
1819
|
-
mergePatch(resultPatch, prediction, true);
|
|
1820
|
-
silentlyApplyPatch(resultPatch, true);
|
|
1821
|
-
for (let prediction of dropPredictions) {
|
|
1822
|
-
let pos = appliedPredictions.indexOf(prediction);
|
|
1823
|
-
if (pos >= 0)
|
|
1824
|
-
appliedPredictions.splice(pos, 1);
|
|
1825
|
-
}
|
|
1826
|
-
if (canonFunc)
|
|
1827
|
-
mergePatch(resultPatch, recordPatch(canonFunc));
|
|
1828
|
-
for (let idx = 0; idx < appliedPredictions.length; idx++) {
|
|
1829
|
-
if (silentlyApplyPatch(appliedPredictions[idx])) {
|
|
1830
|
-
mergePatch(resultPatch, appliedPredictions[idx]);
|
|
1831
|
-
}
|
|
1832
|
-
else {
|
|
1833
|
-
appliedPredictions.splice(idx, 1);
|
|
1834
|
-
idx--;
|
|
1835
|
-
}
|
|
1836
|
-
}
|
|
1837
|
-
emitPatch(resultPatch);
|
|
1749
|
+
export function inhibitEffects(func) {
|
|
1750
|
+
withEmitHandler(() => { }, func);
|
|
1838
1751
|
}
|
|
1839
1752
|
// @ts-ignore
|
|
1840
|
-
//
|
|
1753
|
+
// c8 ignore next
|
|
1841
1754
|
if (!String.prototype.replaceAll)
|
|
1842
1755
|
String.prototype.replaceAll = function (from, to) { return this.split(from).join(to); };
|
|
1756
|
+
//# sourceMappingURL=aberdeen.js.map
|