solarite 0.1.1 → 0.2.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/benchmarks/naive/Solarite.min.js +4 -0
- package/benchmarks/naive/index.html +14 -0
- package/benchmarks/naive/main.js +339 -0
- package/benchmarks/naive/package-lock.json +13 -0
- package/benchmarks/naive/package.json +23 -0
- package/benchmarks/readme.md +48 -0
- package/build/build.bat +2 -2
- package/build/build.js +1 -1
- package/dist/Solarite-debug.js +1344 -1532
- package/dist/Solarite.js +1298 -1334
- package/dist/Solarite.min.js +3 -3
- package/dist/udomdiff-license.txt +18 -0
- package/docs/index.md +440 -130
- package/docs/js/Playground.js +1 -1
- package/docs/js/codemirror/codemirror6.js +3683 -3206
- package/docs/js/codemirror/themeSolarIce.js +1 -1
- package/docs/js/documentation.js +1 -1
- package/docs/js/ui/CodeEditor.js +183 -45
- package/docs/js/ui/FlexResizer.js +14 -3
- package/docs/js/util/Errors.js +11 -0
- package/docs/media/documentation.css +6 -3
- package/index.html +449 -30
- package/package.json +1 -1
- package/readme.md +1 -1
- package/src/solarite/ExprPath.js +349 -151
- package/src/solarite/Globals.js +43 -1
- package/src/solarite/NodeGroup.js +275 -293
- package/src/solarite/Shell.js +26 -28
- package/src/solarite/Solarite.js +12 -0
- package/src/solarite/Template.js +55 -128
- package/src/solarite/Util.js +131 -7
- package/src/solarite/createSolarite.js +14 -19
- package/src/solarite/getArg.js +1 -1
- package/src/solarite/hash.js +18 -35
- package/src/solarite/r.js +127 -127
- package/src/solarite/watch3.js +18 -11
- package/src/{solarite → unused}/NodeGroupManager.js +81 -109
- package/src/{solarite → unused}/watch.js +1 -1
- package/src/{solarite → unused}/watch2.js +1 -2
- package/src/{solarite → util}/MultiValueMap.js +18 -11
- package/src/util/WeakArray.js +33 -0
- package/tests/Solarite.test.js +862 -167
- package/tests/index.html +4 -2
- package/tests/run.bat +1 -1
- package/deno.lock +0 -176
|
@@ -1,23 +1,12 @@
|
|
|
1
1
|
import MultiValueMap from "./MultiValueMap.js";
|
|
2
2
|
import NodeGroup from "./NodeGroup.js";
|
|
3
3
|
import {getObjectHash} from "./hash.js";
|
|
4
|
-
import {serializePath} from "./watch.js";
|
|
5
4
|
|
|
6
5
|
import {assert} from "../util/Errors.js";
|
|
6
|
+
import Globals from "./Globals.js";
|
|
7
|
+
import Template from "./Template.js";
|
|
7
8
|
|
|
8
9
|
|
|
9
|
-
/**
|
|
10
|
-
* @typedef {Object} RenderOptions
|
|
11
|
-
* @property {boolean=} styles - Indicates whether the Courage component is present.
|
|
12
|
-
* @property {boolean=} scripts - Indicates whether the Power component is present.
|
|
13
|
-
* @property {boolean=} ids *
|
|
14
|
-
* @property {?boolean} render
|
|
15
|
-
* Used only when options are given to a class super constructor inheriting from Solarite.
|
|
16
|
-
* True to call render() immediately in super constructor.
|
|
17
|
-
* False to automatically call render() at all.
|
|
18
|
-
* Undefined (default) to call render() when added to the DOM, unless already rendered.
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
10
|
|
|
22
11
|
/**
|
|
23
12
|
* Manage all the NodeGroups for a single WebComponent or root HTMLElement
|
|
@@ -25,7 +14,7 @@ import {assert} from "../util/Errors.js";
|
|
|
25
14
|
* And each NodeGroup manages the one or more nodes created by the expression.
|
|
26
15
|
*
|
|
27
16
|
* An instance of this class exists for each element that r() renders to. */
|
|
28
|
-
|
|
17
|
+
class NodeGroupManager {
|
|
29
18
|
|
|
30
19
|
/** @type {HTMLElement|DocumentFragment} */
|
|
31
20
|
rootEl;
|
|
@@ -46,13 +35,17 @@ export default class NodeGroupManager {
|
|
|
46
35
|
/**
|
|
47
36
|
* A map from the html strings and exprs that created a node group, to the NodeGroup.
|
|
48
37
|
* Also stores a map from just the html strings to the NodeGroup, so we can still find a similar match if the exprs changed.
|
|
49
|
-
*
|
|
50
38
|
* @type {MultiValueMap<string, (string|Template)[], NodeGroup>} */
|
|
51
39
|
nodeGroupsAvailable = new MultiValueMap();
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Save the NodeGroups that are returned by NodeGroupManager.get()
|
|
43
|
+
* Calling reset() at the end of render puts them all back into nodeGroupsAvailable.
|
|
44
|
+
* @type {NodeGroup[]} */
|
|
52
45
|
nodeGroupsInUse = [];
|
|
53
46
|
|
|
54
47
|
|
|
55
|
-
/** @type {RenderOptions} */
|
|
48
|
+
/** @type {RenderOptions} TODO: Where is this ever used? */
|
|
56
49
|
options = {};
|
|
57
50
|
|
|
58
51
|
|
|
@@ -65,17 +58,19 @@ export default class NodeGroupManager {
|
|
|
65
58
|
* @param rootEl {HTMLElement|DocumentFragment} If not specified, the first element of the html will be the rootEl. */
|
|
66
59
|
constructor(rootEl=null) {
|
|
67
60
|
this.rootEl = rootEl;
|
|
61
|
+
this.resetModifications();
|
|
68
62
|
|
|
69
63
|
/*
|
|
70
64
|
//#IFDEV
|
|
71
|
-
|
|
65
|
+
|
|
66
|
+
// Use MutationWatcher to check for illegal DOM modifications.
|
|
72
67
|
function closestCustomElement(node) {
|
|
73
68
|
do {
|
|
74
69
|
if (node.tagName && node.tagName.includes('-'))
|
|
75
70
|
return node;
|
|
76
71
|
} while (node = node.parentNode);
|
|
77
72
|
}
|
|
78
|
-
|
|
73
|
+
|
|
79
74
|
// TODO: Only trigger if we modify nodes inside an ExprPath.
|
|
80
75
|
// TODO: Enable this even when not in dev mode, because it's so useful for debugging?
|
|
81
76
|
// But it modifies the top level prototypes.
|
|
@@ -147,9 +142,10 @@ export default class NodeGroupManager {
|
|
|
147
142
|
if (!ng2.inUse) {
|
|
148
143
|
ng2.inUse = true;
|
|
149
144
|
let success = this.nodeGroupsAvailable.delete(ng2.exactKey, ng2);
|
|
150
|
-
|
|
145
|
+
assert(success);
|
|
146
|
+
|
|
151
147
|
let success2 = this.nodeGroupsAvailable.delete(ng2.closeKey, ng2);
|
|
152
|
-
|
|
148
|
+
assert(success2);
|
|
153
149
|
/*#IFDEV*/assert(success === success2)/*#ENDIF*/
|
|
154
150
|
|
|
155
151
|
// console.log(getHtml(ng2))
|
|
@@ -163,8 +159,10 @@ export default class NodeGroupManager {
|
|
|
163
159
|
// Recurse to mark all child NodeGroups as in-use.
|
|
164
160
|
for (let path of ng.paths)
|
|
165
161
|
for (let childNg of path.nodeGroups) {
|
|
166
|
-
if (!childNg.inUse)
|
|
162
|
+
if (!childNg.inUse) {
|
|
167
163
|
this.findAndDeleteExact(childNg.exactKey, false, childNg);
|
|
164
|
+
// this.findAndDeleteClose(childNg.closeKey, childNg.exactKey, false);
|
|
165
|
+
}
|
|
168
166
|
childNg.inUse = true;
|
|
169
167
|
}
|
|
170
168
|
|
|
@@ -189,6 +187,7 @@ export default class NodeGroupManager {
|
|
|
189
187
|
if (ng) {
|
|
190
188
|
|
|
191
189
|
// We matched on a new key, so delete the old exactKey.
|
|
190
|
+
/*#IFDEV*/assert(ng.exactKey);/*#ENDIF*/
|
|
192
191
|
let exactNg = this.nodeGroupsAvailable.delete(ng.exactKey, ng);
|
|
193
192
|
|
|
194
193
|
/*#IFDEV*/assert(exactNg);/*#ENDIF*/
|
|
@@ -203,10 +202,12 @@ export default class NodeGroupManager {
|
|
|
203
202
|
while (ng2 = ng2?.parentPath?.parentNg) {
|
|
204
203
|
if (!ng2.inUse) {
|
|
205
204
|
ng2.inUse = true; // Might speed it up slightly?
|
|
205
|
+
/*#IFDEV*/assert(ng2.exactKey);/*#ENDIF*/
|
|
206
206
|
let success = this.nodeGroupsAvailable.delete(ng2.exactKey, ng2);
|
|
207
207
|
/*#IFDEV*/assert(success);/*#ENDIF*/
|
|
208
208
|
|
|
209
|
-
// But it can still be a close match, so we
|
|
209
|
+
// But it can still be a close match, so we remove it there too.
|
|
210
|
+
/*#IFDEV*/assert(ng2.closeKey);/*#ENDIF*/
|
|
210
211
|
success = this.nodeGroupsAvailable.delete(ng2.closeKey, ng2);
|
|
211
212
|
/*#IFDEV*/assert(success);/*#ENDIF*/
|
|
212
213
|
|
|
@@ -244,59 +245,49 @@ export default class NodeGroupManager {
|
|
|
244
245
|
* Get an existing or create a new NodeGroup that matches the template,
|
|
245
246
|
* but don't reparent it if it's somewhere else.
|
|
246
247
|
* @param template {Template}
|
|
247
|
-
* @param exact {?boolean}
|
|
248
|
-
* @param
|
|
248
|
+
* @param exact {?boolean} If true, only get a NodeGroup if it matches both the template
|
|
249
|
+
* @param replaceMode {?boolean} If true, use the template to replace an existing element, instead of appending children to it.
|
|
249
250
|
* @return {?NodeGroup} */
|
|
250
|
-
getNodeGroup(template, exact=null,
|
|
251
|
-
|
|
252
|
-
let exactKey = getObjectHash(template)
|
|
251
|
+
getNodeGroup(template, exact=null, replaceMode=null, exactKey=null) {
|
|
252
|
+
exactKey = exactKey || getObjectHash(template);
|
|
253
253
|
|
|
254
|
-
/*#IFDEV*/if(
|
|
254
|
+
/*#IFDEV*/if(logEnabled) this.log(`Looking for ${exact ? 'exact' : 'close'} match: ` + template.debug)/*#ENDIF*/
|
|
255
255
|
|
|
256
256
|
// 1. Try to find an exact match.
|
|
257
|
-
let ng;
|
|
258
|
-
if (exact
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
if (!ng) {
|
|
262
|
-
/*#IFDEV*/this.log(`Not found.`)/*#ENDIF*/
|
|
263
|
-
return null;
|
|
264
|
-
}
|
|
265
|
-
/*#IFDEV*/if(NodeGroupManager.logEnabled) this.log(`Found exact: ` + ng.debug)/*#ENDIF*/
|
|
257
|
+
let ng = this.findAndDeleteExact(exactKey);
|
|
258
|
+
if (exact && !ng) {
|
|
259
|
+
/*#IFDEV*/this.log(`Not found.`)/*#ENDIF*/
|
|
260
|
+
return null;
|
|
266
261
|
}
|
|
262
|
+
/*#IFDEV*/if (logEnabled) this.log(`Found exact: ` + ng.debug);/*#ENDIF*/
|
|
267
263
|
|
|
268
264
|
// 2. Try to find a close match.
|
|
269
|
-
|
|
265
|
+
if (!ng) {
|
|
270
266
|
// We don't need to delete the exact match bc it's already been deleted in the prev pass.
|
|
271
267
|
let closeKey = template.getCloseKey();
|
|
272
|
-
ng =
|
|
268
|
+
ng = this.findAndDeleteClose(closeKey, exactKey);
|
|
273
269
|
|
|
274
270
|
// 2. Update expression values if they've changed.
|
|
275
271
|
if (ng) {
|
|
276
|
-
|
|
277
|
-
// Temporary for debugging:
|
|
278
|
-
if (window.debug && !window.ng)
|
|
279
|
-
window.ng = ng;
|
|
280
|
-
|
|
281
|
-
/*#IFDEV*/if(NodeGroupManager.logEnabled) this.log(`Found close: ` + closeKey + ' ' + ng.debug)/*#ENDIF*/
|
|
272
|
+
/*#IFDEV*/if (logEnabled) this.log(`Found close: ` + closeKey + ' ' + ng.debug);/*#ENDIF*/
|
|
282
273
|
/*#IFDEV*/this.incrementLogDepth(1);/*#ENDIF*/
|
|
283
274
|
/*#IFDEV*/ng.verify();/*#ENDIF*/
|
|
275
|
+
|
|
284
276
|
ng.applyExprs(template.exprs);
|
|
285
277
|
|
|
286
|
-
/*#IFDEV*/ng.verify()
|
|
278
|
+
/*#IFDEV*/ng.verify();/*#ENDIF*/
|
|
287
279
|
/*#IFDEV*/this.incrementLogDepth(-1);/*#ENDIF*/
|
|
288
|
-
/*#IFDEV*/if(
|
|
280
|
+
/*#IFDEV*/if (logEnabled) this.log(`Updated close to: ` + ng.debug);/*#ENDIF*/
|
|
289
281
|
}
|
|
290
282
|
|
|
291
283
|
// 3. Or if not found, create a new NodeGroup
|
|
292
284
|
else {
|
|
293
285
|
/*#IFDEV*/this.incrementLogDepth(1);/*#ENDIF*/
|
|
294
|
-
ng = new NodeGroup(template, this);
|
|
295
|
-
/*#IFDEV*/this.incrementLogDepth(-1);/*#ENDIF*/
|
|
296
286
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
287
|
+
ng = new NodeGroup(template, this, replaceMode, exactKey, closeKey);
|
|
288
|
+
|
|
289
|
+
/*#IFDEV*/this.incrementLogDepth(-1);/*#ENDIF*/
|
|
290
|
+
/*#IFDEV*/this.modifications.created.push(...ng.getNodes())/*#ENDIF*/
|
|
300
291
|
|
|
301
292
|
|
|
302
293
|
// 4. Mark NodeGroup as being in-use.
|
|
@@ -305,12 +296,9 @@ export default class NodeGroupManager {
|
|
|
305
296
|
// Perhaps also result could cache its last exprKey and then we'd use only one map?
|
|
306
297
|
ng.exactKey = exactKey;
|
|
307
298
|
ng.closeKey = closeKey;
|
|
308
|
-
|
|
309
|
-
this.nodeGroupsAvailable.add(ng.exactKey, ng);
|
|
310
|
-
else
|
|
311
|
-
this.nodeGroupsInUse.push(ng)
|
|
299
|
+
this.nodeGroupsInUse.push(ng)
|
|
312
300
|
|
|
313
|
-
/*#IFDEV*/if(
|
|
301
|
+
/*#IFDEV*/if (logEnabled) this.log(`Created new ` + ng.debug)/*#ENDIF*/
|
|
314
302
|
}
|
|
315
303
|
}
|
|
316
304
|
|
|
@@ -328,7 +316,13 @@ export default class NodeGroupManager {
|
|
|
328
316
|
return ng;
|
|
329
317
|
}
|
|
330
318
|
|
|
319
|
+
/**
|
|
320
|
+
* Move everything in nodeGroupsInUse to nodeGroupsAvailable.
|
|
321
|
+
* This is called after the NodeGroup has finished rendering. */
|
|
331
322
|
reset() {
|
|
323
|
+
|
|
324
|
+
/*#IFDEV*/this.rootNg.verify();/*#ENDIF*/
|
|
325
|
+
|
|
332
326
|
//this.changes = [];
|
|
333
327
|
let available = this.nodeGroupsAvailable
|
|
334
328
|
for (let ng of this.nodeGroupsInUse) {
|
|
@@ -343,61 +337,41 @@ export default class NodeGroupManager {
|
|
|
343
337
|
|
|
344
338
|
/*#IFDEV*/this.log('----------------------')/*#ENDIF*/
|
|
345
339
|
// TODO: free the memory from any nodeGroupsAvailable() after render is done, since they weren't used?
|
|
346
|
-
}
|
|
347
340
|
|
|
348
341
|
|
|
349
|
-
|
|
350
|
-
//pathToLoopInfo = new MultiValueMap(); // uses a Set() for each value.
|
|
351
|
-
clearSubscribers = false;
|
|
352
|
-
|
|
353
|
-
//#IFDEV
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* @deprecated - part of watch.js (Watch v1)
|
|
357
|
-
* One path may be used to loop in more than one place, so we use this to get every anchor from each loop.
|
|
358
|
-
* @param path {Array}
|
|
359
|
-
* @return {LoopInfo[]} A function that gets the loop anchor NodeGroup */
|
|
360
|
-
getLoopInfo(path) {
|
|
361
|
-
let serializedArrayPath = serializePath(path);
|
|
362
|
-
return [...this.pathToLoopInfo.getAll(serializedArrayPath)]; // This is set inside forEach()
|
|
342
|
+
/*#IFDEV*/this.rootNg.verify();/*#ENDIF*/
|
|
363
343
|
}
|
|
364
344
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
* @type {MultiValueMap<string, Subscriber>} */
|
|
373
|
-
pathToLoopInfo = new MultiValueMap();
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* Maps variable paths to the templates used to create NodeGroups
|
|
377
|
-
* @type {MultiValueMap<string, Subscriber>} */
|
|
378
|
-
subscribers = new MultiValueMap();
|
|
379
|
-
|
|
380
|
-
clearSubscribersIfNeeded() {
|
|
381
|
-
if (this.clearSubscribers) {
|
|
382
|
-
this.pathToLoopInfo = new MultiValueMap();
|
|
383
|
-
this.subscribers = new MultiValueMap();
|
|
384
|
-
this.clearSubscribers = false;
|
|
385
|
-
}
|
|
345
|
+
resetModifications() {
|
|
346
|
+
this.modifications = {
|
|
347
|
+
created: [],
|
|
348
|
+
updated: [],
|
|
349
|
+
moved: [],
|
|
350
|
+
deleted: []
|
|
351
|
+
};
|
|
386
352
|
}
|
|
387
|
-
|
|
388
353
|
|
|
389
354
|
/**
|
|
390
|
-
* Get the NodeGroupManager for a Web Component.
|
|
391
|
-
*
|
|
355
|
+
* Get the NodeGroupManager for a Web Component, given either its root element or its Template.
|
|
356
|
+
* If given a Template, create a new NodeGroup.
|
|
357
|
+
* Using a Template is typically used when creating a standalone component.
|
|
358
|
+
* @param rootEl {Solarite|HTMLElement|Template}
|
|
392
359
|
* @return {NodeGroupManager} */
|
|
393
360
|
static get(rootEl=null) {
|
|
394
|
-
|
|
395
|
-
|
|
361
|
+
let ngm;
|
|
362
|
+
if (rootEl instanceof Template) {
|
|
363
|
+
ngm = new NodeGroupManager();
|
|
364
|
+
ngm.rootNg = ngm.getNodeGroup(rootEl, false);
|
|
365
|
+
let el = ngm.rootNg.getRootNode();
|
|
366
|
+
Globals.nodeGroupManagers.set(el, ngm);
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
396
369
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
370
|
+
ngm = Globals.nodeGroupManagers.get(rootEl);
|
|
371
|
+
if (!ngm) {
|
|
372
|
+
ngm = new NodeGroupManager(rootEl);
|
|
373
|
+
Globals.nodeGroupManagers.set(rootEl, ngm);
|
|
374
|
+
}
|
|
401
375
|
}
|
|
402
376
|
|
|
403
377
|
return ngm;
|
|
@@ -405,13 +379,13 @@ export default class NodeGroupManager {
|
|
|
405
379
|
|
|
406
380
|
|
|
407
381
|
//#IFDEV
|
|
408
|
-
|
|
382
|
+
|
|
409
383
|
incrementLogDepth(level) {
|
|
410
384
|
this.logDepth += level;
|
|
411
385
|
}
|
|
412
386
|
log(msg, level=0) {
|
|
413
387
|
this.logDepth += level;
|
|
414
|
-
if (
|
|
388
|
+
if (logEnabled) {
|
|
415
389
|
let indent = ' '.repeat(this.logDepth);
|
|
416
390
|
console.log(indent + msg);
|
|
417
391
|
}
|
|
@@ -457,11 +431,9 @@ export default class NodeGroupManager {
|
|
|
457
431
|
|
|
458
432
|
NodeGroupManager.pendingChildren = [];
|
|
459
433
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
let nodeGroupManagers = new WeakMap();
|
|
464
|
-
|
|
434
|
+
//#IFDEV
|
|
435
|
+
let logEnabled = false // Prevent rollup's static analyzer from removing this.
|
|
436
|
+
//#ENDIF
|
|
465
437
|
|
|
466
438
|
|
|
467
439
|
export class LoopInfo {
|
|
@@ -5,7 +5,7 @@ import {assert} from "../util/Errors.js";
|
|
|
5
5
|
import delve from "../util/delve.js";
|
|
6
6
|
import {getObjectHash, getObjectId} from "./hash.js";
|
|
7
7
|
import MultiValueMap from "./MultiValueMap.js";
|
|
8
|
-
import NodeGroupManager, {LoopInfo} from "./NodeGroupManager.js";
|
|
8
|
+
//import NodeGroupManager, {LoopInfo} from "./NodeGroupManager.js";
|
|
9
9
|
import Template from "./Template.js";
|
|
10
10
|
|
|
11
11
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {assert} from "../util/Errors.js";
|
|
2
2
|
import {getObjectId} from "./hash.js";
|
|
3
|
-
import NodeGroupManager from "./NodeGroupManager.js";
|
|
3
|
+
//import NodeGroupManager from "./NodeGroupManager.js";
|
|
4
4
|
import Template from "./Template.js";
|
|
5
5
|
import {serializePath} from "./watch.js";
|
|
6
6
|
import delve from "../util/delve.js";
|
|
@@ -113,7 +113,6 @@ class ProxyHandler {
|
|
|
113
113
|
else if (prop === 'map' && Array.isArray(obj)) {
|
|
114
114
|
|
|
115
115
|
let ngm = NodeGroupManager.get(this.root);
|
|
116
|
-
ngm.clearSubscribersIfNeeded();
|
|
117
116
|
|
|
118
117
|
return callback => {
|
|
119
118
|
let loopInfo;
|
|
@@ -14,6 +14,12 @@ export default class MultiValueMap {
|
|
|
14
14
|
set.add(value);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
isEmpty() {
|
|
18
|
+
for (let key in this.data)
|
|
19
|
+
return true;
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
17
23
|
// Get all values for a key
|
|
18
24
|
getAll(key) {
|
|
19
25
|
return this.data[key] || [];
|
|
@@ -27,9 +33,9 @@ export default class MultiValueMap {
|
|
|
27
33
|
delete(key, val=undefined) {
|
|
28
34
|
// if (key === '["Html2",[[["Html3",["F1","A"]],["Html3",["F1","B"]]]]]')
|
|
29
35
|
// debugger;
|
|
30
|
-
|
|
36
|
+
|
|
31
37
|
let data = this.data;
|
|
32
|
-
|
|
38
|
+
|
|
33
39
|
// if (!data.hasOwnProperty(key))
|
|
34
40
|
// return undefined;
|
|
35
41
|
|
|
@@ -37,24 +43,25 @@ export default class MultiValueMap {
|
|
|
37
43
|
let result;
|
|
38
44
|
let set = data[key];
|
|
39
45
|
if (!set) // slower than pre-check.
|
|
40
|
-
|
|
46
|
+
return undefined;
|
|
41
47
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
result =
|
|
48
|
+
// Delete any value.
|
|
49
|
+
if (val === undefined) {
|
|
50
|
+
//result = set.values().next().value; // get first item from set.
|
|
51
|
+
[result] = set; // Does the same as above and seems to be about the same speed.
|
|
52
|
+
set.delete(result);
|
|
45
53
|
}
|
|
46
54
|
|
|
47
|
-
// Delete
|
|
55
|
+
// Delete a specific value.
|
|
48
56
|
else {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
set.delete(result);
|
|
57
|
+
set.delete(val);
|
|
58
|
+
result = val;
|
|
52
59
|
}
|
|
53
60
|
|
|
54
61
|
// TODO: Will this make it slower?
|
|
55
62
|
if (set.size === 0)
|
|
56
63
|
delete data[key];
|
|
57
|
-
|
|
64
|
+
|
|
58
65
|
return result;
|
|
59
66
|
}
|
|
60
67
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export default class WeakArray {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.items = [];
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
push(...items) {
|
|
7
|
+
for (let item of items) {
|
|
8
|
+
if (typeof item === 'object' && item)
|
|
9
|
+
this.items.push(new WeakRef(item));
|
|
10
|
+
else
|
|
11
|
+
throw new TypeError("Only objects can be added to a WeakArray");
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
get(index) {
|
|
16
|
+
const ref = this.items[index];
|
|
17
|
+
if (ref)
|
|
18
|
+
return ref.deref();
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
cleanup() {
|
|
23
|
+
this.items = this.items.filter(ref => ref.deref() !== undefined);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
*[Symbol.iterator]() {
|
|
27
|
+
for (const ref of this.items) {
|
|
28
|
+
const value = ref.deref();
|
|
29
|
+
if (value !== undefined)
|
|
30
|
+
yield value;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|