solarite 0.2.4 → 0.3.1

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.
Files changed (38) hide show
  1. package/dist/Solarite-debug.js +1457 -1402
  2. package/dist/Solarite.js +1425 -1272
  3. package/dist/Solarite.min.js +2 -2
  4. package/package.json +5 -6
  5. package/readme.md +2 -4
  6. package/src/{solarite/ExprPath.js → ExprPath.js} +421 -231
  7. package/src/Globals.js +79 -0
  8. package/src/HtmlParser.js +91 -0
  9. package/src/{util/MultiValueMap.js → MultiValueMap.js} +22 -26
  10. package/src/{solarite/NodeGroup.js → NodeGroup.js} +286 -226
  11. package/src/{solarite/Shell.js → Shell.js} +119 -92
  12. package/src/Solarite.d.ts +62 -0
  13. package/src/{solarite/Solarite.js → Solarite.js} +15 -13
  14. package/src/{solarite/Template.js → Template.js} +22 -19
  15. package/src/Util.js +330 -0
  16. package/src/{util/Errors.js → assert.js} +1 -0
  17. package/src/createSolarite.js +154 -0
  18. package/src/{util/delve.js → delve.js} +5 -4
  19. package/src/{solarite/getArg.js → getArg.js} +41 -15
  20. package/src/{solarite/r.js → h.js} +59 -29
  21. package/src/{solarite/hash.js → hash.js} +12 -9
  22. package/src/unused/FastLookupArray.js +54 -0
  23. package/src/unused/Hashes.js +339 -0
  24. package/src/unused/InUse.test.js +92 -0
  25. package/src/unused/InUseMap.js +98 -0
  26. package/src/unused/LinkedList.js +117 -0
  27. package/src/unused/LinkedList.test.js +115 -0
  28. package/src/unused/Misc.js +13 -0
  29. package/src/unused/Perf.js +47 -0
  30. package/src/unused/TrackedArray.js +54 -0
  31. package/src/watch.js +546 -0
  32. package/src/solarite/Globals.js +0 -54
  33. package/src/solarite/Util.js +0 -388
  34. package/src/solarite/createSolarite.js +0 -274
  35. package/src/solarite/watch3.js +0 -189
  36. package/src/util/Util.js +0 -113
  37. /package/src/{solarite/udomdiff.js → udomdiff.js} +0 -0
  38. /package/src/{util → unused}/WeakArray.js +0 -0
@@ -1,388 +0,0 @@
1
- let Util = {
2
-
3
- bindStyles(style, root) {
4
- let styleId = root.getAttribute('data-style');
5
- if (!styleId) {
6
- // Keep track of one style id for each class.
7
- // TODO: Put this outside the class in a map, so it doesn't conflict with static properties.
8
- if (!root.constructor.styleId)
9
- root.constructor.styleId = 1;
10
- styleId = root.constructor.styleId++;
11
-
12
- root.setAttribute('data-style', styleId)
13
- }
14
-
15
- let tagName = root.tagName.toLowerCase();
16
- for (let child of style.childNodes) {
17
- if (child.nodeType === 3) {
18
- let oldText = child.textContent;
19
- let newText = oldText.replace(/:host(?=[^a-z0-9_])/gi, tagName + '[data-style="' + styleId + '"]')
20
- if (oldText !== newText)
21
- child.textContent = newText;
22
- }
23
- }
24
- },
25
-
26
- /**
27
- * A generator function that recursively traverses and flattens a value.
28
- *
29
- * - If the input is an array, it recursively traverses and flattens the array.
30
- * - If the input is a function, it calls the function, replaces the function
31
- * with its result, and flattens the result if necessary. It will recursively
32
- * call functions that return other functions.
33
- * - Otherwise it yields the value as is.
34
- *
35
- * This function does not create a new array for the flattened values. Instead,
36
- * it lazily yields each item as it is encountered. This can be more memory-efficient
37
- * for large or deeply nested structures.
38
- *
39
- * @param {any} value - The value to flatten. Can be an array, object, function, or primitive.
40
- * @yields {any} - The next item in the flattened structure.
41
- *
42
- * @example
43
- * const complexArray = [
44
- * 1,
45
- * [2, () => 3, [4, () => [5, 6]], { a: 'object' }],
46
- * () => () => 7,
47
- * () => [() => 8, 9],
48
- * ]; *
49
- * for (const item of flatten(complexArray))
50
- * console.log(item); // Outputs: 1, 2, 3, 4, 5, 6, { a: 'object' }, 7, 8, 9
51
- */
52
- *flatten(value) {
53
- if (Array.isArray(value)) {
54
- for (const item of value) {
55
- yield* Util.flatten(item); // Recursively flatten arrays
56
- }
57
- } else if (typeof value === 'function') {
58
- const result = value();
59
- yield* Util.flatten(result); // Recursively flatten the result of a function
60
- } else
61
- yield value; // Yield primitive values as is
62
- },
63
-
64
- /**
65
- * Get the value of an input as the most appropriate JavaScript type.
66
- * @param node {HTMLInputElement|HTMLSelectElement|HTMLTextAreaElement|HTMLDivElement}
67
- * @return {string|string[]|number|[]|File[]|Date|boolean} */
68
- getInputValue(node) {
69
- if (node.type === 'checkbox' || node.type === 'radio')
70
- return node.checked; // Boolean
71
- if (node.type === 'file')
72
- return [...node.files]; // FileList
73
- if (node.type === 'number' || node.type === 'range')
74
- return node.valueAsNumber; // Number
75
- if (node.type === 'date' || node.type === 'time' || node.type === 'datetime-local')
76
- return node.valueAsDate; // Date Object
77
- if (node.type === 'select-multiple') // <select multiple>
78
- return [...node.selectedOptions].map(option => option.value); // Array of Strings
79
-
80
- return node.value; // String
81
- },
82
-
83
- /**
84
- * Is it an array and a path that can be evaluated by delve() ?
85
- * @param arr {Array|*}
86
- * @returns {boolean} */
87
- isPath(arr) {
88
- return Array.isArray(arr) && typeof arr[0] === 'object' && !arr.slice(1).find(p => typeof p !== 'string' && typeof p !== 'number');
89
- },
90
-
91
- /**
92
- * Find NodeGroups that had their nodes removed and add those nodes to a Fragment so
93
- * they're not lost forever and the NodeGroup's internal structure is still consistent.
94
- * This saves all of a NodeGroup's nodes in order, so that nextChildNode still works.
95
- * This is necessary because a NodeGroup normally only stores the first and last node.
96
- * Called from ExprPath.apply().
97
- * @param oldNodeGroups {NodeGroup[]}
98
- * @param oldNodes {Node[]} */
99
- saveOrphans(oldNodeGroups, oldNodes) {
100
- let oldNgMap = new Map();
101
- for (let ng of oldNodeGroups) {
102
- oldNgMap.set(ng.startNode, ng)
103
-
104
- // TODO: Is this necessary?
105
- // if (ng.parentPath)
106
- // ng.parentPath.clearNodesCache();
107
- }
108
-
109
- for (let i=0, node; node = oldNodes[i]; i++) {
110
- let ng;
111
- if (!node.parentNode && (ng = oldNgMap.get(node))) {
112
- //ng.nodesCache = [];
113
- let fragment = document.createDocumentFragment();
114
- let endNode = ng.endNode;
115
- while (node !== endNode) {
116
- fragment.append(node);
117
- //ng.nodesCache.push(node);
118
- i++;
119
- node = oldNodes[i];
120
- }
121
- fragment.append(endNode);
122
- //ng.nodesCache.push(endNode);
123
- }
124
- }
125
- },
126
-
127
- /**
128
- * Remove nodes from the beginning and end that are not:
129
- * 1. Elements.
130
- * 2. Non-whitespace text nodes.
131
- * @param nodes {Node[]|NodeList}
132
- * @returns {Node[]} */
133
- trimEmptyNodes(nodes) {
134
- const shouldTrimNode = node =>
135
- node.nodeType !== Node.ELEMENT_NODE &&
136
- (node.nodeType !== Node.TEXT_NODE || node.textContent.trim() === '');
137
-
138
- // Convert nodeList to an array for easier manipulation
139
- const result = [...nodes]
140
-
141
- // Trim from the start
142
- while (result.length > 0 && shouldTrimNode(result[0]))
143
- result.shift();
144
-
145
- // Trim from the end
146
- while (result.length > 0 && shouldTrimNode(result[result.length - 1]))
147
- result.pop();
148
-
149
- return result;
150
- }
151
- };
152
-
153
- export default Util;
154
-
155
-
156
-
157
- let div = document.createElement('div');
158
- export {div}
159
-
160
- let isEvent = attrName => attrName.startsWith('on') && attrName in div;
161
- export {isEvent};
162
-
163
-
164
- /**
165
- * Convert a Proper Case name to a name with dashes.
166
- * Dashes will be placed between letters and numbers.
167
- * If there are multiple consecutive capital letters followed by another chracater, a dash will be placed before the last capital letter.
168
- * @param str {string}
169
- * @return {string}
170
- *
171
- * @example
172
- * 'ProperName' => 'proper-name'
173
- * 'HTMLElement' => 'html-element'
174
- * 'BigUI' => 'big-ui'
175
- * 'UIForm' => 'ui-form'
176
- * 'A100' => 'a-100' */
177
- export function camelToDashes(str) {
178
- // Convert any capital letter that is preceded by a lowercase letter or number to lowercase and precede with a dash.
179
- str = str.replace(/([a-z0-9])([A-Z])/g, '$1-$2');
180
-
181
- // Convert any capital letter that is followed by a lowercase letter or number to lowercase and precede with a dash.
182
- str = str.replace(/([A-Z])([A-Z][a-z])/g, '$1-$2');
183
-
184
- // Convert any number that is preceded by a lowercase or uppercase letter to be preceded by a dash.
185
- str = str.replace(/([a-zA-Z])([0-9])/g, '$1-$2');
186
-
187
- // Convert all the remaining capital letters to lowercase.
188
- return str.toLowerCase();
189
- }
190
-
191
-
192
-
193
-
194
-
195
- /**
196
- * Returns false if they're the same. Or the first index where they differ.
197
- * @param a
198
- * @param b
199
- * @returns {boolean} */
200
- export function arraySame(a, b) {
201
- let aLength = a.length;
202
- if (aLength !== b.length)
203
- return false;
204
- for (let i=0; i<aLength; i++)
205
- if (a[i] !== b[i])
206
- return false;
207
- return true; // the same.
208
- }
209
-
210
-
211
- /**
212
- * TODO: Turn this into a class because it has internal state.
213
- * TODO: Don't break on 3<a inside a <script> or <style> tag.
214
- * @param html {?string} Pass null to reset context.
215
- * @returns {string} */
216
- export function htmlContext(html) {
217
- if (html === null) {
218
- state = {...defaultState};
219
- return state.context;
220
- }
221
- for (let i = 0; i < html.length; i++) {
222
- const char = html[i];
223
- switch (state.context) {
224
- case htmlContext.Text:
225
- if (char === '<' && html[i+1].match(/[a-z!]/i)) { // Start of a tag or comment.
226
- // if (html.slice(i, i+4) === '<!--')
227
- // state.context = htmlContext.Comment;
228
- // else
229
- state.context = htmlContext.Tag;
230
- state.buffer = '';
231
- }
232
- break;
233
- case htmlContext.Tag:
234
- if (char === '>') {
235
- state.context = htmlContext.Text;
236
- state.quote = null;
237
- state.buffer = '';
238
- } else if (char === ' ' && !state.buffer) {
239
- // No attribute name is present. Skipping the space.
240
- continue;
241
- } else if (char === ' ' || char === '/' || char === '?') {
242
- state.buffer = ''; // Reset the buffer when a delimiter or potential self-closing sign is found.
243
- } else if (char === '"' || char === "'" || char === '=') {
244
- state.context = htmlContext.Attribute;
245
- state.quote = char === '=' ? null : char;
246
- state.buffer = '';
247
- } else {
248
- state.buffer += char;
249
- }
250
- break;
251
- case htmlContext.Attribute:
252
- if (!state.quote && !state.buffer.length && (char === '"' || char === "'"))
253
- state.quote = char;
254
-
255
- else if (char === state.quote || (!state.quote && state.buffer.length)) {
256
- state.context = htmlContext.Tag;
257
- state.quote = null;
258
- state.buffer = '';
259
- } else if (!state.quote && char === '>') {
260
- state.context = htmlContext.Text;
261
- state.quote = null;
262
- state.buffer = '';
263
- } else if (char !== ' ') {
264
- state.buffer += char;
265
- }
266
- break;
267
- }
268
-
269
- }
270
- return state.context;
271
- }
272
-
273
-
274
- htmlContext.Attribute = 'Attribute';
275
- htmlContext.Text = 'Text';
276
- htmlContext.Tag = 'Tag';
277
- //htmlContext.Comment = 'Comment';
278
- let defaultState = {
279
- context: htmlContext.Text, // possible values: 'TEXT', 'TAG', 'ATTRIBUTE'
280
- quote: null, // possible values: null, '"', "'"
281
- buffer: '',
282
- lastChar: null
283
- };
284
- let state = {...defaultState};
285
-
286
-
287
-
288
-
289
-
290
-
291
- let cacheItems = {};
292
-
293
- /**
294
- * @param item {string}
295
- * @param initial {*}
296
- * @returns {*} */
297
- export function cache(item, initial) {
298
- let result = cacheItems[item];
299
- if (!result) {
300
- cacheItems[item] = initial
301
- result = initial;
302
- }
303
- return result;
304
- }
305
-
306
-
307
-
308
- export class WeakCache {
309
-
310
- items = new WeakMap();
311
-
312
- constructor(initial) {
313
- this.initial = initial;
314
- }
315
-
316
- get(item) {
317
- let result = this.items.get(item);
318
- if (!result) {
319
- let value = typeof this.initial === 'function' ? this.initial() : this.initial;
320
- this.items.set(item, value)
321
- result = this.initial;
322
- }
323
- return result;
324
- }
325
- }
326
-
327
-
328
- // For debugging only
329
- //#IFDEV
330
- export function setIndent(items, level=1) {
331
- if (typeof items === 'string')
332
- items = items.split(/\r?\n/g)
333
-
334
- return items.map(str => {
335
- if (level > 0)
336
- return ' '.repeat(level) + str;
337
- else if (level < 0)
338
- return str.replace(new RegExp(`^ {0,${Math.abs(level)}}`), '');
339
- return str;
340
- })
341
- }
342
-
343
- export function nodeToArrayTree(node, callback=null) {
344
- if (!node) return [];
345
-
346
- let result = [];
347
-
348
- if (callback)
349
- result.push(...callback(node))
350
-
351
- if (node.nodeType === 1) {
352
- let attrs = Array.from(node.attributes).map(attr => `${attr.name}="${attr.value}"`).join(' ');
353
- let openingTag = `<${node.nodeName.toLowerCase()}${attrs ? ' ' + attrs : ''}>`;
354
-
355
- let childrenArray = [];
356
- for (let child of node.childNodes) {
357
- let childResult = nodeToArrayTree(child, callback);
358
- if (childResult.length > 0) {
359
- childrenArray.push(childResult);
360
- }
361
- }
362
-
363
- //let closingTag = `</${node.nodeName.toLowerCase()}>`;
364
-
365
- result.push(openingTag, ...childrenArray);
366
- } else if (node.nodeType === 3) {
367
- result.push("'"+node.nodeValue+"'");
368
- }
369
-
370
- return result;
371
- }
372
-
373
-
374
- export function flattenAndIndent(inputArray, indent = "") {
375
- let result = [];
376
-
377
- for (let item of inputArray) {
378
- if (Array.isArray(item)) {
379
- // Recursively handle nested arrays with increased indentation
380
- result = result.concat(flattenAndIndent(item, indent + " "));
381
- } else {
382
- result.push(indent + item);
383
- }
384
- }
385
-
386
- return result;
387
- }
388
- //#ENDIF
@@ -1,274 +0,0 @@
1
- import Util from "../util/Util.js";
2
- //import {assert} from "../util/Errors.js";
3
- import delve from "../util/delve.js";
4
- import {getArg, ArgType} from "./getArg.js";
5
- import {getObjectHash} from "./hash.js";
6
- //import NodeGroupManager from "./NodeGroupManager.js";
7
- import r from "./r.js";
8
- import {camelToDashes} from "./Util.js";
9
- import Globals from "./Globals.js";
10
-
11
-
12
- //import {watchGet, watchSet} from "./watch.js";
13
-
14
-
15
-
16
- function defineClass(Class, tagName, extendsTag) {
17
- if (!customElements.getName(Class)) { // If not previously defined.
18
- tagName = tagName || camelToDashes(Class.name)
19
- if (!tagName.includes('-'))
20
- tagName += '-element';
21
-
22
- let options = null;
23
- if (extendsTag)
24
- options = {extends: extendsTag}
25
-
26
- customElements.define(tagName, Class, options)
27
- }
28
- }
29
-
30
-
31
-
32
-
33
-
34
- /**
35
- * Create a version of the Solarite class that extends from the given tag name.
36
- * Reasons to inherit from this instead of HTMLElement. None of these are all that useful.
37
- * 1. customElements.define() is called automatically when you create the first instance.
38
- * 2. Calls render() when added to the DOM, if it hasn't been called already.
39
- * 3. Child elements are added before constructor is called. But they're also passed to the constructor.
40
- * 4. We can use this.html = r`...` to set html.
41
- * 5. We have the onConnect, onFirstConnect, and onDisconnect methods.
42
- * Can't figure out how to have these work standalone though, and still be synchronous.
43
- * 6. Can we extend from other element types like TR?
44
- * 7. Shows default text if render() function isn't defined.
45
- *
46
- * Advantages to inheriting from HTMLElement
47
- * 1. Minimization won't break when it renames the Class and we call customElements.define() on the wrong name.
48
- * 2. We can inherit from things like HTMLTableRowElement directly.
49
- * 3. There's less magic, since everyone is familiar with defining custom elements.
50
- *
51
- * @param extendsTag {?string}
52
- * @return {Class} */
53
- export default function createSolarite(extendsTag=null) {
54
-
55
- let BaseClass = HTMLElement;
56
- if (extendsTag && !extendsTag.includes('-')) {
57
- extendsTag = extendsTag.toLowerCase();
58
-
59
- BaseClass = Globals.elementClasses[extendsTag];
60
- if (!BaseClass) { // TODO: Use Cache
61
- BaseClass = document.createElement(extendsTag).constructor;
62
- Globals.elementClasses[extendsTag] = BaseClass
63
- }
64
- }
65
-
66
- /**
67
- * Intercept the construct call to auto-define the class before the constructor is called.
68
- * @type {HTMLElement} */
69
- let HTMLElementAutoDefine = new Proxy(BaseClass, {
70
- construct(Parent, args, Class) {
71
- defineClass(Class, null, extendsTag)
72
-
73
- // This is a good place to manipulate any args before they're sent to the constructor.
74
- // Such as loading them from attributes, if I could find a way to do so.
75
-
76
- // This line is equivalent the to super() call.
77
- return Reflect.construct(Parent, args, Class);
78
- }
79
- });
80
-
81
- return class Solarite extends HTMLElementAutoDefine {
82
-
83
-
84
- /**
85
- * TODO: Make these standalone functions.
86
- * Callbacks.
87
- * Use onConnect.push(() => ...); to add new callbacks. */
88
- onConnect = Util.callback();
89
-
90
- onFirstConnect = Util.callback();
91
- onDisconnect = Util.callback();
92
-
93
- /**
94
- * @param options {RenderOptions} */
95
- constructor(options={}) {
96
- super();
97
-
98
- // TODO: Is options.render ever used?
99
- if (options.render===true)
100
- this.render();
101
-
102
- else if (options.render===false)
103
- Globals.rendered.add(this); // Don't render on connectedCallback()
104
-
105
- // Add children before constructor code executes.
106
- // PendingChildren is setup in NodeGroup.createNewComponent()
107
- // TODO: Match named slots.
108
- let ch = Globals.pendingChildren.pop();
109
- if (ch)
110
- (this.querySelector('slot') || this).append(...ch);
111
-
112
- /** @deprecated */
113
- Object.defineProperty(this, 'html', {
114
- set(html) {
115
- Globals.rendered.add(this);
116
- if (typeof html === 'string') {
117
- console.warn("Assigning to this.html without the r template prefix.")
118
- this.innerHTML = html;
119
- }
120
- else
121
- this.modifications = r(this, html, options);
122
- }
123
- })
124
-
125
- /*
126
- let pthis = new Proxy(this, {
127
- get(obj, prop) {
128
- return Reflect.get(obj, prop)
129
- }
130
- });
131
- this.render = this.render.bind(pthis);
132
- */
133
- }
134
-
135
- /**
136
- * Call render() only if it hasn't already been called. */
137
- renderFirstTime() {
138
- if (!Globals.rendered.has(this) && this.render)
139
- this.render();
140
- }
141
-
142
- /**
143
- * Called automatically by the browser. */
144
- connectedCallback() {
145
- this.renderFirstTime();
146
- if (!Globals.connected.has(this)) {
147
- Globals.connected.add(this);
148
- this.onFirstConnect();
149
- }
150
- this.onConnect();
151
- }
152
-
153
- disconnectedCallback() {
154
- this.onDisconnect();
155
- }
156
-
157
-
158
- static define(tagName=null) {
159
- defineClass(this, tagName, extendsTag)
160
- }
161
-
162
- //#IFDEV
163
-
164
- /** @deprecated */
165
- renderWatched() {
166
- let ngm = NodeGroupManager.get(this);
167
-
168
- let nodeGroupUpdates = [];
169
-
170
- for (let change of ngm.changes) {
171
- if (change.action === 'set') {
172
- for (let transformerInfo of change.transformerInfo) {
173
-
174
- let oldHash = transformerInfo.hash;
175
-
176
- let newObj = delve(watchSet(transformerInfo.path[0]), transformerInfo.path.slice(1));
177
- let newTemplate = transformerInfo.transformer(newObj);
178
- let newHash = getObjectHash(newTemplate);
179
- let ngs = [...ngm.nodeGroupsAvailable.data[oldHash]];
180
- for (let ng of ngs) {
181
- nodeGroupUpdates.push([ng, oldHash, newHash, newTemplate.exprs, transformerInfo]);
182
- }
183
- }
184
- }
185
-
186
- else if (change.action === 'delete') {
187
- for (let hash of change.value) {
188
- let ngs = [...ngm.nodeGroupsAvailable.getAll(hash)]; // deletes from nodeGroupsAvailable.
189
-
190
- for (let ng of ngs) {
191
- if (ng.parentPath)
192
- ng.parentPath.clearNodesCache();
193
-
194
- for (let node of ng.getNodes())
195
- node.remove();
196
-
197
- // TODO: Update ancestor NodeGroup exactKeys
198
- }
199
- }
200
- }
201
- else if (change.action === 'insert') {
202
-
203
- let beforeNg = change.beforeTemplate ? ngm.getNodeGroup(change.beforeTemplate, true) : null;
204
- let arrayPath = [change.root, ...change.path];
205
-
206
- // Get anchor so we can use it to get the parent
207
- // TODO: Should this be watchGet(change.root) ?
208
- for (let loopInfo of ngm.getLoopInfo([change.root, ...change.path.slice(0, -1)])) {
209
-
210
- // Change.extra is aTemplate telling us where to insert before.
211
- let beforeNode = beforeNg?.startNode || loopInfo.template.parentPath.nodeMarker;
212
-
213
- // Loop over every item added to the array.
214
- let i = 0; // TODO: How to get real insert index.
215
- for (let obj of change.value) {
216
-
217
- // Same logic as forEach() function.
218
-
219
- let callback = loopInfo.itemTransformer;
220
- let path = [...arrayPath.slice(0, -1), (arrayPath.at(-1) * 1 + i) + ''];
221
-
222
- // Shortened logic found in watchGet(), but not any faster?
223
- // the watchSet() is what makes this slower!
224
- // let obj = delve(watchSet(path[0]), path.slice(1));
225
- // let template = callback(obj);
226
- // let serializedPath = serializePath(path);
227
- // pathToTransformer.add(serializedPath, new TransformerInfo(path, callback, template)); // Uses a Set() to ensure no duplicates.
228
-
229
- let template = watchGet(path, callback);
230
- i++;
231
-
232
-
233
- //let template = loopInfo.itemTransformer(obj); // What if it takes more than one obj argument?
234
-
235
- // Create new NodeGroup
236
- let ng = ngm.getNodeGroup(template, false, true);
237
- ng.parentPath = beforeNg?.parentPath || loopInfo.template.parentPath;
238
-
239
- for (let node of ng.getNodes())
240
- beforeNode.parentNode.insertBefore(node, beforeNode);
241
-
242
- if (ng.parentPath) // This check is needed for the forEachSpliceInsert test, but why?
243
- ng.parentPath.clearNodesCache();
244
- }
245
-
246
- // TODO: Update ancestor NodeGroup exactKeys
247
- }
248
- }
249
- }
250
-
251
- // Update them all at once, that way we can reassign the same value twice.
252
- for (let [ng, oldHash, newHash, exprs, ti] of nodeGroupUpdates) {
253
- ng.applyExprs(exprs);
254
- ngm.nodeGroupsAvailable.data[oldHash].delete(ng);
255
- ng.exactKey = ti.hash = newHash;
256
- ngm.nodeGroupsAvailable.add(ng.exactKey, ng); // Add back to Map with new key.
257
- }
258
-
259
-
260
- ngm.changes = [];
261
-
262
- return []; // TODO
263
- }
264
-
265
- /**
266
- * @deprecated Use the getArg() function instead. */
267
- getArg(name, val=null, type=ArgType.String) {
268
- throw new Error('deprecated');
269
- return getArg(this, name, val, type);
270
- }
271
- //#ENDIF
272
- }
273
- }
274
-