aberdeen 1.0.7 → 1.0.8
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 +1 -1
- package/dist/aberdeen.js +15 -3
- package/dist/aberdeen.js.map +4 -4
- package/dist/helpers/reverseSortedSet.d.ts +8 -3
- package/dist-min/aberdeen.js +5 -5
- package/dist-min/aberdeen.js.map +4 -4
- package/package.json +1 -1
- package/src/aberdeen.ts +40 -7
- package/src/helpers/reverseSortedSet.ts +34 -31
package/src/aberdeen.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ReverseSortedSet } from "./helpers/reverseSortedSet.js";
|
|
2
|
+
import type { ReverseSortedSetPointer } from "./helpers/reverseSortedSet.js";
|
|
2
3
|
|
|
3
4
|
/*
|
|
4
5
|
* QueueRunner
|
|
@@ -9,9 +10,11 @@ import { ReverseSortedSet } from "./helpers/reverseSortedSet.js";
|
|
|
9
10
|
interface QueueRunner {
|
|
10
11
|
prio: number; // Higher values have higher priority
|
|
11
12
|
queueRun(): void;
|
|
13
|
+
|
|
14
|
+
[ptr: ReverseSortedSetPointer]: QueueRunner;
|
|
12
15
|
}
|
|
13
16
|
|
|
14
|
-
let sortedQueue: ReverseSortedSet<QueueRunner> | undefined; // When set, a runQueue is scheduled or currently running.
|
|
17
|
+
let sortedQueue: ReverseSortedSet<QueueRunner, "prio"> | undefined; // When set, a runQueue is scheduled or currently running.
|
|
15
18
|
let runQueueDepth = 0; // Incremented when a queue event causes another queue event to be added. Reset when queue is empty. Throw when >= 42 to break (infinite) recursion.
|
|
16
19
|
let topRedrawScope: Scope | undefined; // The scope that triggered the current redraw. Elements drawn at this scope level may trigger 'create' animations.
|
|
17
20
|
|
|
@@ -28,7 +31,7 @@ export type DatumType =
|
|
|
28
31
|
|
|
29
32
|
function queue(runner: QueueRunner) {
|
|
30
33
|
if (!sortedQueue) {
|
|
31
|
-
sortedQueue = new ReverseSortedSet<QueueRunner>("prio");
|
|
34
|
+
sortedQueue = new ReverseSortedSet<QueueRunner, "prio">("prio");
|
|
32
35
|
setTimeout(runQueue, 0);
|
|
33
36
|
} else if (!(runQueueDepth & 1)) {
|
|
34
37
|
runQueueDepth++; // Make it uneven
|
|
@@ -168,6 +171,8 @@ abstract class Scope implements QueueRunner {
|
|
|
168
171
|
// order of the source code.
|
|
169
172
|
prio: number = --lastPrio;
|
|
170
173
|
|
|
174
|
+
[ptr: ReverseSortedSetPointer]: this;
|
|
175
|
+
|
|
171
176
|
abstract onChange(index: any, newData: DatumType, oldData: DatumType): void;
|
|
172
177
|
abstract queueRun(): void;
|
|
173
178
|
|
|
@@ -198,6 +203,9 @@ abstract class ContentScope extends Scope {
|
|
|
198
203
|
// be for child scopes, subscriptions as well as `clean(..)` hooks.
|
|
199
204
|
cleaners: Array<{ delete: (scope: Scope) => void } | (() => void)>;
|
|
200
205
|
|
|
206
|
+
// Whether this scope is within an SVG namespace context
|
|
207
|
+
inSvgNamespace: boolean = false;
|
|
208
|
+
|
|
201
209
|
constructor(
|
|
202
210
|
cleaners: Array<{ delete: (scope: Scope) => void } | (() => void)> = [],
|
|
203
211
|
) {
|
|
@@ -264,6 +272,10 @@ class ChainedScope extends ContentScope {
|
|
|
264
272
|
useParentCleaners = false,
|
|
265
273
|
) {
|
|
266
274
|
super(useParentCleaners ? currentScope.cleaners : []);
|
|
275
|
+
|
|
276
|
+
// Inherit SVG namespace state from current scope
|
|
277
|
+
this.inSvgNamespace = currentScope.inSvgNamespace;
|
|
278
|
+
|
|
267
279
|
if (parentElement === currentScope.parentElement) {
|
|
268
280
|
// If `currentScope` is not actually a ChainedScope, prevSibling will be undefined, as intended
|
|
269
281
|
this.prevSibling = currentScope.getChildPrevSibling();
|
|
@@ -334,6 +346,10 @@ class MountScope extends ContentScope {
|
|
|
334
346
|
public renderer: () => any,
|
|
335
347
|
) {
|
|
336
348
|
super();
|
|
349
|
+
|
|
350
|
+
// Inherit SVG namespace state from current scope
|
|
351
|
+
this.inSvgNamespace = currentScope.inSvgNamespace;
|
|
352
|
+
|
|
337
353
|
this.redraw();
|
|
338
354
|
currentScope.cleaners.push(this);
|
|
339
355
|
}
|
|
@@ -442,7 +458,9 @@ class SetArgScope extends ChainedScope {
|
|
|
442
458
|
}
|
|
443
459
|
}
|
|
444
460
|
|
|
445
|
-
let immediateQueue: ReverseSortedSet<Scope> = new ReverseSortedSet(
|
|
461
|
+
let immediateQueue: ReverseSortedSet<Scope, "prio"> = new ReverseSortedSet(
|
|
462
|
+
"prio",
|
|
463
|
+
);
|
|
446
464
|
|
|
447
465
|
class ImmediateScope extends RegularScope {
|
|
448
466
|
onChange(index: any, newData: DatumType, oldData: DatumType) {
|
|
@@ -491,9 +509,8 @@ class OnEachScope extends Scope {
|
|
|
491
509
|
byIndex: Map<any, OnEachItemScope> = new Map();
|
|
492
510
|
|
|
493
511
|
/** The reverse-ordered list of item scopes, not including those for which makeSortKey returned undefined. */
|
|
494
|
-
sortedSet: ReverseSortedSet<OnEachItemScope> =
|
|
495
|
-
"sortKey"
|
|
496
|
-
);
|
|
512
|
+
sortedSet: ReverseSortedSet<OnEachItemScope, "sortKey"> =
|
|
513
|
+
new ReverseSortedSet("sortKey");
|
|
497
514
|
|
|
498
515
|
/** Indexes that have been created/removed and need to be handled in the next `queueRun`. */
|
|
499
516
|
changedIndexes: Set<any> = new Set();
|
|
@@ -594,6 +611,9 @@ class OnEachItemScope extends ContentScope {
|
|
|
594
611
|
) {
|
|
595
612
|
super();
|
|
596
613
|
this.parentElement = parent.parentElement;
|
|
614
|
+
|
|
615
|
+
// Inherit SVG namespace state from current scope
|
|
616
|
+
this.inSvgNamespace = currentScope.inSvgNamespace;
|
|
597
617
|
|
|
598
618
|
this.parent.byIndex.set(this.itemIndex, this);
|
|
599
619
|
|
|
@@ -1699,7 +1719,14 @@ export function $(
|
|
|
1699
1719
|
err = `Tag '${arg}' cannot contain space`;
|
|
1700
1720
|
break;
|
|
1701
1721
|
} else {
|
|
1702
|
-
|
|
1722
|
+
// Determine which namespace to use for element creation
|
|
1723
|
+
const useNamespace = currentScope.inSvgNamespace || arg === 'svg';
|
|
1724
|
+
if (useNamespace) {
|
|
1725
|
+
result = document.createElementNS('http://www.w3.org/2000/svg', arg);
|
|
1726
|
+
} else {
|
|
1727
|
+
result = document.createElement(arg);
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1703
1730
|
if (classes) result.className = classes.replaceAll(".", " ");
|
|
1704
1731
|
if (text) result.textContent = text;
|
|
1705
1732
|
addNode(result);
|
|
@@ -1707,6 +1734,12 @@ export function $(
|
|
|
1707
1734
|
savedCurrentScope = currentScope;
|
|
1708
1735
|
}
|
|
1709
1736
|
const newScope = new ChainedScope(result, true);
|
|
1737
|
+
|
|
1738
|
+
// If we're creating an SVG element, set the SVG namespace flag for child scopes
|
|
1739
|
+
if (arg === 'svg') {
|
|
1740
|
+
newScope.inSvgNamespace = true;
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1710
1743
|
newScope.lastChild = result.lastChild || undefined;
|
|
1711
1744
|
if (topRedrawScope === currentScope) topRedrawScope = newScope;
|
|
1712
1745
|
currentScope = newScope;
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
// Meta-data is saved in-place on ReverseSorted items.
|
|
2
|
+
// - Items in the set should indicate they support {[ptr: ReverseSortedSetPointer]: SELF}
|
|
3
|
+
export type ReverseSortedSetPointer = symbol;
|
|
4
|
+
|
|
5
|
+
// ReverseSortedSet saves the skip links for all required levels on the object itself
|
|
6
|
+
type SkipItem<T> = { [idx: ReverseSortedSetPointer]: T };
|
|
2
7
|
|
|
3
8
|
/**
|
|
4
9
|
* A set-like collection of objects that can do iteration sorted by a specified index property.
|
|
@@ -8,11 +13,11 @@ type Item<T> = T & { [idx: symbol]: Item<T> };
|
|
|
8
13
|
* It's implemented as a skiplist, maintaining all meta-data as part of the objects that it
|
|
9
14
|
* is tracking, for performance.
|
|
10
15
|
*/
|
|
11
|
-
export class ReverseSortedSet<T extends
|
|
16
|
+
export class ReverseSortedSet<T extends SkipItem<T>, KeyPropT extends keyof T> {
|
|
12
17
|
// A fake item, that is not actually T, but *does* contain symbols pointing at the first item for each level.
|
|
13
|
-
private tail:
|
|
18
|
+
private tail: SkipItem<T>;
|
|
14
19
|
// As every SkipList instance has its own symbols, an object can be included in more than one SkipList.
|
|
15
|
-
private symbols:
|
|
20
|
+
private symbols: ReverseSortedSetPointer[];
|
|
16
21
|
|
|
17
22
|
/**
|
|
18
23
|
* Create an empty SortedSet.
|
|
@@ -21,8 +26,8 @@ export class ReverseSortedSet<T extends object> {
|
|
|
21
26
|
* using `<` will be done on this property, so it should probably be a number or a string (or something that
|
|
22
27
|
* has a useful toString-conversion).
|
|
23
28
|
*/
|
|
24
|
-
constructor(private keyProp:
|
|
25
|
-
this.tail = {} as
|
|
29
|
+
constructor(private keyProp: KeyPropT) {
|
|
30
|
+
this.tail = {} as SkipItem<T>;
|
|
26
31
|
this.symbols = [Symbol(0)];
|
|
27
32
|
}
|
|
28
33
|
|
|
@@ -54,15 +59,15 @@ export class ReverseSortedSet<T extends object> {
|
|
|
54
59
|
const keyProp = this.keyProp;
|
|
55
60
|
const key = item[keyProp];
|
|
56
61
|
|
|
57
|
-
|
|
58
|
-
let
|
|
62
|
+
// prev is always a complete T, current might be tail only contain pointers
|
|
63
|
+
let prev: T | undefined;
|
|
64
|
+
let current: SkipItem<T> = this.tail;
|
|
59
65
|
for (let l = this.symbols.length - 1; l >= 0; l--) {
|
|
60
66
|
const symbol = this.symbols[l];
|
|
61
|
-
while ((prev = current[symbol]
|
|
62
|
-
current = prev;
|
|
67
|
+
while ((prev = current[symbol]) && prev[keyProp] > key) current = prev;
|
|
63
68
|
if (l < level) {
|
|
64
|
-
(item as
|
|
65
|
-
|
|
69
|
+
(item as SkipItem<T>)[symbol] = current[symbol];
|
|
70
|
+
current[symbol] = item;
|
|
66
71
|
}
|
|
67
72
|
}
|
|
68
73
|
|
|
@@ -106,13 +111,15 @@ export class ReverseSortedSet<T extends object> {
|
|
|
106
111
|
*
|
|
107
112
|
* Time complexity: O(log n)
|
|
108
113
|
*/
|
|
109
|
-
get(indexValue:
|
|
114
|
+
get(indexValue: T[KeyPropT]): T | undefined {
|
|
110
115
|
const keyProp = this.keyProp;
|
|
111
|
-
|
|
112
|
-
|
|
116
|
+
|
|
117
|
+
// prev is always a complete T, current might be tail only contain pointers
|
|
118
|
+
let prev: T | undefined;
|
|
119
|
+
let current: SkipItem<T> = this.tail;
|
|
113
120
|
for (let l = this.symbols.length - 1; l >= 0; l--) {
|
|
114
121
|
const symbol = this.symbols[l];
|
|
115
|
-
while ((prev = current[symbol]
|
|
122
|
+
while ((prev = current[symbol]) && prev[keyProp] > indexValue)
|
|
116
123
|
current = prev;
|
|
117
124
|
}
|
|
118
125
|
return current[this.symbols[0]]?.[keyProp] === indexValue
|
|
@@ -125,10 +132,10 @@ export class ReverseSortedSet<T extends object> {
|
|
|
125
132
|
*/
|
|
126
133
|
*[Symbol.iterator](): IterableIterator<T> {
|
|
127
134
|
const symbol = this.symbols[0];
|
|
128
|
-
let node
|
|
135
|
+
let node = this.tail[symbol];
|
|
129
136
|
while (node) {
|
|
130
137
|
yield node;
|
|
131
|
-
node = node[symbol]
|
|
138
|
+
node = node[symbol];
|
|
132
139
|
}
|
|
133
140
|
}
|
|
134
141
|
|
|
@@ -140,7 +147,7 @@ export class ReverseSortedSet<T extends object> {
|
|
|
140
147
|
* Time complexity: O(1)
|
|
141
148
|
*/
|
|
142
149
|
prev(item: T): T | undefined {
|
|
143
|
-
return
|
|
150
|
+
return item[this.symbols[0]];
|
|
144
151
|
}
|
|
145
152
|
|
|
146
153
|
/**
|
|
@@ -156,19 +163,15 @@ export class ReverseSortedSet<T extends object> {
|
|
|
156
163
|
const keyProp = this.keyProp;
|
|
157
164
|
const prop = item[keyProp];
|
|
158
165
|
|
|
159
|
-
|
|
160
|
-
let
|
|
161
|
-
|
|
166
|
+
// prev is always a complete T, current might be tail only contain pointers
|
|
167
|
+
let prev: T | undefined;
|
|
168
|
+
let current: SkipItem<T> = this.tail;
|
|
162
169
|
for (let l = this.symbols.length - 1; l >= 0; l--) {
|
|
163
170
|
const symbol = this.symbols[l];
|
|
164
|
-
while (
|
|
165
|
-
(prev = current[symbol] as Item<T>) &&
|
|
166
|
-
prev[keyProp] >= prop &&
|
|
167
|
-
prev !== item
|
|
168
|
-
)
|
|
171
|
+
while ((prev = current[symbol]) && prev[keyProp] >= prop && prev !== item)
|
|
169
172
|
current = prev;
|
|
170
173
|
if (prev === item) {
|
|
171
|
-
|
|
174
|
+
current[symbol] = prev[symbol];
|
|
172
175
|
delete prev[symbol];
|
|
173
176
|
}
|
|
174
177
|
}
|
|
@@ -183,15 +186,15 @@ export class ReverseSortedSet<T extends object> {
|
|
|
183
186
|
*/
|
|
184
187
|
clear(): void {
|
|
185
188
|
const symbol = this.symbols[0];
|
|
186
|
-
let current
|
|
189
|
+
let current = this.tail;
|
|
187
190
|
while (current) {
|
|
188
|
-
const prev = current[symbol]
|
|
191
|
+
const prev = current[symbol];
|
|
189
192
|
for (const symbol of this.symbols) {
|
|
190
193
|
if (!(symbol in current)) break;
|
|
191
194
|
delete current[symbol];
|
|
192
195
|
}
|
|
193
196
|
current = prev;
|
|
194
197
|
}
|
|
195
|
-
this.tail = {}
|
|
198
|
+
this.tail = {};
|
|
196
199
|
}
|
|
197
200
|
}
|