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
package/src/unused/watch2.js
DELETED
|
@@ -1,439 +0,0 @@
|
|
|
1
|
-
import {assert} from "../util/Errors.js";
|
|
2
|
-
import {getObjectId} from "./hash.js";
|
|
3
|
-
//import NodeGroupManager from "./NodeGroupManager.js";
|
|
4
|
-
import Template from "./Template.js";
|
|
5
|
-
import {serializePath} from "./watch.js";
|
|
6
|
-
import delve from "../util/delve.js";
|
|
7
|
-
|
|
8
|
-
let logGets = false;
|
|
9
|
-
let gets = [];
|
|
10
|
-
export {logGets, gets}
|
|
11
|
-
|
|
12
|
-
let withinSet = 0;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Turn the props on obj into JavasCript properties that return Proxies when accessed.
|
|
17
|
-
* If called more than once, return the already-converted object.
|
|
18
|
-
* @param obj {Object}
|
|
19
|
-
* @param props {string}
|
|
20
|
-
* @returns {*|{$proxyHandler}}
|
|
21
|
-
*/
|
|
22
|
-
export function watch(obj, ...props) {
|
|
23
|
-
|
|
24
|
-
if (props.length) {
|
|
25
|
-
let internalProps = {};
|
|
26
|
-
for (let prop of props) {
|
|
27
|
-
internalProps[prop] = obj[prop];
|
|
28
|
-
Object.defineProperty(obj, prop, {
|
|
29
|
-
get() {
|
|
30
|
-
return new Proxy(obj, new ProxyHandler(obj, [], internalProps))[prop];
|
|
31
|
-
},
|
|
32
|
-
set(value) {
|
|
33
|
-
return watch(this)[prop] = value;
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (obj?.$proxyHandler)
|
|
42
|
-
return obj; // It's already a Proxy.
|
|
43
|
-
|
|
44
|
-
// This cache doesn't make things faster.
|
|
45
|
-
// But could it save memory?
|
|
46
|
-
// let result = proxyCache.get(obj);
|
|
47
|
-
// if (!result) {
|
|
48
|
-
// result = new Proxy(obj, new ProxyHandler(obj));
|
|
49
|
-
// proxyCache.set(obj, result);
|
|
50
|
-
// }
|
|
51
|
-
// return result;
|
|
52
|
-
/*#IFDEV*/assert(!obj.$proxyHandler);/*#ENDIF*/
|
|
53
|
-
return new Proxy(obj, new ProxyHandler(obj));
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Provides methods used when a Proxied version of a property is accessed on an object returned by watch() */
|
|
58
|
-
class ProxyHandler {
|
|
59
|
-
|
|
60
|
-
serializedPath;
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* @param root An element managed by a NodeGroupManager. The same as the NodeGroupManager's rootEl.
|
|
64
|
-
* @param path {string[]} Used internally.
|
|
65
|
-
* @param props */
|
|
66
|
-
constructor(root, path=[], props=null) {
|
|
67
|
-
/*#IFDEV*/assert(NodeGroupManager.get(root))/*#ENDIF*/
|
|
68
|
-
this.root = root;
|
|
69
|
-
this.path = path; // path from root to this Proxy.
|
|
70
|
-
this.props = props;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Get the full path to this property from the root watched object.
|
|
75
|
-
* @param atIndex
|
|
76
|
-
* @returns {string} */
|
|
77
|
-
getSerializedPath(atIndex=null) {
|
|
78
|
-
if (!this.serializedPath)
|
|
79
|
-
this.serializedPath = JSON.stringify([getObjectId(this.root), ...this.path.map(item => item + '')])
|
|
80
|
-
|
|
81
|
-
if (atIndex!== null)
|
|
82
|
-
return this.serializedPath.slice(0, -1) + ',"' + atIndex + '"]';
|
|
83
|
-
return this.serializedPath;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Return a ProxyHandler for a property one level deeper at pathItem.
|
|
88
|
-
* @param pathItem {string}
|
|
89
|
-
* @returns {ProxyHandler} */
|
|
90
|
-
extend(pathItem) {
|
|
91
|
-
pathItem += '';
|
|
92
|
-
assert(!this.root.$proxyHandler)
|
|
93
|
-
let result = new ProxyHandler(this.root, [...this.path, pathItem], this.props);
|
|
94
|
-
if (this.serializedPath)
|
|
95
|
-
result.serializedPath = this.getSerializedPath(pathItem);
|
|
96
|
-
|
|
97
|
-
return result;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Called directly by JavaScript when accessing the value of a property.
|
|
102
|
-
* @param obj {Object}
|
|
103
|
-
* @param prop {string} */
|
|
104
|
-
get(obj, prop) {
|
|
105
|
-
|
|
106
|
-
// 1. Special props.
|
|
107
|
-
if (prop === '$proxyHandler')
|
|
108
|
-
return this;
|
|
109
|
-
else if (prop === '$removeProxy')
|
|
110
|
-
return delve(this.props || this.root, this.path);
|
|
111
|
-
|
|
112
|
-
// 2. Array functions.
|
|
113
|
-
else if (prop === 'map' && Array.isArray(obj)) {
|
|
114
|
-
|
|
115
|
-
let ngm = NodeGroupManager.get(this.root);
|
|
116
|
-
|
|
117
|
-
return callback => {
|
|
118
|
-
let loopInfo;
|
|
119
|
-
|
|
120
|
-
let children = [];
|
|
121
|
-
let transformer = obj => {
|
|
122
|
-
let templates = [];
|
|
123
|
-
|
|
124
|
-
for (let i = 0; i < obj.length; i++) {
|
|
125
|
-
|
|
126
|
-
// Watch obj[i].
|
|
127
|
-
let handler = this.extend(i);
|
|
128
|
-
let item = obj[i];
|
|
129
|
-
if (!item.$proxyHandler)
|
|
130
|
-
item = new Proxy(item, handler);
|
|
131
|
-
|
|
132
|
-
let template = callback(item, i, obj);
|
|
133
|
-
templates.push(template);
|
|
134
|
-
|
|
135
|
-
// If the loop is re-evaluted via Set() then we add duplicate TemplateInfo's
|
|
136
|
-
//if (!withinSet) {
|
|
137
|
-
let spath = this.getSerializedPath(i);
|
|
138
|
-
let subscriber = new Subscriber(callback, template);
|
|
139
|
-
subscriber.parent = loopInfo;
|
|
140
|
-
ngm.subscribers.add(spath, subscriber);
|
|
141
|
-
children.push([spath, subscriber]);
|
|
142
|
-
//}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// A parent Template that surrounds all the items in the loop.
|
|
146
|
-
// This lets us get template.nodeGroup.endNode so we can insertBefore().
|
|
147
|
-
return new Template(['', ''], [templates]);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (!withinSet)
|
|
151
|
-
loopInfo = new Subscriber(transformer, null, callback);
|
|
152
|
-
let wholeLoopTemplate = transformer(obj);
|
|
153
|
-
if (!withinSet) {
|
|
154
|
-
loopInfo.template = wholeLoopTemplate;
|
|
155
|
-
loopInfo.children = children;
|
|
156
|
-
ngm.subscribers.add(this.getSerializedPath(), loopInfo);
|
|
157
|
-
}
|
|
158
|
-
return wholeLoopTemplate;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
else if ((prop ==='splice' || prop === 'fastSplice') && Array.isArray(obj)) {
|
|
163
|
-
let ngm = NodeGroupManager.get(this.root);
|
|
164
|
-
return (index, deleteCount, ...items) => {
|
|
165
|
-
let diff = items.length - deleteCount;
|
|
166
|
-
let objLength = obj.length;
|
|
167
|
-
|
|
168
|
-
// Delete
|
|
169
|
-
if (deleteCount) {
|
|
170
|
-
for (let i=index; i<index+deleteCount; i++) {
|
|
171
|
-
|
|
172
|
-
// Update pathToTemplates
|
|
173
|
-
let spath = this.getSerializedPath(i);
|
|
174
|
-
|
|
175
|
-
// Delete nodes of associated NodeGroups.
|
|
176
|
-
for (let subscriber of ngm.subscribers.data[spath] || []) {
|
|
177
|
-
let ng = subscriber.template.nodeGroup;
|
|
178
|
-
for (let node of ng.getNodes())
|
|
179
|
-
node.remove();
|
|
180
|
-
|
|
181
|
-
// Delete NodeGroup from NodeGroupManager.
|
|
182
|
-
ngm.nodeGroupsAvailable.delete(ng.exactKey, ng);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
delete ngm.subscribers.data[spath]; // Deletes templates associated with every loop where this is used.
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// Update indices of subsequent items.
|
|
190
|
-
if (diff) {
|
|
191
|
-
let loopPath = this.getSerializedPath();
|
|
192
|
-
let loopInfo = [...ngm.subscribers.getAll(loopPath)][0]; // TODO: Handle multiple loops.
|
|
193
|
-
|
|
194
|
-
let move = (oldIndex) => {
|
|
195
|
-
let newIndex = oldIndex+diff;
|
|
196
|
-
let oldPath = this.getSerializedPath(oldIndex);
|
|
197
|
-
let newPath = this.getSerializedPath(newIndex);
|
|
198
|
-
|
|
199
|
-
let subscribers = ngm.subscribers.data[oldPath];
|
|
200
|
-
delete ngm.subscribers.data[oldPath]; // TODO: Some can be overwritten w/o being deleted?
|
|
201
|
-
ngm.subscribers.data[newPath] = subscribers;
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
// Update associated NodeGroups by passing them newIndex.
|
|
205
|
-
// This is unnecessary for most loops since they don't use the index.
|
|
206
|
-
// fastSplice skips this path, it skips updating item indices.
|
|
207
|
-
if (prop === 'splice') {
|
|
208
|
-
let array = delve(this.root, this.path)
|
|
209
|
-
for (let subscriber of subscribers) {
|
|
210
|
-
let ng = subscriber.template.nodeGroup;
|
|
211
|
-
|
|
212
|
-
let item = array[oldIndex];
|
|
213
|
-
|
|
214
|
-
//assert(!item.$proxyHandler)
|
|
215
|
-
let proxyItem = getProxy(item, this, this.path, newIndex); //new Proxy(item, this.extend(newIndex));
|
|
216
|
-
let exprs = loopInfo.itemTransformer(proxyItem, newIndex).exprs; // TODO: Pass updated array as third argument to transformer.
|
|
217
|
-
ng.applyExprs(exprs); // this is the slow part.
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Iterate in different directions depending on whether diff is positive or negative.
|
|
223
|
-
if (diff > 0) // Moving items to the right, so we iterate backward from the end.
|
|
224
|
-
for (let i = objLength-1; i >= index + items.length + deleteCount; i--)
|
|
225
|
-
move(i);
|
|
226
|
-
|
|
227
|
-
else // Moving items to the left, so we iterate forward.
|
|
228
|
-
for (let i = index + items.length + deleteCount; i < objLength; i++)
|
|
229
|
-
move(i);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
// Add new items
|
|
234
|
-
if (items.length) {
|
|
235
|
-
let loopPath = this.getSerializedPath();
|
|
236
|
-
|
|
237
|
-
let beforePath = this.getSerializedPath(index);
|
|
238
|
-
let ngm = NodeGroupManager.get(this.root);
|
|
239
|
-
|
|
240
|
-
for (let loopInfo of ngm.subscribers.getAll(loopPath)) {
|
|
241
|
-
let beforeNodes = index < objLength - deleteCount
|
|
242
|
-
? [...ngm.subscribers.getAll(beforePath)].map(t => t.template.nodeGroup.startNode)
|
|
243
|
-
: [loopInfo.template.nodeGroup.endNode]
|
|
244
|
-
for (let beforeNode of beforeNodes) { // TODO: Need to match the beforeNg with the loopInfo instead of iterating.
|
|
245
|
-
for (let i = 0; i < items.length; i++) {
|
|
246
|
-
|
|
247
|
-
// Create NodeGroup of new item.
|
|
248
|
-
let itemHandler = this.extend(index + i);
|
|
249
|
-
assert(!items[i].$proxyHandler)
|
|
250
|
-
let proxyItem = new Proxy(items[i], itemHandler);
|
|
251
|
-
let template = loopInfo.itemTransformer(proxyItem);
|
|
252
|
-
let ng = ngm.getNodeGroup(template, null, true);
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
for (let node of ng.getNodes()) {
|
|
256
|
-
beforeNode.parentNode.insertBefore(node, beforeNode);
|
|
257
|
-
//loopInfo.template.nodeGroup.endNode = node; // The loop's end node is actually an empty text node, so don't do this.
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
// Add new items to ngm.templateInfo
|
|
262
|
-
let spath = itemHandler.getSerializedPath();
|
|
263
|
-
let subscriber = new Subscriber(loopInfo.itemTransformer, template);
|
|
264
|
-
subscriber.parent = loopInfo;
|
|
265
|
-
ngm.subscribers.add(spath, subscriber);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
loopInfo.template.nodeGroup.parentPath.clearNodesCache();
|
|
269
|
-
loopInfo.template.nodeGroup.nodesCache = null;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
let result = obj.splice(index, deleteCount, ...items);
|
|
274
|
-
|
|
275
|
-
//this.notify(this.path);
|
|
276
|
-
|
|
277
|
-
return result;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Allow these functions to use proxied objects as arguments.
|
|
282
|
-
else if (prop ==='indexOf' && Array.isArray(obj)) {
|
|
283
|
-
return item => {
|
|
284
|
-
return obj.indexOf(item.$removeProxy || item)
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// 3. Get property
|
|
289
|
-
else {
|
|
290
|
-
|
|
291
|
-
let obj2 = obj === this.root && this.props ? this.props : obj;
|
|
292
|
-
let result = Reflect.get(obj2, prop);
|
|
293
|
-
|
|
294
|
-
// This is read by watchFunction() which is called in ExprPath.apply().
|
|
295
|
-
// It's used to see what variables contribute to an expression.
|
|
296
|
-
if (logGets)
|
|
297
|
-
gets.push([this.root, ...this.path, prop]);
|
|
298
|
-
|
|
299
|
-
// If we're getting an object or array property, apply watch() to it recursively.
|
|
300
|
-
if (result && typeof result === 'object') {
|
|
301
|
-
let handler = this.extend(prop); // same root, one level deeper on the path.
|
|
302
|
-
/*#IFDEV*/assert(!result.$proxyHandler)/*#ENDIF*/
|
|
303
|
-
return new Proxy(result, handler);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
return result;
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
* Called directly by JavaScript when setting the value of a property via equals.
|
|
313
|
-
* @param obj
|
|
314
|
-
* @param prop
|
|
315
|
-
* @param value
|
|
316
|
-
* @returns {boolean} */
|
|
317
|
-
set(obj, prop, value) {
|
|
318
|
-
withinSet++;
|
|
319
|
-
|
|
320
|
-
let obj2 = obj === this.root && this.props ? this.props : obj;
|
|
321
|
-
let result = Reflect.set(obj2, prop, value);
|
|
322
|
-
let fullPath = [...this.path, prop+''];
|
|
323
|
-
this.notify(fullPath);
|
|
324
|
-
|
|
325
|
-
withinSet --;
|
|
326
|
-
|
|
327
|
-
return result;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* Find every subscriber for fullPath, and above, and call applyExprs() for it.
|
|
332
|
-
* @param fullPath {string[]}
|
|
333
|
-
* @param excluded {Set} */
|
|
334
|
-
notify(fullPath, excluded = new Set()) {
|
|
335
|
-
|
|
336
|
-
// Traverse upward through the path, looking for pathToTemplates.
|
|
337
|
-
let ngm = NodeGroupManager.get(this.root);
|
|
338
|
-
|
|
339
|
-
let rootHash = getObjectId(this.root);
|
|
340
|
-
|
|
341
|
-
let len = fullPath.length;
|
|
342
|
-
while (len >= 1) {
|
|
343
|
-
let path = fullPath.slice(0, len);
|
|
344
|
-
let val = delve(this.root, path);
|
|
345
|
-
let serializedPath = JSON.stringify([rootHash, ...path]);
|
|
346
|
-
for (let subscriber of ngm.subscribers.getAll(serializedPath)) {
|
|
347
|
-
// We already applied expressions for a single item within this loop.
|
|
348
|
-
if (excluded.has(subscriber))
|
|
349
|
-
continue;
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
// Delete child subscriptions so we don't have duplicate subscriptions when we call applyExprs() directly below.
|
|
353
|
-
if (subscriber.children) {
|
|
354
|
-
for (let [spath, childInfo] of subscriber.children)
|
|
355
|
-
ngm.subscribers.delete(spath, childInfo);
|
|
356
|
-
subscriber.children = undefined;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
let proxyVal = getProxy(val, this, path);
|
|
360
|
-
let exprs = subscriber.transformer(proxyVal).exprs; // TODO: Pass updated array as third argument to transformer.
|
|
361
|
-
for (let path of subscriber.template.nodeGroup.paths)
|
|
362
|
-
path.clearNodesCache();
|
|
363
|
-
|
|
364
|
-
// Apply expressions.
|
|
365
|
-
subscriber.template.nodeGroup.applyExprs(exprs);
|
|
366
|
-
|
|
367
|
-
// Don't also process parent loop after updating a single item within it.
|
|
368
|
-
if (subscriber.parent)
|
|
369
|
-
excluded.add(subscriber.parent);
|
|
370
|
-
}
|
|
371
|
-
len--;
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
function getProxy(obj, ph, path, path2) {
|
|
377
|
-
if (!obj || !typeof obj !== 'object')
|
|
378
|
-
return obj;
|
|
379
|
-
|
|
380
|
-
if (obj.$proxyHandler) {
|
|
381
|
-
//#IFDEV
|
|
382
|
-
if (path2)
|
|
383
|
-
path = [...path, path2+''];
|
|
384
|
-
//#ENDIF
|
|
385
|
-
assert(obj.$proxyHandler.root === ph.root && JSON.stringify(obj.$proxyHandler.path) === JSON.stringify(path));
|
|
386
|
-
return obj;
|
|
387
|
-
}
|
|
388
|
-
if (path2)
|
|
389
|
-
path = [...path, path2+''];
|
|
390
|
-
return new Proxy(obj, new ProxyHandler(ph.root, path, ph.props));
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* Call a function and record which watched variables it accesess, storing their paths in pathToTemplates.
|
|
395
|
-
* Used by ExprPath.apply().
|
|
396
|
-
* TODO: only allow this to be called once per callback.
|
|
397
|
-
* @param callback {function}
|
|
398
|
-
* @param ngm {NodeGroupManager}
|
|
399
|
-
* @returns {Template} */
|
|
400
|
-
export function watchFunction(callback, ngm) {
|
|
401
|
-
ngm.clearSubscribersIfNeeded();
|
|
402
|
-
|
|
403
|
-
logGets = true;
|
|
404
|
-
|
|
405
|
-
let transformer = () => new Template(['', ''], [callback()])
|
|
406
|
-
let template = transformer();
|
|
407
|
-
for (let path of gets) {
|
|
408
|
-
let subscriber = new Subscriber(transformer, template);
|
|
409
|
-
ngm.subscribers.add(serializePath(path), subscriber);
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
gets = [];
|
|
413
|
-
logGets = false;
|
|
414
|
-
return template;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
/**
|
|
418
|
-
* Represents a place where nodes will be updated.
|
|
419
|
-
* TODO: Merge this with Template, or ExprPath? */
|
|
420
|
-
class Subscriber {
|
|
421
|
-
|
|
422
|
-
/** @type {Subscriber} Used only for children of a loop. */
|
|
423
|
-
parent;
|
|
424
|
-
|
|
425
|
-
/** @type {Subscriber[]} TemplateInfo for each child of a loop. */
|
|
426
|
-
children;
|
|
427
|
-
|
|
428
|
-
/**
|
|
429
|
-
* @param transformer {function} Function that turns the object at the path into a template.
|
|
430
|
-
* @param template {Template}
|
|
431
|
-
* @param itemTransformer {function} If a loop, this transforms each item in the loop. */
|
|
432
|
-
constructor(transformer, template, itemTransformer=null) {
|
|
433
|
-
this.transformer = transformer;
|
|
434
|
-
this.template = template;
|
|
435
|
-
|
|
436
|
-
// Used only for loops
|
|
437
|
-
this.itemTransformer = itemTransformer;
|
|
438
|
-
}
|
|
439
|
-
}
|