@cocreate/render 1.51.0 → 2.0.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/.github/FUNDING.yml +1 -1
- package/.github/workflows/automated.yml +18 -7
- package/CHANGELOG.md +691 -652
- package/CONTRIBUTING.md +6 -6
- package/README.md +48 -18
- package/demo/fetch.string.html +2 -2
- package/demo/index.html +1 -1
- package/demo/render.array.html +1 -1
- package/demo/render.html +1 -1
- package/demo/render.object.html +1 -1
- package/demo/renderKey.html +1 -1
- package/docs/index.html +7 -7
- package/package.json +20 -26
- package/src/client/index.css +2 -0
- package/src/{index.js → client/index.js} +597 -321
- package/src/client/package.json +3 -0
- package/src/server/index.js +801 -0
- package/src/server/package.json +3 -0
- package/test/client/render-order.test.cjs +90 -0
- package/test/client/render-value-ast.test.cjs +58 -0
- package/test/server/deferred-file.test.js +14 -0
- package/test/server/file-access.test.js +9 -0
- package/test/server/package.json +3 -0
- package/test/server/stylesheet-links.test.js +103 -0
- package/test/server/theme.test.js +21 -0
- package/test/server/value-transformations.test.js +87 -0
- package/webpack.config.js +1 -1
- package/src/index.css +0 -1
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
// Commercial Licensing Information:
|
|
19
19
|
// For commercial use of this software without the copyleft provisions of the AGPLv3,
|
|
20
20
|
// you must obtain a commercial license from CoCreate LLC.
|
|
21
|
-
// For details, visit <https://
|
|
21
|
+
// For details, visit <https://CoCreateJS.com/licenses/> or contact us at sales@CoCreateJS.com.
|
|
22
22
|
|
|
23
23
|
/*globals CustomEvent*/
|
|
24
24
|
import Actions from "@cocreate/actions";
|
|
@@ -28,15 +28,67 @@ import {
|
|
|
28
28
|
getValueFromObject,
|
|
29
29
|
dotNotationToObject,
|
|
30
30
|
ObjectId,
|
|
31
|
-
|
|
31
|
+
renderValue as renderValueUtility,
|
|
32
32
|
uuid
|
|
33
33
|
} from "@cocreate/utils";
|
|
34
34
|
import "@cocreate/element-prototype";
|
|
35
35
|
import "./index.css";
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
/**
|
|
38
|
+
*
|
|
39
|
+
* @public
|
|
40
|
+
* @environment client
|
|
41
|
+
*/
|
|
42
|
+
const sources = new Map();
|
|
43
|
+
/**
|
|
44
|
+
*
|
|
45
|
+
* @public
|
|
46
|
+
* @environment client
|
|
47
|
+
*/
|
|
48
|
+
const renderedNodes = new Map();
|
|
49
|
+
const elementSelector = "[render-query]";
|
|
50
|
+
|
|
51
|
+
function getRenderContext(renderedNode, key) {
|
|
52
|
+
const keys = renderedNode?.keys;
|
|
53
|
+
if (!keys || Array.isArray(keys) || typeof keys !== "object") return;
|
|
54
|
+
|
|
55
|
+
const contextKey = key || Object.keys(keys).at(-1);
|
|
56
|
+
if (!contextKey || !keys[contextKey]) return;
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
key: contextKey,
|
|
60
|
+
keyPath: keys[contextKey].path,
|
|
61
|
+
renderData: keys[contextKey].data,
|
|
62
|
+
renderAs: keys[contextKey].renderAs
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function setRenderContext(renderedNode, context = {}) {
|
|
67
|
+
if (!renderedNode.keys || Array.isArray(renderedNode.keys))
|
|
68
|
+
renderedNode.keys = {};
|
|
69
|
+
|
|
70
|
+
const contextKey =
|
|
71
|
+
context.key || Object.keys(renderedNode.keys).at(-1) || "$default";
|
|
72
|
+
const renderContext = renderedNode.keys[contextKey] || {};
|
|
73
|
+
if (context.keyPath !== undefined)
|
|
74
|
+
renderContext.path = context.keyPath;
|
|
75
|
+
if (context.renderData !== undefined)
|
|
76
|
+
renderContext.data = context.renderData;
|
|
77
|
+
if (context.renderAs !== undefined)
|
|
78
|
+
renderContext.renderAs = context.renderAs;
|
|
79
|
+
|
|
80
|
+
// Reinsert the context so the last key always represents the active
|
|
81
|
+
// render scope without requiring top-level key metadata.
|
|
82
|
+
delete renderedNode.keys[contextKey];
|
|
83
|
+
renderedNode.keys[contextKey] = renderContext;
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
key: contextKey,
|
|
87
|
+
keyPath: renderContext.path,
|
|
88
|
+
renderData: renderContext.data,
|
|
89
|
+
renderAs: renderContext.renderAs
|
|
90
|
+
};
|
|
91
|
+
}
|
|
40
92
|
|
|
41
93
|
/**
|
|
42
94
|
* Initializes elements based on specified rendering attributes. If a specific element is provided, it initializes that element.
|
|
@@ -49,6 +101,8 @@ const elementSelector = "[render-query]";
|
|
|
49
101
|
* - If an HTMLCollection or an array of elements is provided, each element in the collection/array is initialized.
|
|
50
102
|
* - If a single element is provided, only that element is initialized.
|
|
51
103
|
* - If null or omitted, the function queries and initializes all elements matching the 'elementSelector'.
|
|
104
|
+
* @private
|
|
105
|
+
* @environment client
|
|
52
106
|
*/
|
|
53
107
|
function init(element) {
|
|
54
108
|
if (
|
|
@@ -82,7 +136,7 @@ function init(element) {
|
|
|
82
136
|
} catch (error) {
|
|
83
137
|
console.error("Failed to parse render-data:", error);
|
|
84
138
|
}
|
|
85
|
-
}
|
|
139
|
+
}
|
|
86
140
|
}
|
|
87
141
|
}
|
|
88
142
|
|
|
@@ -100,6 +154,8 @@ function init(element) {
|
|
|
100
154
|
* @param {number} [options.currentIndex] - The current index in a list or array rendering process.
|
|
101
155
|
* @param {boolean} [options.update] - Flag to indicate if the rendering should update existing content.
|
|
102
156
|
* @param {boolean} [options.remove] - Flag to indicate if the rendering should remove elements instead of updating or appending.
|
|
157
|
+
* @public
|
|
158
|
+
* @environment client
|
|
103
159
|
*/
|
|
104
160
|
async function render({
|
|
105
161
|
source,
|
|
@@ -163,13 +219,19 @@ async function render({
|
|
|
163
219
|
}
|
|
164
220
|
}
|
|
165
221
|
|
|
166
|
-
if (data
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
222
|
+
if (data.filter) {
|
|
223
|
+
update ??= data.filter.update;
|
|
224
|
+
remove ??= data.filter.remove;
|
|
225
|
+
|
|
226
|
+
const isMutation = remove || update || data.filter.create;
|
|
227
|
+
if (index == null) {
|
|
228
|
+
index = isMutation
|
|
229
|
+
? data.filter.index
|
|
230
|
+
: data.filter.startingIndex ?? data.filter.index;
|
|
231
|
+
}
|
|
170
232
|
}
|
|
171
233
|
|
|
172
|
-
let isMutation = remove || update || (data
|
|
234
|
+
let isMutation = remove || update || (data.filter && data.filter.create);
|
|
173
235
|
|
|
174
236
|
let sourceData = sources.get(source);
|
|
175
237
|
if (!sourceData) {
|
|
@@ -190,6 +252,8 @@ async function render({
|
|
|
190
252
|
element = [element];
|
|
191
253
|
}
|
|
192
254
|
|
|
255
|
+
const renderedNodesToClear = new Set();
|
|
256
|
+
|
|
193
257
|
for (let i = 0; i < element.length; i++) {
|
|
194
258
|
let renderedNode = renderedNodes.get(element[i]);
|
|
195
259
|
if (renderedNode) {
|
|
@@ -211,6 +275,7 @@ async function render({
|
|
|
211
275
|
};
|
|
212
276
|
renderedNodes.set(element[i], renderedNode);
|
|
213
277
|
}
|
|
278
|
+
renderedNodesToClear.add(renderedNode);
|
|
214
279
|
|
|
215
280
|
let clones = renderedNode.clones; // || renderedNode.template.clones..
|
|
216
281
|
if (!clones && renderedNode.template) {
|
|
@@ -232,12 +297,15 @@ async function render({
|
|
|
232
297
|
|
|
233
298
|
let clone;
|
|
234
299
|
if (remove) {
|
|
235
|
-
|
|
300
|
+
const removedItems = data[key];
|
|
301
|
+
if (!Array.isArray(removedItems) || !clones) continue;
|
|
302
|
+
|
|
303
|
+
for (let j = 0; j < removedItems.length; j++) {
|
|
236
304
|
let cloneKey;
|
|
237
305
|
if (key === "object") {
|
|
238
|
-
cloneKey =
|
|
306
|
+
cloneKey = removedItems[j]._id;
|
|
239
307
|
} else {
|
|
240
|
-
cloneKey =
|
|
308
|
+
cloneKey = removedItems[j].name;
|
|
241
309
|
}
|
|
242
310
|
|
|
243
311
|
clone = clones.get(cloneKey);
|
|
@@ -251,7 +319,7 @@ async function render({
|
|
|
251
319
|
(key && (Array.isArray(data[key]) || renderType)) ||
|
|
252
320
|
Array.isArray(data)
|
|
253
321
|
) {
|
|
254
|
-
if (update) {
|
|
322
|
+
if (update && renderType !== "object") {
|
|
255
323
|
for (let j = 0; j < data[key].length; j++) {
|
|
256
324
|
if (key === "object") {
|
|
257
325
|
clone = clones.get(data[key][j]._id);
|
|
@@ -259,7 +327,8 @@ async function render({
|
|
|
259
327
|
clone = clones.get(data[key][j].name);
|
|
260
328
|
}
|
|
261
329
|
|
|
262
|
-
if (
|
|
330
|
+
if (currentIndex == null)
|
|
331
|
+
currentIndex = data.filter.currentIndex;
|
|
263
332
|
|
|
264
333
|
if (!clone) continue;
|
|
265
334
|
|
|
@@ -270,7 +339,7 @@ async function render({
|
|
|
270
339
|
} else {
|
|
271
340
|
// TODO: if $auto here every subsequent clone will have same value, not replacing here will apply unique value to each clone
|
|
272
341
|
if (key === "$auto")
|
|
273
|
-
key = key.replace(/\$auto/g,
|
|
342
|
+
key = key.replace(/\$auto/g, uuid(6));
|
|
274
343
|
|
|
275
344
|
if (key) element[i].setAttribute("render", key);
|
|
276
345
|
|
|
@@ -281,6 +350,10 @@ async function render({
|
|
|
281
350
|
await renderValues(element[i], data, key, renderAs);
|
|
282
351
|
}
|
|
283
352
|
}
|
|
353
|
+
for (const renderedNode of renderedNodesToClear) {
|
|
354
|
+
clearRendering(renderedNode);
|
|
355
|
+
}
|
|
356
|
+
|
|
284
357
|
// Dispatch input and change events after rendering is complete
|
|
285
358
|
if (source instanceof HTMLElement) {
|
|
286
359
|
source.dispatchEvent(new Event("input"));
|
|
@@ -289,15 +362,25 @@ async function render({
|
|
|
289
362
|
}
|
|
290
363
|
|
|
291
364
|
async function renderTemplate(template, data, key, index, keyPath) {
|
|
292
|
-
if (
|
|
293
|
-
|
|
365
|
+
if (template && template.nodeType !== 1) {
|
|
366
|
+
template = template.ownerElement || template.parentElement;
|
|
294
367
|
}
|
|
295
|
-
|
|
296
|
-
if (!
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
368
|
+
if (!template) return;
|
|
369
|
+
if (template instanceof HTMLTemplateElement && !template.content.firstElementChild) return;
|
|
370
|
+
let templateData = renderedNodes.get(template);
|
|
371
|
+
if (!templateData) {
|
|
372
|
+
templateData = {
|
|
373
|
+
element: template,
|
|
374
|
+
clones: new Map(),
|
|
375
|
+
isTemplate: true
|
|
376
|
+
};
|
|
377
|
+
renderedNodes.set(template, templateData);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
templateData.element = template;
|
|
381
|
+
if (!templateData.clones) templateData.clones = new Map();
|
|
382
|
+
templateData.isTemplate = true;
|
|
383
|
+
|
|
301
384
|
if (!templateData.parent && !templateData.source) {
|
|
302
385
|
let parentRender = template.parentElement.closest("[render]")
|
|
303
386
|
if (parentRender || template.source)
|
|
@@ -306,65 +389,60 @@ async function renderTemplate(template, data, key, index, keyPath) {
|
|
|
306
389
|
console.log("template parent missing is there no parent or is this an error? and source is", templateData.source)
|
|
307
390
|
}
|
|
308
391
|
|
|
309
|
-
if(!templateData.source && templateData.parent && templateData.parent.source){
|
|
392
|
+
if (!templateData.source && templateData.parent && templateData.parent.source) {
|
|
310
393
|
templateData.source = templateData.parent.source
|
|
311
394
|
}
|
|
312
395
|
|
|
313
|
-
|
|
314
|
-
if (keyPath) {
|
|
315
|
-
templateData.keyPath = keyPath;
|
|
316
|
-
} else if (templateData.parent && templateData.parent.keyPath) {
|
|
317
|
-
templateData.keyPath = templateData.parent.keyPath;
|
|
318
|
-
} else if (key) {
|
|
319
|
-
templateData.keyPath = key;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
let renderData;
|
|
396
|
+
let renderData;
|
|
324
397
|
if (key) {
|
|
325
398
|
renderData = getRenderValue(templateData.element, data, key);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
if (!renderData) {
|
|
329
|
-
renderData = data;
|
|
330
399
|
}
|
|
331
400
|
|
|
332
|
-
|
|
333
|
-
|
|
401
|
+
if (renderData === undefined || renderData === null) {
|
|
402
|
+
renderData = key ? [] : data;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
let el = templateData.element;
|
|
334
406
|
if (el.nodeType !== 1) {
|
|
335
|
-
|
|
407
|
+
el = templateData.parent.element;
|
|
336
408
|
}
|
|
337
409
|
|
|
338
|
-
let renderAs =
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
410
|
+
let renderAs =
|
|
411
|
+
el.getAttribute("render-as") ||
|
|
412
|
+
el.getAttribute("render") ||
|
|
413
|
+
getRenderContext(templateData, key)?.renderAs ||
|
|
414
|
+
key;
|
|
415
|
+
const parentContext = getRenderContext(templateData.parent);
|
|
416
|
+
const resolvedKeyPath =
|
|
417
|
+
keyPath || key || parentContext?.keyPath;
|
|
418
|
+
const templateContext = setRenderContext(templateData, {
|
|
419
|
+
key,
|
|
420
|
+
keyPath: resolvedKeyPath,
|
|
421
|
+
renderData,
|
|
422
|
+
renderAs
|
|
423
|
+
});
|
|
346
424
|
|
|
347
425
|
let renderType;
|
|
348
426
|
if (templateData.source && templateData.source.element)
|
|
349
427
|
renderType = templateData.source.element.getAttribute("render-type");
|
|
350
428
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
templateData.source.element.getAttribute("render-reverse");
|
|
355
|
-
if (renderReverse === null)
|
|
356
|
-
renderReverse = el.getAttribute("render-reverse");
|
|
357
|
-
}
|
|
429
|
+
const renderReverse =
|
|
430
|
+
templateData.source?.element?.getAttribute("render-reverse") ??
|
|
431
|
+
el.getAttribute("render-reverse");
|
|
358
432
|
|
|
359
433
|
let exclude = el.getAttribute("render-exclude") || "";
|
|
360
434
|
if (exclude) {
|
|
361
435
|
exclude = exclude.replace(/ /g, "").split(",");
|
|
362
436
|
}
|
|
437
|
+
const include = el.getAttribute("render-include");
|
|
438
|
+
const includePattern = include ? new RegExp(include) : null;
|
|
363
439
|
let reference = el.getAttribute("render-reference");
|
|
364
440
|
|
|
365
|
-
|
|
441
|
+
const isLoadMore = data.filter?.loadMore && !data.filter?.overwrite;
|
|
442
|
+
const appendIndex = templateData.clones.size;
|
|
443
|
+
let isInsert = data.filter && (data.filter.create || data.filter.update);
|
|
366
444
|
if (renderType !== "object") {
|
|
367
|
-
if ((!isInsert && !index) || data
|
|
445
|
+
if ((!isInsert && !isLoadMore && !index) || data.filter?.overwrite) {
|
|
368
446
|
if (!templateData.clones) {
|
|
369
447
|
return
|
|
370
448
|
if (templateData.template) {
|
|
@@ -379,7 +457,7 @@ async function renderTemplate(template, data, key, index, keyPath) {
|
|
|
379
457
|
element.remove();
|
|
380
458
|
templateData.clones.delete(key);
|
|
381
459
|
}
|
|
382
|
-
}
|
|
460
|
+
}
|
|
383
461
|
}
|
|
384
462
|
|
|
385
463
|
if ((key && !Array.isArray(renderData)) || renderType === "object") {
|
|
@@ -387,19 +465,35 @@ async function renderTemplate(template, data, key, index, keyPath) {
|
|
|
387
465
|
renderData = renderData[0];
|
|
388
466
|
|
|
389
467
|
const keys = Object.keys(renderData);
|
|
468
|
+
if (renderType === "object" && data.method?.endsWith(".read")) {
|
|
469
|
+
const present = new Set(keys);
|
|
470
|
+
for (const [id, element] of templateData.clones) {
|
|
471
|
+
if (present.has(element.getAttribute("renderedKey"))) continue;
|
|
472
|
+
renderedNodes.delete(element);
|
|
473
|
+
element.remove();
|
|
474
|
+
templateData.clones.delete(id);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
const fieldClones = new Map(
|
|
478
|
+
Array.from(templateData.clones.values(), (element) => [
|
|
479
|
+
element.getAttribute("renderedKey"),
|
|
480
|
+
element
|
|
481
|
+
])
|
|
482
|
+
);
|
|
390
483
|
for (let i = 0; i < keys.length; i++) {
|
|
391
|
-
let clone =
|
|
484
|
+
let clone = fieldClones.get(keys[i]);
|
|
392
485
|
clone = renderedNodes.get(clone);
|
|
393
486
|
|
|
394
487
|
if (
|
|
395
488
|
exclude.includes(keys[i]) ||
|
|
489
|
+
(includePattern && !includePattern.test(keys[i])) ||
|
|
396
490
|
(reference === "false" &&
|
|
397
491
|
["$storage", "$database", "$array"].includes(key))
|
|
398
492
|
) {
|
|
399
493
|
if (clone) {
|
|
400
494
|
renderedNodes.delete(clone.element);
|
|
401
495
|
clone.element.remove();
|
|
402
|
-
templateData.clones.delete(
|
|
496
|
+
templateData.clones.delete(clone.element.getAttribute("eid"));
|
|
403
497
|
}
|
|
404
498
|
continue;
|
|
405
499
|
}
|
|
@@ -410,7 +504,11 @@ async function renderTemplate(template, data, key, index, keyPath) {
|
|
|
410
504
|
if (Array.isArray(value)) type = "array";
|
|
411
505
|
else if (typeof value == "object") type = "object";
|
|
412
506
|
|
|
413
|
-
|
|
507
|
+
// Nested object templates still need fields from the source record.
|
|
508
|
+
let Data = {
|
|
509
|
+
...data,
|
|
510
|
+
[renderAs]: { key: keys[i], value, type }
|
|
511
|
+
};
|
|
414
512
|
|
|
415
513
|
if (!clone) {
|
|
416
514
|
clone = cloneTemplate(templateData);
|
|
@@ -424,13 +522,19 @@ async function renderTemplate(template, data, key, index, keyPath) {
|
|
|
424
522
|
let renderedKey = key.split(".");
|
|
425
523
|
renderedKey = renderedKey[renderedKey.length - 1];
|
|
426
524
|
|
|
427
|
-
|
|
428
|
-
clone.
|
|
429
|
-
clone.
|
|
430
|
-
clone
|
|
525
|
+
const cloneKeyPath = `${templateContext.keyPath}.${renderedKey}`;
|
|
526
|
+
clone.parentKey = renderedKey;
|
|
527
|
+
clone.renderKey = key;
|
|
528
|
+
setRenderContext(clone, {
|
|
529
|
+
key: keys[i],
|
|
530
|
+
keyPath: cloneKeyPath,
|
|
531
|
+
renderData: Data,
|
|
532
|
+
renderAs
|
|
533
|
+
});
|
|
431
534
|
clone.element.setAttribute("renderedKey", keys[i]);
|
|
432
535
|
await renderValues(clone.element, Data, keys[i], renderAs);
|
|
433
536
|
insertElement(templateData, clone.element, index);
|
|
537
|
+
fieldClones.set(keys[i], clone.element);
|
|
434
538
|
}
|
|
435
539
|
} else {
|
|
436
540
|
if (!key && !Array.isArray(renderData)) {
|
|
@@ -441,7 +545,12 @@ async function renderTemplate(template, data, key, index, keyPath) {
|
|
|
441
545
|
|
|
442
546
|
if (!renderData) {
|
|
443
547
|
let clone = cloneTemplate(templateData);
|
|
444
|
-
clone
|
|
548
|
+
setRenderContext(clone, {
|
|
549
|
+
key,
|
|
550
|
+
keyPath: templateContext.keyPath,
|
|
551
|
+
renderData: data,
|
|
552
|
+
renderAs
|
|
553
|
+
});
|
|
445
554
|
await renderValues(clone.element, data, key, renderAs);
|
|
446
555
|
insertElement(templateData, clone.element, index);
|
|
447
556
|
} else {
|
|
@@ -453,7 +562,7 @@ async function renderTemplate(template, data, key, index, keyPath) {
|
|
|
453
562
|
let test = clone.element.getAttribute("render-as");
|
|
454
563
|
if (test) renderAs = test;
|
|
455
564
|
|
|
456
|
-
|
|
565
|
+
const cloneKeyPath = `${templateContext.keyPath || ""}[${i}]`;
|
|
457
566
|
|
|
458
567
|
let object;
|
|
459
568
|
if (
|
|
@@ -480,11 +589,22 @@ async function renderTemplate(template, data, key, index, keyPath) {
|
|
|
480
589
|
object = dotNotationToObject(object);
|
|
481
590
|
} else object = object;
|
|
482
591
|
|
|
483
|
-
let
|
|
484
|
-
if (key !== "data")
|
|
592
|
+
let insertIndex;
|
|
593
|
+
if (key !== "data") {
|
|
594
|
+
insertIndex = isLoadMore
|
|
595
|
+
? appendIndex + i
|
|
596
|
+
: data.filter?.create && index != null
|
|
597
|
+
? index + i
|
|
598
|
+
: i;
|
|
599
|
+
}
|
|
485
600
|
|
|
486
|
-
clone
|
|
487
|
-
|
|
601
|
+
setRenderContext(clone, {
|
|
602
|
+
key,
|
|
603
|
+
keyPath: cloneKeyPath,
|
|
604
|
+
renderData: object,
|
|
605
|
+
renderAs
|
|
606
|
+
});
|
|
607
|
+
clone.index = insertIndex
|
|
488
608
|
await renderValues(
|
|
489
609
|
clone.element,
|
|
490
610
|
object,
|
|
@@ -492,12 +612,12 @@ async function renderTemplate(template, data, key, index, keyPath) {
|
|
|
492
612
|
renderAs,
|
|
493
613
|
null,
|
|
494
614
|
null,
|
|
495
|
-
|
|
615
|
+
insertIndex
|
|
496
616
|
);
|
|
497
617
|
insertElement(
|
|
498
618
|
templateData,
|
|
499
619
|
clone.element,
|
|
500
|
-
|
|
620
|
+
insertIndex,
|
|
501
621
|
"",
|
|
502
622
|
data,
|
|
503
623
|
renderReverse
|
|
@@ -507,17 +627,17 @@ async function renderTemplate(template, data, key, index, keyPath) {
|
|
|
507
627
|
}
|
|
508
628
|
}
|
|
509
629
|
|
|
510
|
-
function cloneTemplate(template) {
|
|
630
|
+
function cloneTemplate(template) {
|
|
511
631
|
let clone = template.element.cloneNode(true);
|
|
512
632
|
let cloneTagName = clone.tagName;
|
|
513
633
|
|
|
514
634
|
if (clone.tagName == "TEMPLATE") {
|
|
515
|
-
clone =
|
|
635
|
+
clone = clone.content.firstElementChild;
|
|
516
636
|
for (let attribute of template.element.attributes) {
|
|
517
637
|
clone.setAttribute(attribute.name, attribute.value || "");
|
|
518
638
|
}
|
|
519
639
|
} else {
|
|
520
|
-
clone.classList
|
|
640
|
+
clone.classList?.remove("template");
|
|
521
641
|
clone.removeAttribute("template");
|
|
522
642
|
}
|
|
523
643
|
|
|
@@ -525,7 +645,7 @@ function cloneTemplate(template) {
|
|
|
525
645
|
|
|
526
646
|
let renderAs = clone.getAttribute("render-as");
|
|
527
647
|
if (renderAs === "$auto") {
|
|
528
|
-
renderAs =
|
|
648
|
+
renderAs = uuid(6);
|
|
529
649
|
clone = clone.outerHTML.replace(/\$auto/g, renderAs);
|
|
530
650
|
}
|
|
531
651
|
|
|
@@ -542,10 +662,27 @@ function cloneTemplate(template) {
|
|
|
542
662
|
|
|
543
663
|
let renderedNode = { template, element: clone };
|
|
544
664
|
renderedNodes.set(clone, renderedNode);
|
|
545
|
-
return renderedNode;
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
function
|
|
665
|
+
return renderedNode;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
function getCloneIdentity(data, renderAs) {
|
|
669
|
+
const value = renderAs ? getValueFromObject(data, renderAs) : data;
|
|
670
|
+
if (!value || typeof value !== "object") return;
|
|
671
|
+
|
|
672
|
+
for (const key of ["_id", "id", "object", "name", "key"]) {
|
|
673
|
+
const identity = value[key];
|
|
674
|
+
if (
|
|
675
|
+
(typeof identity === "string" || typeof identity === "number") &&
|
|
676
|
+
identity !== "" &&
|
|
677
|
+
identity !== "{}" &&
|
|
678
|
+
identity !== "[]"
|
|
679
|
+
) {
|
|
680
|
+
return String(identity);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
function insertElement(
|
|
549
686
|
template,
|
|
550
687
|
element,
|
|
551
688
|
index,
|
|
@@ -555,7 +692,7 @@ function insertElement(
|
|
|
555
692
|
) {
|
|
556
693
|
let eid = element.getAttribute("eid");
|
|
557
694
|
if (!eid) {
|
|
558
|
-
eid =
|
|
695
|
+
eid = uuid(6);
|
|
559
696
|
element.setAttribute("eid", eid);
|
|
560
697
|
}
|
|
561
698
|
|
|
@@ -563,9 +700,11 @@ function insertElement(
|
|
|
563
700
|
if (!template.clones) template = template.template;
|
|
564
701
|
|
|
565
702
|
let item;
|
|
703
|
+
let isNewClone = false;
|
|
566
704
|
if (!currentIndex && currentIndex !== 0) {
|
|
567
705
|
if (template.clones.has(eid)) return;
|
|
568
706
|
item = [eid, element];
|
|
707
|
+
isNewClone = true;
|
|
569
708
|
}
|
|
570
709
|
|
|
571
710
|
const clones = Array.from(template.clones);
|
|
@@ -574,11 +713,13 @@ function insertElement(
|
|
|
574
713
|
item = clones.splice(currentIndex, 1)[0];
|
|
575
714
|
}
|
|
576
715
|
|
|
577
|
-
if (data && data
|
|
578
|
-
index += data
|
|
716
|
+
if (data && data.filter && data.filter.startingIndex && !data.filter.loadMore)
|
|
717
|
+
index += data.filter.startingIndex;
|
|
579
718
|
clones.splice(index, 0, item); // Insert item into clones at the specified index
|
|
580
719
|
|
|
581
|
-
if (
|
|
720
|
+
if (isNewClone) trackRendering(template, element);
|
|
721
|
+
|
|
722
|
+
if (renderReverse != null && renderReverse !== false && renderReverse !== "false") {
|
|
582
723
|
// Chat-specific logic: reverse rendering but still inserting based on sorting
|
|
583
724
|
if (clones[index + 1] && clones[index + 1][1] !== element) {
|
|
584
725
|
clones[index + 1][1].insertAdjacentElement("afterend", element);
|
|
@@ -603,59 +744,77 @@ function insertElement(
|
|
|
603
744
|
|
|
604
745
|
template.clones = new Map(clones);
|
|
605
746
|
} else {
|
|
747
|
+
const isNewClone = !template.clones.has(eid);
|
|
748
|
+
if (isNewClone) trackRendering(template, element);
|
|
606
749
|
template.clones.set(eid, element);
|
|
607
750
|
template.element.insertAdjacentElement("beforebegin", element);
|
|
608
751
|
}
|
|
609
752
|
}
|
|
610
753
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
if (!renderedNode.key) renderedNode.key = key;
|
|
618
|
-
else if (!key) key = renderedNode.key;
|
|
619
|
-
|
|
620
|
-
if (!renderedNode.renderAs) renderedNode.renderAs = renderAs;
|
|
621
|
-
else if (!renderAs) renderAs = renderedNode.renderAs;
|
|
622
|
-
|
|
623
|
-
if (keyPath) renderedNode.keyPath = keyPath;
|
|
624
|
-
else if (renderedNode.keyPath) keyPath = renderedNode.keyPath;
|
|
754
|
+
function trackRendering(renderedNode, element) {
|
|
755
|
+
element.classList.add("render-pending");
|
|
756
|
+
if (!renderedNode.rendering) renderedNode.rendering = [];
|
|
757
|
+
renderedNode.rendering.push(element);
|
|
758
|
+
}
|
|
625
759
|
|
|
626
|
-
|
|
760
|
+
function clearRendering(renderedNode) {
|
|
761
|
+
if (!renderedNode?.rendering) return;
|
|
627
762
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
parent = renderedNode;
|
|
763
|
+
const elements = renderedNode.rendering;
|
|
764
|
+
delete renderedNode.rendering;
|
|
631
765
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
766
|
+
setTimeout(() => {
|
|
767
|
+
for (const element of elements) {
|
|
768
|
+
element.classList.remove("render-pending");
|
|
769
|
+
}
|
|
770
|
+
}, 300);
|
|
771
|
+
}
|
|
635
772
|
|
|
636
|
-
|
|
773
|
+
async function renderValues(node, data, key, renderAs, keyPath, parent, index) {
|
|
774
|
+
if (!data) return;
|
|
637
775
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
776
|
+
let renderedNode = renderedNodes.get(node);
|
|
777
|
+
if (!renderedNode) {
|
|
778
|
+
renderedNode = { element: node };
|
|
779
|
+
renderedNodes.set(node, renderedNode);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
const previousContext = getRenderContext(renderedNode, key);
|
|
783
|
+
if (!key) key = previousContext?.key;
|
|
784
|
+
if (!renderAs) renderAs = previousContext?.renderAs;
|
|
785
|
+
if (!keyPath) keyPath = previousContext?.keyPath;
|
|
786
|
+
setRenderContext(renderedNode, {
|
|
787
|
+
key,
|
|
788
|
+
keyPath,
|
|
789
|
+
renderData: data,
|
|
790
|
+
renderAs
|
|
791
|
+
});
|
|
645
792
|
|
|
646
|
-
|
|
793
|
+
if (parent) renderedNode.parent = parent;
|
|
647
794
|
|
|
795
|
+
if (node.nodeType == 1) {
|
|
796
|
+
if (node.hasAttribute("render-clone")) {
|
|
797
|
+
parent = renderedNode;
|
|
798
|
+
|
|
799
|
+
const eid = getCloneIdentity(data, renderAs);
|
|
800
|
+
if (eid) {
|
|
801
|
+
let oldEid = renderedNode.eid;
|
|
802
|
+
let temp = renderedNode.template;
|
|
803
|
+
while (temp && !temp.clones)
|
|
804
|
+
temp = temp.template || temp.parent;
|
|
805
|
+
|
|
806
|
+
if (temp?.clones && oldEid && oldEid !== eid) {
|
|
807
|
+
temp.clones.delete(oldEid);
|
|
648
808
|
temp.clones.set(eid, node);
|
|
649
809
|
}
|
|
650
810
|
|
|
651
811
|
renderedNode.eid = eid;
|
|
812
|
+
|
|
813
|
+
node.setAttribute("eid", eid);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
652
816
|
|
|
653
|
-
|
|
654
|
-
break;
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
for (let attr of node.attributes) {
|
|
817
|
+
for (let attr of Array.from(node.attributes)) {
|
|
659
818
|
let name = attr.name;
|
|
660
819
|
let value = attr.value;
|
|
661
820
|
|
|
@@ -667,6 +826,7 @@ async function renderValues(node, data, key, renderAs, keyPath, parent, index) {
|
|
|
667
826
|
renderAs,
|
|
668
827
|
parent: renderedNode
|
|
669
828
|
};
|
|
829
|
+
renderedNodes.set(attr, renderedAttribute);
|
|
670
830
|
}
|
|
671
831
|
|
|
672
832
|
let namePlaceholder = renderedAttribute.placeholder.name || name;
|
|
@@ -688,15 +848,13 @@ async function renderValues(node, data, key, renderAs, keyPath, parent, index) {
|
|
|
688
848
|
renderedAttribute,
|
|
689
849
|
index
|
|
690
850
|
);
|
|
691
|
-
if (namePlaceholder.includes("{{") && name) {
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
node.setAttribute(attributeName, attributeValue);
|
|
699
|
-
});
|
|
851
|
+
if (namePlaceholder.includes("{{") && name && !name.includes("{{")) {
|
|
852
|
+
if (name !== attr.name) {
|
|
853
|
+
node.removeAttribute(attr.name);
|
|
854
|
+
renderedNodes.delete(attr);
|
|
855
|
+
}
|
|
856
|
+
node.setAttribute(name, value ?? "");
|
|
857
|
+
renderedNodes.set(node.getAttributeNode(name), renderedAttribute);
|
|
700
858
|
} else if (name === undefined || name === null || name === "") {
|
|
701
859
|
renderedNodes.delete(attr);
|
|
702
860
|
node.removeAttribute(attr.name);
|
|
@@ -732,11 +890,23 @@ async function renderValues(node, data, key, renderAs, keyPath, parent, index) {
|
|
|
732
890
|
return;
|
|
733
891
|
}
|
|
734
892
|
}
|
|
893
|
+
|
|
894
|
+
// A nested render-query owns its template and descendants. Parent
|
|
895
|
+
// rendering may resolve this controller's attributes, but its own
|
|
896
|
+
// render cycle must populate its target.
|
|
897
|
+
if (
|
|
898
|
+
node.hasAttribute("render-query") &&
|
|
899
|
+
!node.hasAttribute("render-clone")
|
|
900
|
+
) {
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
|
|
735
904
|
if (node.hasAttribute("render") && !node.hasAttribute("render-clone")) {
|
|
736
905
|
let renderKey = node.getAttribute("render") || key
|
|
737
|
-
renderTemplate(node, data, renderKey);
|
|
738
|
-
} else if (node.childNodes.length > 0) {
|
|
739
|
-
for (let childNode of node.childNodes) {
|
|
906
|
+
await renderTemplate(node, data, renderKey);
|
|
907
|
+
} else if (node.childNodes.length > 0) {
|
|
908
|
+
for (let childNode of Array.from(node.childNodes)) {
|
|
909
|
+
if (childNode.parentNode !== node) continue;
|
|
740
910
|
await renderValues(
|
|
741
911
|
childNode,
|
|
742
912
|
data,
|
|
@@ -748,7 +918,7 @@ async function renderValues(node, data, key, renderAs, keyPath, parent, index) {
|
|
|
748
918
|
);
|
|
749
919
|
}
|
|
750
920
|
}
|
|
751
|
-
} else if (node.nodeType == 3) {
|
|
921
|
+
} else if (node.nodeType == 3 || (node.nodeType == 8 && renderedNode.htmlEnd)) {
|
|
752
922
|
let valueType = node.parentElement.getAttribute("value-type");
|
|
753
923
|
|
|
754
924
|
let textContent, text;
|
|
@@ -772,33 +942,34 @@ async function renderValues(node, data, key, renderAs, keyPath, parent, index) {
|
|
|
772
942
|
if (text != renderedNode.text) {
|
|
773
943
|
renderedNode.text = text;
|
|
774
944
|
|
|
775
|
-
if (
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
945
|
+
if (valueType !== "html" && !renderedNode.htmlEnd) {
|
|
946
|
+
node.textContent = text;
|
|
947
|
+
} else {
|
|
948
|
+
const content = document.createElement("div");
|
|
949
|
+
if (valueType === "html") content.innerHTML = text;
|
|
950
|
+
else content.textContent = text;
|
|
951
|
+
|
|
952
|
+
if (renderedNode.htmlEnd) {
|
|
953
|
+
// Replace this binding's output while retaining its original template.
|
|
954
|
+
let child = node.nextSibling;
|
|
955
|
+
while (child && child !== renderedNode.htmlEnd) {
|
|
956
|
+
const next = child.nextSibling;
|
|
957
|
+
child.remove();
|
|
958
|
+
child = next;
|
|
959
|
+
}
|
|
960
|
+
renderedNode.htmlEnd.before(...content.childNodes);
|
|
961
|
+
} else {
|
|
962
|
+
const start = document.createComment("render");
|
|
963
|
+
const end = document.createComment("/render");
|
|
964
|
+
renderedNodes.set(start, {
|
|
965
|
+
...renderedNode,
|
|
966
|
+
element: start,
|
|
967
|
+
htmlEnd: end
|
|
968
|
+
});
|
|
969
|
+
renderedNodes.delete(node);
|
|
970
|
+
node.replaceWith(start, ...content.childNodes, end);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
802
973
|
|
|
803
974
|
if (node.childNodes.length > 0) {
|
|
804
975
|
for (let childNode of node.childNodes) {
|
|
@@ -820,7 +991,7 @@ async function renderValues(node, data, key, renderAs, keyPath, parent, index) {
|
|
|
820
991
|
|
|
821
992
|
/**
|
|
822
993
|
* Renders placeholders within a string using asynchronous AST calculations and data context binding.
|
|
823
|
-
*
|
|
994
|
+
*
|
|
824
995
|
* @param {Node} node - The target DOM node (attributes, text nodes, elements).
|
|
825
996
|
* @param {Object} data - The contextual database record or dataset being rendered.
|
|
826
997
|
* @param {string} placeholder - The raw template string containing double curly braces (e.g. "{{ $query('#input').value }}").
|
|
@@ -828,127 +999,22 @@ async function renderValues(node, data, key, renderAs, keyPath, parent, index) {
|
|
|
828
999
|
* @param {HTMLElement} renderedNode - Optional target element where rendering applies.
|
|
829
1000
|
* @param {number} index - Optional current iteration index if rendering a list.
|
|
830
1001
|
* @returns {Promise<string>} - A promise resolving to the fully processed output string.
|
|
1002
|
+
* @public
|
|
1003
|
+
* @environment client
|
|
831
1004
|
*/
|
|
832
|
-
async function renderValue(
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
// Determine the actual HTMLElement for astAsync
|
|
843
|
-
// If the node is not an Element (nodeType 1), get its owner or parent element.
|
|
844
|
-
// This safely covers Attributes (ownerElement), Text, Comments, etc. (parentElement)
|
|
845
|
-
let contextElement = node;
|
|
846
|
-
if (node && node.nodeType !== 1) {
|
|
847
|
-
contextElement = node.ownerElement || node.parentElement;
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
// Matches ONLY the innermost {{ }} brackets to prevent greedy-matching nested boundaries.
|
|
851
|
-
let regex = /\{\{\s*((?:(?!\{\{|\}\})[\s\S])+?)\s*\}\}/;
|
|
852
|
-
let omitted = {};
|
|
853
|
-
|
|
854
|
-
let match;
|
|
855
|
-
do {
|
|
856
|
-
match = output.match(regex);
|
|
857
|
-
if (match) {
|
|
858
|
-
let value;
|
|
859
|
-
let placeholderContent = match[1].trim();
|
|
860
|
-
|
|
861
|
-
try {
|
|
862
|
-
if (placeholderContent === "index" && index >= 0) {
|
|
863
|
-
value = index;
|
|
864
|
-
} else {
|
|
865
|
-
// --- UNIFIED PARSING PROCESS ---
|
|
866
|
-
let preProcessed = placeholderContent;
|
|
867
|
-
|
|
868
|
-
// Build unified evaluation sandbox inheriting state & local indices
|
|
869
|
-
const sandboxContext = {
|
|
870
|
-
data: data,
|
|
871
|
-
$data: data,
|
|
872
|
-
index: index,
|
|
873
|
-
$index: index,
|
|
874
|
-
...data // Expose data fields natively so they are available as local sandbox variables
|
|
875
|
-
};
|
|
876
|
-
|
|
877
|
-
// 1. PRE-PROCESSOR FOR QUOTED OPERATORS (Saves and processes quoted formulas first)
|
|
878
|
-
let quoteRegex = /(['"])(.*?)\1/g;
|
|
879
|
-
let quoteMatch;
|
|
880
|
-
let replacements = [];
|
|
881
|
-
while ((quoteMatch = quoteRegex.exec(preProcessed)) !== null) {
|
|
882
|
-
let quoteType = quoteMatch[1];
|
|
883
|
-
let content = quoteMatch[2];
|
|
884
|
-
if (content.trim().startsWith('\u0024')) {
|
|
885
|
-
let resolved = await astAsync({
|
|
886
|
-
executeAST: content,
|
|
887
|
-
node: contextElement,
|
|
888
|
-
sandbox: sandboxContext
|
|
889
|
-
});
|
|
890
|
-
if (resolved === null || resolved === undefined) resolved = "";
|
|
891
|
-
replacements.push({
|
|
892
|
-
original: quoteMatch[0],
|
|
893
|
-
replacement: `${quoteType}${resolved}${quoteType}`
|
|
894
|
-
});
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
for (let r of replacements) {
|
|
899
|
-
preProcessed = preProcessed.replace(r.original, r.replacement);
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
// 2. PRE-PROCESSOR: Resolve unquoted DOM selectors, custom operators, and CRUD JSON formulas
|
|
903
|
-
preProcessed = await astAsync({
|
|
904
|
-
executeAST: preProcessed,
|
|
905
|
-
node: contextElement,
|
|
906
|
-
sandbox: sandboxContext
|
|
907
|
-
});
|
|
908
|
-
|
|
909
|
-
// 3. DATA LOOKUP: Resolve simple keys from the provided data object
|
|
910
|
-
// Keep `node` here because getRenderValue needs to look up the attribute cache!
|
|
911
|
-
value = getRenderValue(
|
|
912
|
-
node,
|
|
913
|
-
data,
|
|
914
|
-
preProcessed,
|
|
915
|
-
renderAs
|
|
916
|
-
);
|
|
917
|
-
}
|
|
918
|
-
} catch (error) {
|
|
919
|
-
console.error("[Renderer AST Bridge] Value computation error:", error);
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
// Replace the placeholder with the resolved value
|
|
923
|
-
if (value !== undefined && value !== null) {
|
|
924
|
-
if (typeof value === "object") {
|
|
925
|
-
value = JSON.stringify(value, null, 2);
|
|
926
|
-
}
|
|
927
|
-
output = output.replaceAll(match[0], value);
|
|
928
|
-
} else if (renderAs) {
|
|
929
|
-
if (match[0].startsWith(`{{${renderAs}.`)) {
|
|
930
|
-
output = output.replaceAll(match[0], "");
|
|
931
|
-
} else if (placeholderContent) {
|
|
932
|
-
output = output.replaceAll(
|
|
933
|
-
match[0],
|
|
934
|
-
`<|${placeholderContent}|>`
|
|
935
|
-
);
|
|
936
|
-
omitted[`<|${placeholderContent}|>`] = match[0];
|
|
937
|
-
}
|
|
938
|
-
} else {
|
|
939
|
-
match = null;
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
} while (match);
|
|
943
|
-
|
|
944
|
-
for (let key of Object.keys(omitted)) {
|
|
945
|
-
output = output.replaceAll(key, omitted[key]);
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
return output;
|
|
1005
|
+
async function renderValue(node, data, placeholder, renderAs, renderedNode, index) {
|
|
1006
|
+
return renderValueUtility(
|
|
1007
|
+
node,
|
|
1008
|
+
data,
|
|
1009
|
+
placeholder,
|
|
1010
|
+
renderAs,
|
|
1011
|
+
renderedNode,
|
|
1012
|
+
index,
|
|
1013
|
+
getRenderValue
|
|
1014
|
+
);
|
|
949
1015
|
}
|
|
950
|
-
|
|
951
|
-
function getRenderValue(node, data, key, renderAs) {
|
|
1016
|
+
|
|
1017
|
+
function getRenderValue(node, data, key, renderAs, renderIndex) {
|
|
952
1018
|
let operator;
|
|
953
1019
|
if (key.startsWith("$")) {
|
|
954
1020
|
let keys = key.split(".");
|
|
@@ -958,20 +1024,37 @@ function getRenderValue(node, data, key, renderAs) {
|
|
|
958
1024
|
}
|
|
959
1025
|
}
|
|
960
1026
|
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1027
|
+
// Field reads can arrive wrapped under a literal dotted or bracketed key.
|
|
1028
|
+
const hasLiteralKey = Object.prototype.hasOwnProperty.call(data, key);
|
|
1029
|
+
const rootKey = key.split(/[.\[]/, 1)[0];
|
|
1030
|
+
const ownsRootScope =
|
|
1031
|
+
hasLiteralKey || Object.prototype.hasOwnProperty.call(data, rootKey);
|
|
1032
|
+
let value = hasLiteralKey
|
|
1033
|
+
? data[key]
|
|
1034
|
+
: getValueFromObject(data, key);
|
|
1035
|
+
|
|
1036
|
+
// Once the current scope owns a root (for example `object`), a missing
|
|
1037
|
+
// descendant must remain missing instead of leaking in from another
|
|
1038
|
+
// clone's ancestor/source data.
|
|
1039
|
+
if ((value === undefined || value === null) && node && !ownsRootScope) {
|
|
1040
|
+
let parentTemplate = node;
|
|
1041
|
+
do {
|
|
1042
|
+
let parentNode = renderedNodes.get(parentTemplate);
|
|
1043
|
+
if (parentNode) {
|
|
1044
|
+
if (
|
|
1045
|
+
(value === undefined || value === null) &&
|
|
1046
|
+
(parentNode.parent || parentNode.template)
|
|
1047
|
+
) {
|
|
1048
|
+
let Data,
|
|
1049
|
+
eid,
|
|
1050
|
+
parent = parentNode.parent || parentNode.template;
|
|
1051
|
+
do {
|
|
1052
|
+
const parentNodeContext = getRenderContext(parentNode);
|
|
1053
|
+
const parentContext = getRenderContext(parent);
|
|
1054
|
+
if (key.includes("keyPath")) {
|
|
1055
|
+
value =
|
|
1056
|
+
parentNodeContext?.keyPath ||
|
|
1057
|
+
parentContext?.keyPath;
|
|
975
1058
|
} else if (key.includes("parentKey")) {
|
|
976
1059
|
value =
|
|
977
1060
|
parentNode.parentKey ||
|
|
@@ -982,7 +1065,7 @@ function getRenderValue(node, data, key, renderAs) {
|
|
|
982
1065
|
} else if (key === "object" || key === "_id")
|
|
983
1066
|
value = ObjectId().toString();
|
|
984
1067
|
else if (key === "uuid") {
|
|
985
|
-
value =
|
|
1068
|
+
value = uuid(6);
|
|
986
1069
|
} else if (parent.source) {
|
|
987
1070
|
Data = parent.source.data;
|
|
988
1071
|
} else if (parent.parent) {
|
|
@@ -994,28 +1077,36 @@ function getRenderValue(node, data, key, renderAs) {
|
|
|
994
1077
|
}
|
|
995
1078
|
|
|
996
1079
|
if (!Data && parent && parent.eid) eid = parent.eid;
|
|
997
|
-
} while (
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1080
|
+
} while (
|
|
1081
|
+
(value === undefined || value === null) &&
|
|
1082
|
+
parent &&
|
|
1083
|
+
!Data
|
|
1084
|
+
);
|
|
1085
|
+
|
|
1086
|
+
if ((value === undefined || value === null) && Data) {
|
|
1087
|
+
value = getValueFromObject(Data, key);
|
|
1088
|
+
}
|
|
1089
|
+
if (
|
|
1090
|
+
(value === undefined || value === null) &&
|
|
1091
|
+
key.includes(".")
|
|
1092
|
+
) {
|
|
1003
1093
|
let renderedData = getValueFromObject(
|
|
1004
1094
|
Data,
|
|
1005
1095
|
key.split(".")[0]
|
|
1006
1096
|
);
|
|
1007
|
-
if (renderedData) {
|
|
1008
|
-
let index;
|
|
1097
|
+
if (renderedData !== undefined && renderedData !== null) {
|
|
1098
|
+
let index = renderIndex;
|
|
1009
1099
|
if (
|
|
1010
|
-
|
|
1011
|
-
parent.clones.size
|
|
1100
|
+
index === undefined &&
|
|
1101
|
+
(!parent.clones.size ||
|
|
1102
|
+
parent.clones.size === 1)
|
|
1012
1103
|
) {
|
|
1013
1104
|
index = 0;
|
|
1014
|
-
} else if (eid) {
|
|
1105
|
+
} else if (index === undefined && eid) {
|
|
1015
1106
|
index = Array.from(
|
|
1016
1107
|
parent.clones.keys()
|
|
1017
1108
|
).indexOf(eid);
|
|
1018
|
-
}
|
|
1109
|
+
}
|
|
1019
1110
|
|
|
1020
1111
|
if (index >= 0) {
|
|
1021
1112
|
Data = {
|
|
@@ -1028,13 +1119,16 @@ function getRenderValue(node, data, key, renderAs) {
|
|
|
1028
1119
|
}
|
|
1029
1120
|
}
|
|
1030
1121
|
|
|
1031
|
-
if (
|
|
1122
|
+
if (
|
|
1123
|
+
(value === undefined || value === null) &&
|
|
1124
|
+
parentTemplate.parentElement
|
|
1125
|
+
) {
|
|
1032
1126
|
parentTemplate =
|
|
1033
1127
|
parentTemplate.parentElement.closest("[render]");
|
|
1034
1128
|
} else {
|
|
1035
1129
|
parentTemplate = undefined;
|
|
1036
1130
|
}
|
|
1037
|
-
} while (
|
|
1131
|
+
} while ((value === undefined || value === null) && parentTemplate);
|
|
1038
1132
|
}
|
|
1039
1133
|
|
|
1040
1134
|
if (operator) {
|
|
@@ -1150,12 +1244,13 @@ Observer.init({
|
|
|
1150
1244
|
let parentElement = mutation.target.parentElement.closest("[render]");
|
|
1151
1245
|
if (!parentElement) return;
|
|
1152
1246
|
|
|
1153
|
-
let renderedNode = renderedNodes.get(parentElement);
|
|
1154
|
-
let data;
|
|
1155
|
-
if (renderedNode) {
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1247
|
+
let renderedNode = renderedNodes.get(parentElement);
|
|
1248
|
+
let data;
|
|
1249
|
+
if (renderedNode) {
|
|
1250
|
+
const renderContext = getRenderContext(renderedNode);
|
|
1251
|
+
if (renderContext?.renderData) {
|
|
1252
|
+
data = renderContext.renderData;
|
|
1253
|
+
}
|
|
1159
1254
|
} else {
|
|
1160
1255
|
let sourceElement = mutation.target.parentElement.closest("[render-query]")
|
|
1161
1256
|
let sourceData = sources.get(sourceElement);
|
|
@@ -1184,4 +1279,185 @@ Observer.init({
|
|
|
1184
1279
|
|
|
1185
1280
|
init();
|
|
1186
1281
|
|
|
1187
|
-
export { render, renderValue, sources, renderedNodes };
|
|
1282
|
+
export { render, renderValue, sources, renderedNodes };
|
|
1283
|
+
|
|
1284
|
+
/**
|
|
1285
|
+
*
|
|
1286
|
+
* @name array
|
|
1287
|
+
* @kind member
|
|
1288
|
+
* @attribute
|
|
1289
|
+
* @environment client
|
|
1290
|
+
*/
|
|
1291
|
+
|
|
1292
|
+
/**
|
|
1293
|
+
*
|
|
1294
|
+
* @name eid
|
|
1295
|
+
* @kind member
|
|
1296
|
+
* @attribute
|
|
1297
|
+
* @environment client
|
|
1298
|
+
*/
|
|
1299
|
+
|
|
1300
|
+
/**
|
|
1301
|
+
*
|
|
1302
|
+
* @name key
|
|
1303
|
+
* @kind member
|
|
1304
|
+
* @attribute
|
|
1305
|
+
* @environment client
|
|
1306
|
+
*/
|
|
1307
|
+
|
|
1308
|
+
/**
|
|
1309
|
+
*
|
|
1310
|
+
* @name render
|
|
1311
|
+
* @kind member
|
|
1312
|
+
* @attribute
|
|
1313
|
+
* @environment client
|
|
1314
|
+
*/
|
|
1315
|
+
|
|
1316
|
+
/**
|
|
1317
|
+
*
|
|
1318
|
+
* @name render-as
|
|
1319
|
+
* @kind member
|
|
1320
|
+
* @attribute
|
|
1321
|
+
* @environment client
|
|
1322
|
+
*/
|
|
1323
|
+
|
|
1324
|
+
/**
|
|
1325
|
+
*
|
|
1326
|
+
* @name render-clone
|
|
1327
|
+
* @kind member
|
|
1328
|
+
* @attribute
|
|
1329
|
+
* @environment client
|
|
1330
|
+
*/
|
|
1331
|
+
|
|
1332
|
+
/**
|
|
1333
|
+
*
|
|
1334
|
+
* @name render-data
|
|
1335
|
+
* @kind member
|
|
1336
|
+
* @attribute
|
|
1337
|
+
* @environment client
|
|
1338
|
+
*/
|
|
1339
|
+
|
|
1340
|
+
/**
|
|
1341
|
+
*
|
|
1342
|
+
* @name render-exclude
|
|
1343
|
+
* @kind member
|
|
1344
|
+
* @attribute
|
|
1345
|
+
* @environment client
|
|
1346
|
+
*/
|
|
1347
|
+
|
|
1348
|
+
/**
|
|
1349
|
+
*
|
|
1350
|
+
* @name render-include
|
|
1351
|
+
* @kind member
|
|
1352
|
+
* @attribute
|
|
1353
|
+
* @environment client
|
|
1354
|
+
*/
|
|
1355
|
+
|
|
1356
|
+
/**
|
|
1357
|
+
*
|
|
1358
|
+
* @name render-limit
|
|
1359
|
+
* @kind member
|
|
1360
|
+
* @attribute
|
|
1361
|
+
* @environment client
|
|
1362
|
+
*/
|
|
1363
|
+
|
|
1364
|
+
/**
|
|
1365
|
+
*
|
|
1366
|
+
* @name render-query
|
|
1367
|
+
* @kind member
|
|
1368
|
+
* @attribute
|
|
1369
|
+
* @environment client
|
|
1370
|
+
*/
|
|
1371
|
+
|
|
1372
|
+
/**
|
|
1373
|
+
*
|
|
1374
|
+
* @name render-reference
|
|
1375
|
+
* @kind member
|
|
1376
|
+
* @attribute
|
|
1377
|
+
* @environment client
|
|
1378
|
+
*/
|
|
1379
|
+
|
|
1380
|
+
/**
|
|
1381
|
+
*
|
|
1382
|
+
* @name render-reverse
|
|
1383
|
+
* @kind member
|
|
1384
|
+
* @attribute
|
|
1385
|
+
* @environment client
|
|
1386
|
+
*/
|
|
1387
|
+
|
|
1388
|
+
/**
|
|
1389
|
+
*
|
|
1390
|
+
* @name render-type
|
|
1391
|
+
* @kind member
|
|
1392
|
+
* @attribute
|
|
1393
|
+
* @environment client
|
|
1394
|
+
*/
|
|
1395
|
+
|
|
1396
|
+
/**
|
|
1397
|
+
*
|
|
1398
|
+
* @name renderedKey
|
|
1399
|
+
* @kind member
|
|
1400
|
+
* @attribute
|
|
1401
|
+
* @environment client
|
|
1402
|
+
*/
|
|
1403
|
+
|
|
1404
|
+
/**
|
|
1405
|
+
*
|
|
1406
|
+
* @name template
|
|
1407
|
+
* @kind member
|
|
1408
|
+
* @attribute
|
|
1409
|
+
* @environment client
|
|
1410
|
+
*/
|
|
1411
|
+
|
|
1412
|
+
/**
|
|
1413
|
+
*
|
|
1414
|
+
* @name value-type
|
|
1415
|
+
* @kind member
|
|
1416
|
+
* @attribute
|
|
1417
|
+
* @environment client
|
|
1418
|
+
*/
|
|
1419
|
+
|
|
1420
|
+
/**
|
|
1421
|
+
* A simple render component in vanilla javascript. Easily configured using HTML5 attributes and/or JavaScript API.
|
|
1422
|
+
*
|
|
1423
|
+
* @module render
|
|
1424
|
+
* @environment client
|
|
1425
|
+
*/
|
|
1426
|
+
|
|
1427
|
+
/**
|
|
1428
|
+
* Declarative selectors recognized by this module.
|
|
1429
|
+
*
|
|
1430
|
+
* @name declarative-selectors
|
|
1431
|
+
* @kind member
|
|
1432
|
+
* @selectors
|
|
1433
|
+
* @environment client
|
|
1434
|
+
* @example
|
|
1435
|
+
* ```json
|
|
1436
|
+
* [
|
|
1437
|
+
* "[${params}]",
|
|
1438
|
+
* "[render-element]",
|
|
1439
|
+
* "[render-clone]",
|
|
1440
|
+
* "[render-query]"
|
|
1441
|
+
* ]
|
|
1442
|
+
* ```
|
|
1443
|
+
*/
|
|
1444
|
+
|
|
1445
|
+
/**
|
|
1446
|
+
* Declarative actions supported or registered by this module.
|
|
1447
|
+
*
|
|
1448
|
+
* @name declarative-actions
|
|
1449
|
+
* @kind member
|
|
1450
|
+
* @actions
|
|
1451
|
+
* @environment client
|
|
1452
|
+
* @example
|
|
1453
|
+
* ```json
|
|
1454
|
+
* [
|
|
1455
|
+
* {
|
|
1456
|
+
* "name": "render"
|
|
1457
|
+
* },
|
|
1458
|
+
* {
|
|
1459
|
+
* "name": "renderKey"
|
|
1460
|
+
* }
|
|
1461
|
+
* ]
|
|
1462
|
+
* ```
|
|
1463
|
+
*/
|