solarite 0.2.2 → 0.2.4
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/dist/Solarite-debug.js +357 -39
- package/dist/Solarite.js +357 -39
- package/dist/Solarite.min.js +2 -2
- package/package.json +3 -2
- package/src/solarite/ExprPath.js +84 -17
- package/src/solarite/Globals.js +3 -2
- package/src/solarite/NodeGroup.js +37 -0
- package/src/solarite/Solarite.js +1 -1
- package/src/solarite/Template.js +5 -3
- package/src/solarite/getArg.js +18 -8
- package/src/solarite/watch3.js +119 -28
- package/src/util/MultiValueMap.js +35 -2
- package/src/util/Util.js +26 -1
- package/src/unused/FastLookupArray.js +0 -54
- package/src/unused/Hashes.js +0 -339
- package/src/unused/InUse.test.js +0 -92
- package/src/unused/InUseMap.js +0 -98
- package/src/unused/LinkedList.js +0 -117
- package/src/unused/LinkedList.test.js +0 -115
- package/src/unused/NodeGroupManager.js +0 -444
- package/src/unused/Perf.js +0 -47
- package/src/unused/Template.js +0 -108
- package/src/unused/onConnect.js +0 -79
- package/src/unused/watch.js +0 -302
- package/src/unused/watch2.js +0 -439
|
@@ -1,444 +0,0 @@
|
|
|
1
|
-
import MultiValueMap from "./MultiValueMap.js";
|
|
2
|
-
import NodeGroup from "./NodeGroup.js";
|
|
3
|
-
import {getObjectHash} from "./hash.js";
|
|
4
|
-
|
|
5
|
-
import {assert} from "../util/Errors.js";
|
|
6
|
-
import Globals from "./Globals.js";
|
|
7
|
-
import Template from "./Template.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Manage all the NodeGroups for a single WebComponent or root HTMLElement
|
|
13
|
-
* There's one NodeGroup for the root of the WebComponent, and one for every ${...} expression that creates Node children.
|
|
14
|
-
* And each NodeGroup manages the one or more nodes created by the expression.
|
|
15
|
-
*
|
|
16
|
-
* An instance of this class exists for each element that r() renders to. */
|
|
17
|
-
class NodeGroupManager {
|
|
18
|
-
|
|
19
|
-
/** @type {HTMLElement|DocumentFragment} */
|
|
20
|
-
rootEl;
|
|
21
|
-
|
|
22
|
-
/** @type {NodeGroup} */
|
|
23
|
-
rootNg;
|
|
24
|
-
|
|
25
|
-
/** @type {Change[]} */
|
|
26
|
-
changes = [];
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
//#IFDEV
|
|
31
|
-
modifications;
|
|
32
|
-
logDepth=0
|
|
33
|
-
//#ENDIF
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* A map from the html strings and exprs that created a node group, to the NodeGroup.
|
|
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.
|
|
38
|
-
* @type {MultiValueMap<string, (string|Template)[], NodeGroup>} */
|
|
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[]} */
|
|
45
|
-
nodeGroupsInUse = [];
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
/** @type {RenderOptions} TODO: Where is this ever used? */
|
|
49
|
-
options = {};
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
//#IFDEV
|
|
53
|
-
mutationWatcher;
|
|
54
|
-
mutationWatcherEnabled = true;
|
|
55
|
-
//#ENDIF
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* @param rootEl {HTMLElement|DocumentFragment} If not specified, the first element of the html will be the rootEl. */
|
|
59
|
-
constructor(rootEl=null) {
|
|
60
|
-
this.rootEl = rootEl;
|
|
61
|
-
this.resetModifications();
|
|
62
|
-
|
|
63
|
-
/*
|
|
64
|
-
//#IFDEV
|
|
65
|
-
|
|
66
|
-
// Use MutationWatcher to check for illegal DOM modifications.
|
|
67
|
-
function closestCustomElement(node) {
|
|
68
|
-
do {
|
|
69
|
-
if (node.tagName && node.tagName.includes('-'))
|
|
70
|
-
return node;
|
|
71
|
-
} while (node = node.parentNode);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// TODO: Only trigger if we modify nodes inside an ExprPath.
|
|
75
|
-
// TODO: Enable this even when not in dev mode, because it's so useful for debugging?
|
|
76
|
-
// But it modifies the top level prototypes.
|
|
77
|
-
// TODO: Remove the onBeforeMutation callback when this.rootEl is not in the document.
|
|
78
|
-
// Because we won't get notified of document changes then anyway.
|
|
79
|
-
if (this.rootEl && this.rootEl.ownerDocument?.defaultView) { // TODO: Bind whenever we have rootEl.
|
|
80
|
-
this.mutationWatcher = MutationWatcher.getFromDocument(this.rootEl.ownerDocument);
|
|
81
|
-
this.mutationWatcher.onBeforeMutation.push((node, action, args) => {
|
|
82
|
-
|
|
83
|
-
// If a modification was made
|
|
84
|
-
if (this.mutationWatcherEnabled) {
|
|
85
|
-
if (this.rootEl.contains(node) && closestCustomElement(node) === this.rootEl) {
|
|
86
|
-
//console.log(node, action, args);
|
|
87
|
-
//throw new Error('DOM modification');
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// If another DOM node steals one of ours by adding it to itself.
|
|
91
|
-
// TODO: append can use multiple arguments.
|
|
92
|
-
if (['insertBefore', 'append', 'appendChild'].includes(action)
|
|
93
|
-
&& this.rootEl.contains(args[0]) && closestCustomElement(args[0]) === this.rootEl) {
|
|
94
|
-
|
|
95
|
-
//console.log(node, action, args);
|
|
96
|
-
//throw new Error('Another element attempted to steal one of our nodes.');
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
//#ENDIF
|
|
102
|
-
*/
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
*
|
|
108
|
-
* 1. Delete a NodeGroup from this.nodeGroupsAvailable that matches this exactKey.
|
|
109
|
-
* 2. Then delete all of that NodeGroup's parents' exactKey entries
|
|
110
|
-
* We don't move them to in-use because we plucked the NodeGroup from them, they no longer match their exactKeys.
|
|
111
|
-
* 3. Then we move all the NodeGroup's exact+close keyed children to inUse because we don't want future calls
|
|
112
|
-
* to getNodeGroup() to borrow the children now that the whole NodeGroup is in-use.
|
|
113
|
-
*
|
|
114
|
-
* TODO: Have NodeGroups keep track of whether they're inUse.
|
|
115
|
-
* That way when we go up or down we don't have to remove those with .inUse===true
|
|
116
|
-
*
|
|
117
|
-
* @param exactKey
|
|
118
|
-
* @param goUp
|
|
119
|
-
* @param child
|
|
120
|
-
* @returns {?NodeGroup} */
|
|
121
|
-
findAndDeleteExact(exactKey, goUp=true, child=undefined) {
|
|
122
|
-
|
|
123
|
-
let ng = this.nodeGroupsAvailable.delete(exactKey, child);
|
|
124
|
-
if (ng) {
|
|
125
|
-
/*#IFDEV*/assert(ng.exactKey === exactKey);/*#ENDIF*/
|
|
126
|
-
|
|
127
|
-
// Mark close-key version as in-use.
|
|
128
|
-
let closeNg = this.nodeGroupsAvailable.delete(ng.closeKey, ng);
|
|
129
|
-
/*#IFDEV*/assert(closeNg);/*#ENDIF*/
|
|
130
|
-
|
|
131
|
-
// Mark our self as in-use.
|
|
132
|
-
this.nodeGroupsInUse.push(ng)
|
|
133
|
-
|
|
134
|
-
ng.inUse = true;
|
|
135
|
-
closeNg.inUse = true;
|
|
136
|
-
|
|
137
|
-
// Mark all parents that have this NodeGroup as a child as in-use.
|
|
138
|
-
// So that way we don't use this parent again
|
|
139
|
-
if (goUp) {
|
|
140
|
-
let ng2 = ng;
|
|
141
|
-
while (ng2 = ng2?.parentPath?.parentNg) {
|
|
142
|
-
if (!ng2.inUse) {
|
|
143
|
-
ng2.inUse = true;
|
|
144
|
-
let success = this.nodeGroupsAvailable.delete(ng2.exactKey, ng2);
|
|
145
|
-
assert(success);
|
|
146
|
-
|
|
147
|
-
let success2 = this.nodeGroupsAvailable.delete(ng2.closeKey, ng2);
|
|
148
|
-
assert(success2);
|
|
149
|
-
/*#IFDEV*/assert(success === success2)/*#ENDIF*/
|
|
150
|
-
|
|
151
|
-
// console.log(getHtml(ng2))
|
|
152
|
-
if (success) {
|
|
153
|
-
this.nodeGroupsInUse.push(ng2)
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Recurse to mark all child NodeGroups as in-use.
|
|
160
|
-
for (let path of ng.paths)
|
|
161
|
-
for (let childNg of path.nodeGroups) {
|
|
162
|
-
if (!childNg.inUse) {
|
|
163
|
-
this.findAndDeleteExact(childNg.exactKey, false, childNg);
|
|
164
|
-
// this.findAndDeleteClose(childNg.closeKey, childNg.exactKey, false);
|
|
165
|
-
}
|
|
166
|
-
childNg.inUse = true;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (ng.parentPath) {
|
|
170
|
-
//ng.parentPath.clearNodesCache();
|
|
171
|
-
// ng.parentPath = null;
|
|
172
|
-
//ng.parentPath.removeNodeGroup(ng);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return ng;
|
|
176
|
-
}
|
|
177
|
-
return null;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* @param closeKey {string}
|
|
182
|
-
* @param exactKey {string}
|
|
183
|
-
* @param goUp {boolean}
|
|
184
|
-
* @returns {NodeGroup} */
|
|
185
|
-
findAndDeleteClose(closeKey, exactKey, goUp=true) {
|
|
186
|
-
let ng = this.nodeGroupsAvailable.delete(closeKey);
|
|
187
|
-
if (ng) {
|
|
188
|
-
|
|
189
|
-
// We matched on a new key, so delete the old exactKey.
|
|
190
|
-
/*#IFDEV*/assert(ng.exactKey);/*#ENDIF*/
|
|
191
|
-
let exactNg = this.nodeGroupsAvailable.delete(ng.exactKey, ng);
|
|
192
|
-
|
|
193
|
-
/*#IFDEV*/assert(exactNg);/*#ENDIF*/
|
|
194
|
-
/*#IFDEV*/assert(ng === exactNg)/*#ENDIF*/
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
ng.inUse = true;
|
|
198
|
-
if (goUp) {
|
|
199
|
-
let ng2 = ng;
|
|
200
|
-
|
|
201
|
-
// We borrowed a node from another node group so make sure its parent isn't still an exact match.
|
|
202
|
-
while (ng2 = ng2?.parentPath?.parentNg) {
|
|
203
|
-
if (!ng2.inUse) {
|
|
204
|
-
ng2.inUse = true; // Might speed it up slightly?
|
|
205
|
-
/*#IFDEV*/assert(ng2.exactKey);/*#ENDIF*/
|
|
206
|
-
let success = this.nodeGroupsAvailable.delete(ng2.exactKey, ng2);
|
|
207
|
-
/*#IFDEV*/assert(success);/*#ENDIF*/
|
|
208
|
-
|
|
209
|
-
// But it can still be a close match, so we remove it there too.
|
|
210
|
-
/*#IFDEV*/assert(ng2.closeKey);/*#ENDIF*/
|
|
211
|
-
success = this.nodeGroupsAvailable.delete(ng2.closeKey, ng2);
|
|
212
|
-
/*#IFDEV*/assert(success);/*#ENDIF*/
|
|
213
|
-
|
|
214
|
-
this.nodeGroupsInUse.push(ng2);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Recursively mark all child NodeGroups as in-use.
|
|
220
|
-
// We actually DON't want to do this becuse applyExprs is going to swap out the child NodeGroups
|
|
221
|
-
// and mark them as in-use as it goes.
|
|
222
|
-
// that's probably why uncommenting this causes tests to fail.
|
|
223
|
-
// for (let path of ng.paths)
|
|
224
|
-
// for (let childNg of path.nodeGroups)
|
|
225
|
-
// this.findAndDeleteExact(childNg.exactKey, false, childNg);
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
ng.exactKey = exactKey;
|
|
229
|
-
ng.closeKey = closeKey;
|
|
230
|
-
this.nodeGroupsInUse.push(ng)
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
if (ng.parentPath) {
|
|
234
|
-
//ng.parentPath.clearNodesCache();
|
|
235
|
-
//ng.parentPath = null;
|
|
236
|
-
//ng.parentPath.removeNodeGroup(ng);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
return ng;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Get an existing or create a new NodeGroup that matches the template,
|
|
246
|
-
* but don't reparent it if it's somewhere else.
|
|
247
|
-
* @param template {Template}
|
|
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.
|
|
250
|
-
* @return {?NodeGroup} */
|
|
251
|
-
getNodeGroup(template, exact=null, replaceMode=null, exactKey=null) {
|
|
252
|
-
exactKey = exactKey || getObjectHash(template);
|
|
253
|
-
|
|
254
|
-
/*#IFDEV*/if(logEnabled) this.log(`Looking for ${exact ? 'exact' : 'close'} match: ` + template.debug)/*#ENDIF*/
|
|
255
|
-
|
|
256
|
-
// 1. Try to find an exact match.
|
|
257
|
-
let ng = this.findAndDeleteExact(exactKey);
|
|
258
|
-
if (exact && !ng) {
|
|
259
|
-
/*#IFDEV*/this.log(`Not found.`)/*#ENDIF*/
|
|
260
|
-
return null;
|
|
261
|
-
}
|
|
262
|
-
/*#IFDEV*/if (logEnabled) this.log(`Found exact: ` + ng.debug);/*#ENDIF*/
|
|
263
|
-
|
|
264
|
-
// 2. Try to find a close match.
|
|
265
|
-
if (!ng) {
|
|
266
|
-
// We don't need to delete the exact match bc it's already been deleted in the prev pass.
|
|
267
|
-
let closeKey = template.getCloseKey();
|
|
268
|
-
ng = this.findAndDeleteClose(closeKey, exactKey);
|
|
269
|
-
|
|
270
|
-
// 2. Update expression values if they've changed.
|
|
271
|
-
if (ng) {
|
|
272
|
-
/*#IFDEV*/if (logEnabled) this.log(`Found close: ` + closeKey + ' ' + ng.debug);/*#ENDIF*/
|
|
273
|
-
/*#IFDEV*/this.incrementLogDepth(1);/*#ENDIF*/
|
|
274
|
-
/*#IFDEV*/ng.verify();/*#ENDIF*/
|
|
275
|
-
|
|
276
|
-
ng.applyExprs(template.exprs);
|
|
277
|
-
|
|
278
|
-
/*#IFDEV*/ng.verify();/*#ENDIF*/
|
|
279
|
-
/*#IFDEV*/this.incrementLogDepth(-1);/*#ENDIF*/
|
|
280
|
-
/*#IFDEV*/if (logEnabled) this.log(`Updated close to: ` + ng.debug);/*#ENDIF*/
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// 3. Or if not found, create a new NodeGroup
|
|
284
|
-
else {
|
|
285
|
-
/*#IFDEV*/this.incrementLogDepth(1);/*#ENDIF*/
|
|
286
|
-
|
|
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*/
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
// 4. Mark NodeGroup as being in-use.
|
|
294
|
-
// TODO: Moving from one group to another thrashes the gc. Is there a faster way?
|
|
295
|
-
// Could I have just a single WeakSet of those in use?
|
|
296
|
-
// Perhaps also result could cache its last exprKey and then we'd use only one map?
|
|
297
|
-
ng.exactKey = exactKey;
|
|
298
|
-
ng.closeKey = closeKey;
|
|
299
|
-
this.nodeGroupsInUse.push(ng)
|
|
300
|
-
|
|
301
|
-
/*#IFDEV*/if (logEnabled) this.log(`Created new ` + ng.debug)/*#ENDIF*/
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// New!
|
|
306
|
-
// We clear the parent PathExpr's nodesCache when we remove ourselves from it.
|
|
307
|
-
// Benchmarking shows this doesn't slow down the partialUpdate benchmark.
|
|
308
|
-
if (ng.parentPath) {
|
|
309
|
-
// ng.parentPath.clearNodesCache(); // Makes partialUpdate benchmark 10x slower!
|
|
310
|
-
ng.parentPath = null;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
/*#IFDEV*/ng.verify()/*#ENDIF*/
|
|
315
|
-
|
|
316
|
-
return ng;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* Move everything in nodeGroupsInUse to nodeGroupsAvailable.
|
|
321
|
-
* This is called after the NodeGroup has finished rendering. */
|
|
322
|
-
reset() {
|
|
323
|
-
|
|
324
|
-
/*#IFDEV*/this.rootNg.verify();/*#ENDIF*/
|
|
325
|
-
|
|
326
|
-
//this.changes = [];
|
|
327
|
-
let available = this.nodeGroupsAvailable
|
|
328
|
-
for (let ng of this.nodeGroupsInUse) {
|
|
329
|
-
ng.inUse = false;
|
|
330
|
-
available.add(ng.exactKey, ng)
|
|
331
|
-
available.add(ng.closeKey, ng)
|
|
332
|
-
}
|
|
333
|
-
this.nodeGroupsInUse = [];
|
|
334
|
-
|
|
335
|
-
// Used for watches
|
|
336
|
-
this.changes = [];
|
|
337
|
-
|
|
338
|
-
/*#IFDEV*/this.log('----------------------')/*#ENDIF*/
|
|
339
|
-
// TODO: free the memory from any nodeGroupsAvailable() after render is done, since they weren't used?
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
/*#IFDEV*/this.rootNg.verify();/*#ENDIF*/
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
resetModifications() {
|
|
346
|
-
this.modifications = {
|
|
347
|
-
created: [],
|
|
348
|
-
updated: [],
|
|
349
|
-
moved: [],
|
|
350
|
-
deleted: []
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
/**
|
|
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}
|
|
359
|
-
* @return {NodeGroupManager} */
|
|
360
|
-
static get(rootEl=null) {
|
|
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 {
|
|
369
|
-
|
|
370
|
-
ngm = Globals.nodeGroupManagers.get(rootEl);
|
|
371
|
-
if (!ngm) {
|
|
372
|
-
ngm = new NodeGroupManager(rootEl);
|
|
373
|
-
Globals.nodeGroupManagers.set(rootEl, ngm);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
return ngm;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
//#IFDEV
|
|
382
|
-
|
|
383
|
-
incrementLogDepth(level) {
|
|
384
|
-
this.logDepth += level;
|
|
385
|
-
}
|
|
386
|
-
log(msg, level=0) {
|
|
387
|
-
this.logDepth += level;
|
|
388
|
-
if (logEnabled) {
|
|
389
|
-
let indent = ' '.repeat(this.logDepth);
|
|
390
|
-
console.log(indent + msg);
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* @returns {NodeGroup[]} */
|
|
396
|
-
getAllAvailableGroups() {
|
|
397
|
-
let result = new Set();
|
|
398
|
-
for (let values of Object.values(this.nodeGroupsAvailable.data))
|
|
399
|
-
result.add(...values)
|
|
400
|
-
return [...result];
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
verify() {
|
|
404
|
-
if (!window.verify)
|
|
405
|
-
return;
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
let findCloseMatch = item => {
|
|
409
|
-
let names = this.nodeGroupsAvailable.hasValue(item);
|
|
410
|
-
for (let name of names)
|
|
411
|
-
if (name.startsWith('@'))
|
|
412
|
-
return true;
|
|
413
|
-
return false;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// Check to make sure every exact match is also in close matches.
|
|
417
|
-
for (let name in this.nodeGroupsAvailable)
|
|
418
|
-
if (!name.startsWith('@)'))
|
|
419
|
-
for (let item of this.nodeGroupsAvailable.getAll(name))
|
|
420
|
-
assert(findCloseMatch(item))
|
|
421
|
-
|
|
422
|
-
// Recursively traverse through all node Groups
|
|
423
|
-
if (this.rootNg)
|
|
424
|
-
this.rootNg.verify();
|
|
425
|
-
|
|
426
|
-
for (let ng of this.getAllAvailableGroups())
|
|
427
|
-
ng.verify();
|
|
428
|
-
}
|
|
429
|
-
//#ENDIF
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
NodeGroupManager.pendingChildren = [];
|
|
433
|
-
|
|
434
|
-
//#IFDEV
|
|
435
|
-
let logEnabled = false // Prevent rollup's static analyzer from removing this.
|
|
436
|
-
//#ENDIF
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
export class LoopInfo {
|
|
440
|
-
constructor(loopTemplate, itemTransformer) {
|
|
441
|
-
this.template = loopTemplate
|
|
442
|
-
this.itemTransformer = itemTransformer;
|
|
443
|
-
}
|
|
444
|
-
}
|
package/src/unused/Perf.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
var Perf = {
|
|
2
|
-
_timers: {},
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Start a timer with a given name.
|
|
6
|
-
* @param timerName {string}
|
|
7
|
-
*/
|
|
8
|
-
start(timerName) {
|
|
9
|
-
if (!Perf._timers[timerName]) {
|
|
10
|
-
Perf._timers[timerName] = {
|
|
11
|
-
startTime: null,
|
|
12
|
-
elapsedTime: 0
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
Perf._timers[timerName].startTime = performance.now();
|
|
16
|
-
},
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Stop a timer with a given name and calculate the elapsed time.
|
|
20
|
-
* @param timerName {string}
|
|
21
|
-
*/
|
|
22
|
-
stop(timerName) {
|
|
23
|
-
if (Perf._timers[timerName] && Perf._timers[timerName].startTime !== null) {
|
|
24
|
-
let stopTime = performance.now();
|
|
25
|
-
Perf._timers[timerName].elapsedTime += stopTime - Perf._timers[timerName].startTime;
|
|
26
|
-
Perf._timers[timerName].startTime = null; // Reset start time
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Get the total time elapsed for each timer.
|
|
32
|
-
* @return {string[]}
|
|
33
|
-
*/
|
|
34
|
-
getReport() {
|
|
35
|
-
let report = [];
|
|
36
|
-
for (let timerName in Perf._timers) {
|
|
37
|
-
report.push(`${timerName}: ${Perf._timers[timerName].elapsedTime.toFixed(2)} ms`);
|
|
38
|
-
}
|
|
39
|
-
return report;
|
|
40
|
-
},
|
|
41
|
-
|
|
42
|
-
clear() {
|
|
43
|
-
this._timers = {};
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export default Perf;
|
package/src/unused/Template.js
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import getObjectId from "./getObjectId.js";
|
|
2
|
-
|
|
3
|
-
import {memoize, hashObject, hashObject64, hash64Cache, hashObjectJson, getObjectId3} from "./Util.js";
|
|
4
|
-
|
|
5
|
-
let htmlMap = new WeakMap();
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* version of the template class that still has the old memoize function, If I ever want to compare hashing speed again.
|
|
9
|
-
*/
|
|
10
|
-
export default class Template {
|
|
11
|
-
|
|
12
|
-
/** @type {string[]} Html strings. Stored externally from object to speed up JSON serialization. */
|
|
13
|
-
//get html() { return htmlMap.get(this) }
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
/** @type {(Template|string|function)|(Template|string|function)[]} Evaulated expressions. */
|
|
17
|
-
exprs = []
|
|
18
|
-
|
|
19
|
-
html = [];
|
|
20
|
-
|
|
21
|
-
hashedFields;
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
*
|
|
25
|
-
* @param htmlStrings {string}
|
|
26
|
-
* @param exprs {*[]} */
|
|
27
|
-
constructor(htmlStrings, exprs) {
|
|
28
|
-
//htmlMap.set(this, htmlStrings)
|
|
29
|
-
this.html = htmlStrings;
|
|
30
|
-
this.exprs = exprs;
|
|
31
|
-
|
|
32
|
-
this.hashedFields = [getObjectId(htmlStrings), exprs]
|
|
33
|
-
|
|
34
|
-
//#IFDEV
|
|
35
|
-
Object.defineProperty(this, 'debug', {
|
|
36
|
-
get() {
|
|
37
|
-
return this.memoize();
|
|
38
|
-
}
|
|
39
|
-
})
|
|
40
|
-
//#ENDIF
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Called by JSON.serialize when it encounters a Template.
|
|
45
|
-
* This prevents the hashed version from being too large.
|
|
46
|
-
* @returns {*}
|
|
47
|
-
*/
|
|
48
|
-
toJSON() {
|
|
49
|
-
return this.hashedFields
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
memoize() {
|
|
53
|
-
|
|
54
|
-
// Slower
|
|
55
|
-
// let result2 = hashObject64(this)
|
|
56
|
-
// return result2[0].toString(16) + result2[1].toString(16);
|
|
57
|
-
|
|
58
|
-
// This version is slower but includes the full html in the template and can help with debugging:
|
|
59
|
-
//#IFDEV
|
|
60
|
-
// let exprLength = this.exprs.length;
|
|
61
|
-
// let l = this.html.length + exprLength;
|
|
62
|
-
// let result = new Array(l);
|
|
63
|
-
// for (let i=0; i<exprLength; i++) {
|
|
64
|
-
// result[i*2] = this.html[i];
|
|
65
|
-
// result[i*2+1] = '${' + s(this.exprs[i]) + '}'
|
|
66
|
-
// }
|
|
67
|
-
// result[result.length-1] = this.html[this.html.length-1];
|
|
68
|
-
//
|
|
69
|
-
// return result.join('')
|
|
70
|
-
//#ENDIF
|
|
71
|
-
|
|
72
|
-
// Speed up most common case:
|
|
73
|
-
if (this.exprs.length === 1)
|
|
74
|
-
return memoizeValue(this.exprs[0]) + '\f' + this.htmlId // \f is the "Form feed" control character, unlikely to be usedin regular text.
|
|
75
|
-
|
|
76
|
-
let exprLength = this.exprs.length;
|
|
77
|
-
let result = new Array(exprLength+1);
|
|
78
|
-
let exprs = this.exprs;
|
|
79
|
-
for (let i=0; i<exprLength; i++) {
|
|
80
|
-
result[i] = memoizeValue(exprs[i])
|
|
81
|
-
}
|
|
82
|
-
result[result.length] = this.htmlId;
|
|
83
|
-
|
|
84
|
-
return result.join('\f')
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
getCloseKey() {
|
|
88
|
-
// Use the joined html when debugging?
|
|
89
|
-
//return '@'+this.html.join('|')
|
|
90
|
-
|
|
91
|
-
return '@'+this.hashedFields[0];
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
function memoizeValue(val) {
|
|
99
|
-
if (typeof val === 'string')
|
|
100
|
-
return val;
|
|
101
|
-
else if (Array.isArray(val))
|
|
102
|
-
return '[' + val.map(v => memoizeValue(v)) + `]`; // join(',') is implicitly called here.
|
|
103
|
-
else if (val && val.memoize) // is another Template
|
|
104
|
-
return 'T('+val.memoize()+')'
|
|
105
|
-
else
|
|
106
|
-
return memoize(val); // TODO: This might not differentiate between 1 and '1'
|
|
107
|
-
|
|
108
|
-
}
|
package/src/unused/onConnect.js
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
// TODO: Everything here fails because our own connectedCallback function is never called.
|
|
2
|
-
// Perhaps b/c these callbacks must be defined before we register the custom element.
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* @type {Map<HTMLElement, Set<function(HTMLElement)>>} */
|
|
7
|
-
let connectCallbacks = new Map();
|
|
8
|
-
/**
|
|
9
|
-
* @type {Map<HTMLElement, Set<function(HTMLElement)>>} */
|
|
10
|
-
let firstConnectCallbacks = new Map();
|
|
11
|
-
/**
|
|
12
|
-
* @type {Map<HTMLElement, Set<function(HTMLElement)>>} */
|
|
13
|
-
let disconnectCallbacks = new Map();
|
|
14
|
-
|
|
15
|
-
function getElCallbacks(el, map) {
|
|
16
|
-
let callbacks = connectCallbacks.get(el);
|
|
17
|
-
if (!callbacks) {
|
|
18
|
-
callbacks = new Set();
|
|
19
|
-
connectCallbacks.set(el, callbacks);
|
|
20
|
-
}
|
|
21
|
-
return callbacks;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
let createConnectedCallback = el => () => {
|
|
25
|
-
debugger;
|
|
26
|
-
for (let cb of getElCallbacks(el, connectCallbacks))
|
|
27
|
-
cb();
|
|
28
|
-
|
|
29
|
-
let cbs = getElCallbacks(el, firstConnectCallbacks);
|
|
30
|
-
for (let cb of cbs) {
|
|
31
|
-
cb();
|
|
32
|
-
cbs.delete(cb);
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
*
|
|
39
|
-
* @param el {HTMLElement}
|
|
40
|
-
* @param callback {function}
|
|
41
|
-
* @return {function()} A function that removes the callback. */
|
|
42
|
-
export function onConnect(el, callback) {
|
|
43
|
-
let callbacks = getElCallbacks(el, connectCallbacks);
|
|
44
|
-
callbacks.add(callback);
|
|
45
|
-
|
|
46
|
-
if (!el.constructor.prototype.hasOwnProperty('connectedCallback'))
|
|
47
|
-
el.constructor.prototype.connectedCallback = createConnectedCallback(el);
|
|
48
|
-
|
|
49
|
-
return () => {
|
|
50
|
-
callbacks.delete(callback);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function onFirstConnect(el, callback) {
|
|
55
|
-
let callbacks = getElCallbacks(el, firstConnectCallbacks);
|
|
56
|
-
callbacks.add(callback);
|
|
57
|
-
|
|
58
|
-
if (!el.hasOwnProperty('connectedCallback'))
|
|
59
|
-
el.connectedCallback = createConnectedCallback(el);
|
|
60
|
-
|
|
61
|
-
return () => {
|
|
62
|
-
callbacks.delete(callback);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function onDisconnect(el, callback) {
|
|
67
|
-
let callbacks = getElCallbacks(el, disconnectCallbacks);
|
|
68
|
-
callbacks.add(callback);
|
|
69
|
-
|
|
70
|
-
if (!el.hasOwnProperty('connectedCallback'))
|
|
71
|
-
el.connectedCallback = () => {
|
|
72
|
-
for (let cb of callbacks)
|
|
73
|
-
cb();
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return () => {
|
|
77
|
-
callbacks.delete(callback);
|
|
78
|
-
}
|
|
79
|
-
}
|