@cocreate/render 1.45.5 → 1.46.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/src/index.js CHANGED
@@ -25,10 +25,12 @@ import Actions from "@cocreate/actions";
25
25
  import Observer from "@cocreate/observer";
26
26
  import uuid from "@cocreate/uuid";
27
27
  import {
28
- queryElements,
29
- getValueFromObject,
30
- dotNotationToObject,
31
- ObjectId
28
+ queryElements,
29
+ getValueFromObject,
30
+ dotNotationToObject,
31
+ ObjectId,
32
+ safeParse,
33
+ processOperatorsAsync
32
34
  } from "@cocreate/utils";
33
35
  import "@cocreate/element-prototype";
34
36
  import "./index.css";
@@ -45,28 +47,44 @@ const elementSelector = "[render-query]";
45
47
  * - render-query: [Describe what this attribute does]
46
48
  *
47
49
  * @param {(Element|Element[]|HTMLCollection|null)} [element] - Optional. A single element, an array of elements, an HTMLCollection, or null.
48
- * - If an HTMLCollection or an array of elements is provided, each element in the collection/array is initialized.
49
- * - If a single element is provided, only that element is initialized.
50
- * - If null or omitted, the function queries and initializes all elements matching the 'elementSelector'.
50
+ * - If an HTMLCollection or an array of elements is provided, each element in the collection/array is initialized.
51
+ * - If a single element is provided, only that element is initialized.
52
+ * - If null or omitted, the function queries and initializes all elements matching the 'elementSelector'.
51
53
  */
52
54
  function init(element) {
53
- if (
54
- element &&
55
- !(element instanceof HTMLCollection) &&
56
- !Array.isArray(element)
57
- )
58
- element = [element];
59
- else if (!element) element = document.querySelectorAll(elementSelector);
60
-
61
- for (let i = 0; i < element.length; i++) {
62
- let source = sources.get(element[i]);
63
- if (!source) {
64
- sources.set(element[i], { element: element[i] });
65
- element[i].renderValue = async (data) =>
66
- await render({ source: element[i], data });
67
- element[i].getData = () => sources.get(element[i]).data;
68
- }
69
- }
55
+ if (
56
+ element &&
57
+ !(element instanceof HTMLCollection) &&
58
+ !Array.isArray(element)
59
+ )
60
+ element = [element];
61
+ else if (!element) element = document.querySelectorAll(elementSelector);
62
+
63
+ for (let i = 0; i < element.length; i++) {
64
+ let source = sources.get(element[i]);
65
+ if (!source) {
66
+ sources.set(element[i], { element: element[i] });
67
+ element[i].renderValue = async (data) =>
68
+ await render({ source: element[i], data });
69
+ element[i].getData = () => sources.get(element[i]).data;
70
+ }
71
+
72
+ let dataAttr = element[i].getAttribute("render-data");
73
+
74
+ if (dataAttr) {
75
+ try {
76
+ // Parse the string into a usable object
77
+ const parsedData = JSON.parse(dataAttr);
78
+
79
+ // Ensure the method exists before calling it
80
+ if (typeof element[i].renderValue === "function") {
81
+ element[i].renderValue(parsedData);
82
+ }
83
+ } catch (error) {
84
+ console.error("Failed to parse render-data:", error);
85
+ }
86
+ }
87
+ }
70
88
  }
71
89
 
72
90
  /**
@@ -85,1178 +103,1084 @@ function init(element) {
85
103
  * @param {boolean} [options.remove] - Flag to indicate if the rendering should remove elements instead of updating or appending.
86
104
  */
87
105
  async function render({
88
- source,
89
- element,
90
- selector,
91
- data,
92
- key,
93
- index,
94
- currentIndex,
95
- update,
96
- remove
106
+ source,
107
+ element,
108
+ selector,
109
+ data,
110
+ key,
111
+ index,
112
+ currentIndex,
113
+ update,
114
+ remove
97
115
  }) {
98
- if (!data || (!source && !data)) {
99
- return;
100
- }
101
-
102
- if (!element) {
103
- if (source instanceof HTMLElement) {
104
- if (selector || (selector = source.getAttribute("render-query"))) {
105
- element = queryElements({
106
- element: source,
107
- selector
108
- });
109
- } else if (source.children.length > 0) {
110
- element = Array.from(source.children).find((child) =>
111
- child.matches("template, [template], .template, [render]")
112
- );
113
- if (!element) {
114
- element = source.querySelector(
115
- "template, [template], .template, [render]"
116
- );
117
- }
118
- }
119
- } else if (selector) {
120
- element = queryElements({
121
- selector
122
- });
123
- }
124
-
125
- if (!element || (Array.isArray(element) && !element.length)) {
126
- return;
127
- }
128
- }
129
-
130
- if (!key) {
131
- if (source instanceof HTMLElement) {
132
- key =
133
- source.getAttribute("render") ||
134
- source.getAttribute("key") ||
135
- data.type;
136
- } else {
137
- key = data.type;
138
- }
139
- if (!key || key === "key") {
140
- key = "object";
141
- } else if (key === "{}" || (!key && data.method)) {
142
- key = data.method ? data.method.split(".")[0] : undefined;
143
- }
144
- if (!key) {
145
- return;
146
- }
147
- }
148
-
149
- let sourceData = sources.get(source);
150
- if (!sourceData) {
151
- sourceData = { element: source, data: { ...data } };
152
- sources.set(source, sourceData);
153
- } else if (sourceData.data) {
154
- sourceData.data = { ...data };
155
- } else {
156
- // TODO: Most effiecientway to comapre too objects or array of objects
157
- if (data === sourceData.data) return;
158
- sourceData.data = { ...data };
159
- }
160
-
161
- if (data.$filter) {
162
- index = index || data.$filter.startingIndex || data.$filter.index;
163
- update = update || data.$filter.update;
164
- remove = remove || data.$filter.remove;
165
- }
166
-
167
- if (
168
- !Array.isArray(element) &&
169
- !(element instanceof HTMLCollection) &&
170
- !(element instanceof NodeList)
171
- ) {
172
- element = [element];
173
- }
174
-
175
- for (let i = 0; i < element.length; i++) {
176
- let renderedNode = renderedNodes.get(element[i]);
177
- if (renderedNode) {
178
- if (
179
- renderedNode.clones &&
180
- renderedNode.source &&
181
- renderedNode.source.element
182
- ) {
183
- let limit =
184
- renderedNode.source.element.getAttribute("render-limit");
185
- if (limit && renderedNode.clones.size >= parseInt(limit))
186
- continue;
187
- }
188
- } else {
189
- renderedNode = {
190
- element: element[i],
191
- source: sourceData,
192
- clones: new Map()
193
- };
194
- renderedNodes.set(element[i], renderedNode);
195
- }
196
-
197
- let clones = renderedNode.clones; // || renderedNode.template.clones..
198
- if (!clones && renderedNode.template) {
199
- clones = renderedNode.template.clones;
200
- console.log("renderedNode.template.clones", clones);
201
- }
202
-
203
- key = element[i].getAttribute("render") || key;
204
- let renderedSource = renderedNode.source;
205
- if (!renderedSource) {
206
- if (renderedNode.parent && renderedNode.parent.template) {
207
- renderedSource = renderedNode.parent.template.source;
208
- }
209
- }
210
- let renderType;
211
- if (renderedSource && renderedSource.element) {
212
- renderType = renderedSource.element.getAttribute("render-type");
213
- }
214
-
215
- let clone;
216
- if (remove) {
217
- for (let j = 0; j < data[key].length; j++) {
218
- let cloneKey;
219
- if (key === "object") {
220
- cloneKey = data[key][j]._id;
221
- } else {
222
- cloneKey = data[key][j].name;
223
- }
224
-
225
- clone = clones.get(cloneKey);
226
- if (!clone) return;
227
-
228
- clones.delete(cloneKey);
229
- renderedNodes.delete(clone);
230
- clone.remove();
231
- }
232
- } else if (
233
- (key && (Array.isArray(data[key]) || renderType)) ||
234
- Array.isArray(data)
235
- ) {
236
- if (update) {
237
- for (let j = 0; j < data[key].length; j++) {
238
- if (key === "object") {
239
- clone = clones.get(data[key][j]._id);
240
- } else {
241
- clone = clones.get(data[key][j].name);
242
- }
243
-
244
- if (!currentIndex) currentIndex = data.$filter.currentIndex;
245
-
246
- if (!clone) return;
247
-
248
- await renderValues(clone, { object: data[key][j] });
249
- if (currentIndex >= 0)
250
- insertElement(renderedNode, clone, index, currentIndex);
251
- }
252
- } else {
253
- // TODO: if $auto here every subsequent clone will have same value, not replacing here will apply unique value to each clone
254
- if (key === "$auto")
255
- key = key.replace(/\$auto/g, uuid.generate(6));
256
-
257
- if (key) element[i].setAttribute("render", key);
258
-
259
- await renderTemplate(element[i], data, key, index);
260
- }
261
- } else {
262
- let renderAs = element[i].getAttribute("render-as") || key;
263
- await renderValues(element[i], data, key, renderAs);
264
- }
265
- }
266
- // Dispatch input and change events after rendering is complete
267
- if (source instanceof HTMLElement) {
268
- source.dispatchEvent(new Event("input"));
269
- source.dispatchEvent(new Event("change"));
270
- }
116
+ if (!data || (!source && !data)) {
117
+ return;
118
+ }
119
+
120
+ if (!element) {
121
+ if (source instanceof HTMLElement) {
122
+ if (selector || (selector = source.getAttribute("render-query"))) {
123
+ element = queryElements({
124
+ element: source,
125
+ selector
126
+ });
127
+ } else if (source.children.length > 0) {
128
+ element = Array.from(source.children).find((child) =>
129
+ child.matches("template, [template], .template, [render]")
130
+ );
131
+ if (!element) {
132
+ element = source.querySelector(
133
+ "template, [template], .template, [render]"
134
+ );
135
+ }
136
+ }
137
+ } else if (selector) {
138
+ element = queryElements({
139
+ selector
140
+ });
141
+ }
142
+
143
+ if (!element || (Array.isArray(element) && !element.length)) {
144
+ return;
145
+ }
146
+ }
147
+
148
+ if (!key) {
149
+ if (source instanceof HTMLElement) {
150
+ key =
151
+ source.getAttribute("render") ||
152
+ source.getAttribute("key") ||
153
+ data.type;
154
+ } else {
155
+ key = data.type;
156
+ }
157
+ if (!key || key === "key") {
158
+ key = "object";
159
+ } else if (key === "{}" || (!key && data.method)) {
160
+ key = data.method ? data.method.split(".")[0] : undefined;
161
+ }
162
+ if (!key) {
163
+ return;
164
+ }
165
+ }
166
+
167
+ let sourceData = sources.get(source);
168
+ if (!sourceData) {
169
+ sourceData = { element: source, data: { ...data } };
170
+ sources.set(source, sourceData);
171
+ } else if (sourceData.data) {
172
+ sourceData.data = { ...data };
173
+ } else {
174
+ // TODO: Most effiecientway to comapre too objects or array of objects
175
+ if (data === sourceData.data) return;
176
+ sourceData.data = { ...data };
177
+ }
178
+
179
+ if (data.$filter) {
180
+ index = index || data.$filter.startingIndex || data.$filter.index;
181
+ update = update || data.$filter.update;
182
+ remove = remove || data.$filter.remove;
183
+ }
184
+
185
+ if (
186
+ !Array.isArray(element) &&
187
+ !(element instanceof HTMLCollection) &&
188
+ !(element instanceof NodeList)
189
+ ) {
190
+ element = [element];
191
+ }
192
+
193
+ for (let i = 0; i < element.length; i++) {
194
+ let renderedNode = renderedNodes.get(element[i]);
195
+ if (renderedNode) {
196
+ if (
197
+ renderedNode.clones &&
198
+ renderedNode.source &&
199
+ renderedNode.source.element
200
+ ) {
201
+ let limit =
202
+ renderedNode.source.element.getAttribute("render-limit");
203
+ if (limit && renderedNode.clones.size >= parseInt(limit))
204
+ continue;
205
+ }
206
+ } else {
207
+ renderedNode = {
208
+ element: element[i],
209
+ source: sourceData,
210
+ clones: new Map()
211
+ };
212
+ renderedNodes.set(element[i], renderedNode);
213
+ }
214
+
215
+ let clones = renderedNode.clones; // || renderedNode.template.clones..
216
+ if (!clones && renderedNode.template) {
217
+ clones = renderedNode.template.clones;
218
+ console.log("renderedNode.template.clones", clones);
219
+ }
220
+
221
+ key = element[i].getAttribute("render") || key;
222
+ let renderedSource = renderedNode.source;
223
+ if (!renderedSource) {
224
+ if (renderedNode.parent && renderedNode.parent.template) {
225
+ renderedSource = renderedNode.parent.template.source;
226
+ }
227
+ }
228
+ let renderType;
229
+ if (renderedSource && renderedSource.element) {
230
+ renderType = renderedSource.element.getAttribute("render-type");
231
+ }
232
+
233
+ let clone;
234
+ if (remove) {
235
+ for (let j = 0; j < data[key].length; j++) {
236
+ let cloneKey;
237
+ if (key === "object") {
238
+ cloneKey = data[key][j]._id;
239
+ } else {
240
+ cloneKey = data[key][j].name;
241
+ }
242
+
243
+ clone = clones.get(cloneKey);
244
+ if (!clone) return;
245
+
246
+ clones.delete(cloneKey);
247
+ renderedNodes.delete(clone);
248
+ clone.remove();
249
+ }
250
+ } else if (
251
+ (key && (Array.isArray(data[key]) || renderType)) ||
252
+ Array.isArray(data)
253
+ ) {
254
+ if (update) {
255
+ for (let j = 0; j < data[key].length; j++) {
256
+ if (key === "object") {
257
+ clone = clones.get(data[key][j]._id);
258
+ } else {
259
+ clone = clones.get(data[key][j].name);
260
+ }
261
+
262
+ if (!currentIndex) currentIndex = data.$filter.currentIndex;
263
+
264
+ if (!clone) return;
265
+
266
+ await renderValues(clone, { object: data[key][j] });
267
+ if (currentIndex >= 0)
268
+ insertElement(renderedNode, clone, index, currentIndex);
269
+ }
270
+ } else {
271
+ // TODO: if $auto here every subsequent clone will have same value, not replacing here will apply unique value to each clone
272
+ if (key === "$auto")
273
+ key = key.replace(/\$auto/g, uuid.generate(6));
274
+
275
+ if (key) element[i].setAttribute("render", key);
276
+
277
+ await renderTemplate(element[i], data, key, index);
278
+ }
279
+ } else {
280
+ let renderAs = element[i].getAttribute("render-as") || key;
281
+ await renderValues(element[i], data, key, renderAs);
282
+ }
283
+ }
284
+ // Dispatch input and change events after rendering is complete
285
+ if (source instanceof HTMLElement) {
286
+ source.dispatchEvent(new Event("input"));
287
+ source.dispatchEvent(new Event("change"));
288
+ }
271
289
  }
272
290
 
273
291
  async function renderTemplate(template, data, key, index, keyPath) {
274
- // if (!key)
275
- // key = template.getAttribute('render')
276
- if (!(template instanceof HTMLElement)) {
277
- console.log("template not an element but should be")
278
- }
279
- let templateData = renderedNodes.get(template);
280
- if (!templateData) {
281
- templateData = {element: template, clones: new Map() };
282
- renderedNodes.set(template, templateData);
283
- }
284
-
285
- if (!templateData.parent && !templateData.source) {
286
- let parentRender = template.parentElement.closest("[render]")
287
- if (parentRender || template.source)
288
- templateData.parent = renderedNodes.get(parentRender) || template.source
289
- else
290
- console.log("template parent missing is there no parent or is this an error? and source is", templateData.source)
291
- }
292
-
293
- if(!templateData.source && templateData.parent && templateData.parent.source){
294
- templateData.source = templateData.parent.source
295
- }
296
-
297
- if (!templateData.keyPath) {
298
- if (keyPath) {
299
- templateData.keyPath = keyPath;
300
- } else if (templateData.parent && templateData.parent.keyPath) {
301
- templateData.keyPath = templateData.parent.keyPath;
302
- } else if (key) {
303
- templateData.keyPath = key;
304
- }
305
- }
306
-
307
- let renderData;
308
- if (key) {
309
- renderData = getRenderValue(templateData.element, data, key);
310
- }
311
-
312
- if (!renderData) {
313
- renderData = data;
314
- }
315
-
316
- // else if (Array.isArray(data)) {
317
- // renderData = data;
318
- // }
319
-
320
- // if (!renderData && data) {
321
- // if (Array.isArray(data)) {
322
- // renderData = data;
323
- // } else {
324
- // renderData = [data];
325
- // }
326
- // } else return;
327
-
328
- templateData.renderData = renderData;
329
- let el = templateData.element;
330
- if (el.nodeType !== 1) {
331
- el = templateData.parent.element;
332
- }
333
-
334
- let renderAs = templateData.renderAs
335
- if (!templateData.renderAs) {
336
- renderAs =
337
- el.getAttribute("render-as") ||
338
- el.getAttribute("render") ||
339
- key;
340
- templateData.renderAs = renderAs;
341
- }
342
-
343
- let renderType;
344
- if (templateData.source && templateData.source.element)
345
- renderType = templateData.source.element.getAttribute("render-type");
346
-
347
- let renderReverse;
348
- if (templateData.source && templateData.source.element) {
349
- renderReverse =
350
- templateData.source.element.getAttribute("render-reverse");
351
- if (!renderReverse)
352
- renderReverse = el.getAttribute("render-reverse");
353
- }
354
-
355
- let exclude = el.getAttribute("render-exclude") || "";
356
- if (exclude) {
357
- exclude = exclude.replace(/ /g, "").split(",");
358
- }
359
- let reference = el.getAttribute("render-reference");
360
-
361
- let isInsert = data.$filter && (data.$filter.create || data.$filter.update);
362
- if (renderType !== "object") {
363
- if ((!isInsert && !index) || data.$filter.overwrite) {
364
- if (!templateData.clones) {
365
- return
366
- if (templateData.template) {
367
- templateData.clones = templateData.template.clones
368
- } else if (templateData.parent && templateData.parent.template) {
369
- templateData.clones = templateData.parent.template.clones
370
- }
371
- }
372
-
373
- // if (!templateData.clones) {
374
- // templateData
375
- // templateData = templateData.templateData;
376
- // }
377
- for (const [key, element] of templateData.clones) {
378
- renderedNodes.delete(element);
379
- element.remove();
380
- templateData.clones.delete(key);
381
- }
382
- } //
383
- // else if (index) {
384
- // updates data that has already been rendered
385
- // templateData.data = dotNotationToObject(renderData, templateData.data)
386
- // }
387
- }
388
-
389
- if ((key && !Array.isArray(renderData)) || renderType === "object") {
390
- if (renderType && Array.isArray(renderData) && renderData.length === 1)
391
- renderData = renderData[0];
392
-
393
- const keys = Object.keys(renderData);
394
- for (let i = 0; i < keys.length; i++) {
395
- let clone = templateData.clones.get(keys[i]);
396
- clone = renderedNodes.get(clone);
397
-
398
- if (
399
- exclude.includes(keys[i]) ||
400
- (reference === "false" &&
401
- ["$storage", "$database", "$array"].includes(key))
402
- ) {
403
- if (clone) {
404
- renderedNodes.delete(clone.element);
405
- clone.element.remove();
406
- templateData.clones.delete(keys[i]);
407
- }
408
- continue;
409
- }
410
-
411
- let value = renderData[keys[i]];
412
- let type = "string";
413
-
414
- if (Array.isArray(value)) type = "array";
415
- else if (typeof value == "object") type = "object";
416
-
417
- let Data = { [renderAs]: { key: keys[i], value, type } };
418
-
419
- if (!clone) {
420
- clone = cloneTemplate(templateData);
421
- } else {
422
- if (clone.renderedValue === value) {
423
- continue;
424
- }
425
- }
426
- clone.renderedValue = value;
427
- // let clone = cloneTemplate(templateData);
428
-
429
- let renderedKey = key.split(".");
430
- renderedKey = renderedKey[renderedKey.length - 1];
431
- //renderedKey needs to remove the parent.renderAs/key
432
-
433
- clone.key = keys[i];
434
- clone.keyPath = templateData.keyPath + "." + renderedKey;
435
- clone.parentKey = renderedKey;
436
- clone.renderKey = key;
437
- clone.element.setAttribute("renderedKey", keys[i]);
438
- await renderValues(clone.element, Data, keys[i], renderAs);
439
- insertElement(templateData, clone.element, index);
440
- }
441
- } else {
442
- if (!key && !Array.isArray(renderData)) {
443
- key = "data";
444
- renderData = getValueFromObject(renderData, key);
445
- if (!renderAs) renderAs = key;
446
- }
447
-
448
- if (!renderData) {
449
- let clone = cloneTemplate(templateData);
450
- clone.keyPath = templateData.keyPath;
451
- await renderValues(clone.element, data, key, renderAs);
452
- insertElement(templateData, clone.element, index);
453
- } else {
454
- if (!Array.isArray(renderData)) renderData = [renderData];
455
-
456
- for (let i = 0; i < renderData.length; i++) {
457
- let clone = cloneTemplate(templateData);
458
-
459
- let test = clone.element.getAttribute("render-as");
460
- if (test) renderAs = test;
461
-
462
- clone.keyPath = templateData.keyPath || "" + `[${i}]`;
463
-
464
- let object;
465
- if (
466
- typeof renderData[i] === "object" &&
467
- renderData[i] !== null
468
- ) {
469
- object = { ...renderData[i] };
470
- } else {
471
- object = renderData[i];
472
- }
473
-
474
- for (let j = 0; j < exclude.length; j++)
475
- delete object[exclude[j]];
476
-
477
- if (reference === "false") {
478
- delete object.$storage;
479
- delete object.$database;
480
- delete object.$array;
481
- }
482
-
483
- if (renderAs) {
484
- object = { [renderAs]: object };
485
- if (renderAs.includes("."))
486
- object = dotNotationToObject(object);
487
- } else object = object;
488
-
489
- let index;
490
- if (key !== "data") index = i;
491
-
492
- clone.renderData = object
493
- clone.index = index
494
- await renderValues(
495
- clone.element,
496
- object,
497
- key,
498
- renderAs,
499
- null,
500
- null,
501
- index
502
- );
503
- insertElement(
504
- templateData,
505
- clone.element,
506
- index,
507
- "",
508
- data,
509
- renderReverse
510
- );
511
- }
512
- }
513
- }
292
+ if (!(template instanceof HTMLElement)) {
293
+ console.log("template not an element but should be")
294
+ }
295
+ let templateData = renderedNodes.get(template);
296
+ if (!templateData) {
297
+ templateData = {element: template, clones: new Map() };
298
+ renderedNodes.set(template, templateData);
299
+ }
300
+
301
+ if (!templateData.parent && !templateData.source) {
302
+ let parentRender = template.parentElement.closest("[render]")
303
+ if (parentRender || template.source)
304
+ templateData.parent = renderedNodes.get(parentRender) || template.source
305
+ else
306
+ console.log("template parent missing is there no parent or is this an error? and source is", templateData.source)
307
+ }
308
+
309
+ if(!templateData.source && templateData.parent && templateData.parent.source){
310
+ templateData.source = templateData.parent.source
311
+ }
312
+
313
+ if (!templateData.keyPath) {
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;
324
+ if (key) {
325
+ renderData = getRenderValue(templateData.element, data, key);
326
+ }
327
+
328
+ if (!renderData) {
329
+ renderData = data;
330
+ }
331
+
332
+ templateData.renderData = renderData;
333
+ let el = templateData.element;
334
+ if (el.nodeType !== 1) {
335
+ el = templateData.parent.element;
336
+ }
337
+
338
+ let renderAs = templateData.renderAs
339
+ if (!templateData.renderAs) {
340
+ renderAs =
341
+ el.getAttribute("render-as") ||
342
+ el.getAttribute("render") ||
343
+ key;
344
+ templateData.renderAs = renderAs;
345
+ }
346
+
347
+ let renderType;
348
+ if (templateData.source && templateData.source.element)
349
+ renderType = templateData.source.element.getAttribute("render-type");
350
+
351
+ let renderReverse;
352
+ if (templateData.source && templateData.source.element) {
353
+ renderReverse =
354
+ templateData.source.element.getAttribute("render-reverse");
355
+ if (renderReverse === null)
356
+ renderReverse = el.getAttribute("render-reverse");
357
+ }
358
+
359
+ let exclude = el.getAttribute("render-exclude") || "";
360
+ if (exclude) {
361
+ exclude = exclude.replace(/ /g, "").split(",");
362
+ }
363
+ let reference = el.getAttribute("render-reference");
364
+
365
+ let isInsert = data.$filter && (data.$filter.create || data.$filter.update);
366
+ if (renderType !== "object") {
367
+ if ((!isInsert && !index) || data.$filter.overwrite) {
368
+ if (!templateData.clones) {
369
+ return
370
+ if (templateData.template) {
371
+ templateData.clones = templateData.template.clones
372
+ } else if (templateData.parent && templateData.parent.template) {
373
+ templateData.clones = templateData.parent.template.clones
374
+ }
375
+ }
376
+
377
+ for (const [key, element] of templateData.clones) {
378
+ renderedNodes.delete(element);
379
+ element.remove();
380
+ templateData.clones.delete(key);
381
+ }
382
+ }
383
+ }
384
+
385
+ if ((key && !Array.isArray(renderData)) || renderType === "object") {
386
+ if (renderType && Array.isArray(renderData) && renderData.length === 1)
387
+ renderData = renderData[0];
388
+
389
+ const keys = Object.keys(renderData);
390
+ for (let i = 0; i < keys.length; i++) {
391
+ let clone = templateData.clones.get(keys[i]);
392
+ clone = renderedNodes.get(clone);
393
+
394
+ if (
395
+ exclude.includes(keys[i]) ||
396
+ (reference === "false" &&
397
+ ["$storage", "$database", "$array"].includes(key))
398
+ ) {
399
+ if (clone) {
400
+ renderedNodes.delete(clone.element);
401
+ clone.element.remove();
402
+ templateData.clones.delete(keys[i]);
403
+ }
404
+ continue;
405
+ }
406
+
407
+ let value = renderData[keys[i]];
408
+ let type = "string";
409
+
410
+ if (Array.isArray(value)) type = "array";
411
+ else if (typeof value == "object") type = "object";
412
+
413
+ let Data = { [renderAs]: { key: keys[i], value, type } };
414
+
415
+ if (!clone) {
416
+ clone = cloneTemplate(templateData);
417
+ } else {
418
+ if (clone.renderedValue === value) {
419
+ continue;
420
+ }
421
+ }
422
+ clone.renderedValue = value;
423
+
424
+ let renderedKey = key.split(".");
425
+ renderedKey = renderedKey[renderedKey.length - 1];
426
+
427
+ clone.key = keys[i];
428
+ clone.keyPath = templateData.keyPath + "." + renderedKey;
429
+ clone.parentKey = renderedKey;
430
+ clone.renderKey = key;
431
+ clone.element.setAttribute("renderedKey", keys[i]);
432
+ await renderValues(clone.element, Data, keys[i], renderAs);
433
+ insertElement(templateData, clone.element, index);
434
+ }
435
+ } else {
436
+ if (!key && !Array.isArray(renderData)) {
437
+ key = "data";
438
+ renderData = getValueFromObject(renderData, key);
439
+ if (!renderAs) renderAs = key;
440
+ }
441
+
442
+ if (!renderData) {
443
+ let clone = cloneTemplate(templateData);
444
+ clone.keyPath = templateData.keyPath;
445
+ await renderValues(clone.element, data, key, renderAs);
446
+ insertElement(templateData, clone.element, index);
447
+ } else {
448
+ if (!Array.isArray(renderData)) renderData = [renderData];
449
+
450
+ for (let i = 0; i < renderData.length; i++) {
451
+ let clone = cloneTemplate(templateData);
452
+
453
+ let test = clone.element.getAttribute("render-as");
454
+ if (test) renderAs = test;
455
+
456
+ clone.keyPath = templateData.keyPath || "" + `[${i}]`;
457
+
458
+ let object;
459
+ if (
460
+ typeof renderData[i] === "object" &&
461
+ renderData[i] !== null
462
+ ) {
463
+ object = { ...renderData[i] };
464
+ } else {
465
+ object = renderData[i];
466
+ }
467
+
468
+ for (let j = 0; j < exclude.length; j++)
469
+ delete object[exclude[j]];
470
+
471
+ if (reference === "false") {
472
+ delete object.$storage;
473
+ delete object.$database;
474
+ delete object.$array;
475
+ }
476
+
477
+ if (renderAs) {
478
+ object = { [renderAs]: object };
479
+ if (renderAs.includes("."))
480
+ object = dotNotationToObject(object);
481
+ } else object = object;
482
+
483
+ let index;
484
+ if (key !== "data") index = i;
485
+
486
+ clone.renderData = object
487
+ clone.index = index
488
+ await renderValues(
489
+ clone.element,
490
+ object,
491
+ key,
492
+ renderAs,
493
+ null,
494
+ null,
495
+ index
496
+ );
497
+ insertElement(
498
+ templateData,
499
+ clone.element,
500
+ index,
501
+ "",
502
+ data,
503
+ renderReverse
504
+ );
505
+ }
506
+ }
507
+ }
514
508
  }
515
509
 
516
510
  function cloneTemplate(template) {
517
- let clone = template.element.cloneNode(true);
518
- let cloneTagName = clone.tagName;
519
-
520
- if (clone.tagName == "TEMPLATE") {
521
- clone = template.element.content.firstElementChild;
522
- for (let attribute of template.element.attributes) {
523
- clone.setAttribute(attribute.name, attribute.value || "");
524
- }
525
- } else {
526
- clone.classList.remove("template");
527
- clone.removeAttribute("template");
528
- }
529
-
530
- clone.setAttribute("render-clone", "");
531
-
532
- let renderAs = clone.getAttribute("render-as");
533
- if (renderAs === "$auto") {
534
- renderAs = uuid.generate(6);
535
- clone = clone.outerHTML.replace(/\$auto/g, renderAs);
536
- }
537
-
538
- if (typeof clone === "string") {
539
- // TODO: Some elements are only allowed a specific element, need to find these elements and confirm proper rendering.
540
- let container;
541
- if (cloneTagName === "TR") container = document.createElement("tbody");
542
- else container = document.createElement("div");
543
-
544
- container.innerHTML = clone;
545
- clone = container.firstChild;
546
- container.remove();
547
- }
548
-
549
- let renderedNode = { template, element: clone };
550
- renderedNodes.set(clone, renderedNode);
551
- return renderedNode;
511
+ let clone = template.element.cloneNode(true);
512
+ let cloneTagName = clone.tagName;
513
+
514
+ if (clone.tagName == "TEMPLATE") {
515
+ clone = template.element.content.firstElementChild;
516
+ for (let attribute of template.element.attributes) {
517
+ clone.setAttribute(attribute.name, attribute.value || "");
518
+ }
519
+ } else {
520
+ clone.classList.remove("template");
521
+ clone.removeAttribute("template");
522
+ }
523
+
524
+ clone.setAttribute("render-clone", "");
525
+
526
+ let renderAs = clone.getAttribute("render-as");
527
+ if (renderAs === "$auto") {
528
+ renderAs = uuid.generate(6);
529
+ clone = clone.outerHTML.replace(/\$auto/g, renderAs);
530
+ }
531
+
532
+ if (typeof clone === "string") {
533
+ // TODO: Some elements are only allowed a specific element, need to find these elements and confirm proper rendering.
534
+ let container;
535
+ if (cloneTagName === "TR") container = document.createElement("tbody");
536
+ else container = document.createElement("div");
537
+
538
+ container.innerHTML = clone;
539
+ clone = container.firstChild;
540
+ container.remove();
541
+ }
542
+
543
+ let renderedNode = { template, element: clone };
544
+ renderedNodes.set(clone, renderedNode);
545
+ return renderedNode;
552
546
  }
553
547
 
554
548
  function insertElement(
555
- template,
556
- element,
557
- index,
558
- currentIndex,
559
- data,
560
- renderReverse
549
+ template,
550
+ element,
551
+ index,
552
+ currentIndex,
553
+ data,
554
+ renderReverse
561
555
  ) {
562
- let eid = element.getAttribute("eid");
563
- if (!eid) {
564
- eid = uuid.generate(6);
565
- element.setAttribute("eid", eid);
566
- }
567
-
568
- if (index !== null && index >= 0) {
569
- if (!template.clones) template = template.template;
570
- // const clones = Array.from(template.clones);
571
-
572
- let item;
573
- if (!currentIndex && currentIndex !== 0) {
574
- if (template.clones.has(eid)) return;
575
- item = [eid, element];
576
- }
577
-
578
- const clones = Array.from(template.clones);
579
-
580
- if (currentIndex || currentIndex === 0) {
581
- item = clones.splice(currentIndex, 1)[0];
582
- }
583
-
584
- if (data && data.$filter && data.$filter.startingIndex)
585
- index += data.$filter.startingIndex;
586
- clones.splice(index, 0, item); // Insert item into clones at the specified index
587
-
588
- if (renderReverse) {
589
- // Chat-specific logic: reverse rendering but still inserting based on sorting
590
-
591
- // If we're rendering in reverse order, we want to respect the insertion index
592
- if (clones[index + 1] && clones[index + 1][1] !== element) {
593
- // Insert before the next element (reverse order means newer items come before older ones)
594
- clones[index + 1][1].insertAdjacentElement("afterend", element);
595
- } else if (clones[index] && clones[index][1] !== element) {
596
- // Insert after the current element if there's no next element
597
- clones[index][1].insertAdjacentElement("beforeend", element);
598
- } else {
599
- // Fallback: insert before the template if clones are empty or undefined
600
- template.element.insertAdjacentElement("afterend", element);
601
- }
602
- } else {
603
- // Non-chat logic: handle ascending or descending order as usual
604
- if (clones[index + 1] && clones[index + 1][1] !== element) {
605
- // Insert before the next element
606
- clones[index + 1][1].insertAdjacentElement(
607
- "beforebegin",
608
- element
609
- );
610
- } else if (clones[index] && clones[index][1] !== element) {
611
- // Insert after the current element if there's no next element
612
- clones[index][1].insertAdjacentElement("afterend", element);
613
- } else {
614
- // Fallback: insert before the template if clones are empty or undefined
615
- template.element.insertAdjacentElement("beforebegin", element);
616
- }
617
- }
618
-
619
- template.clones = new Map(clones);
620
- } else {
621
- template.clones.set(eid, element);
622
- template.element.insertAdjacentElement("beforebegin", element);
623
- }
556
+ let eid = element.getAttribute("eid");
557
+ if (!eid) {
558
+ eid = uuid.generate(6);
559
+ element.setAttribute("eid", eid);
560
+ }
561
+
562
+ if (index !== null && index >= 0) {
563
+ if (!template.clones) template = template.template;
564
+
565
+ let item;
566
+ if (!currentIndex && currentIndex !== 0) {
567
+ if (template.clones.has(eid)) return;
568
+ item = [eid, element];
569
+ }
570
+
571
+ const clones = Array.from(template.clones);
572
+
573
+ if (currentIndex || currentIndex === 0) {
574
+ item = clones.splice(currentIndex, 1)[0];
575
+ }
576
+
577
+ if (data && data.$filter && data.$filter.startingIndex)
578
+ index += data.$filter.startingIndex;
579
+ clones.splice(index, 0, item); // Insert item into clones at the specified index
580
+
581
+ if (renderReverse !== null && renderReverse !== "false" ) {
582
+ // Chat-specific logic: reverse rendering but still inserting based on sorting
583
+ if (clones[index + 1] && clones[index + 1][1] !== element) {
584
+ clones[index + 1][1].insertAdjacentElement("afterend", element);
585
+ } else if (clones[index] && clones[index][1] !== element) {
586
+ clones[index][1].insertAdjacentElement("beforeend", element);
587
+ } else {
588
+ template.element.insertAdjacentElement("afterend", element);
589
+ }
590
+ } else {
591
+ // Non-chat logic: handle ascending or descending order as usual
592
+ if (clones[index + 1] && clones[index + 1][1] !== element) {
593
+ clones[index + 1][1].insertAdjacentElement(
594
+ "beforebegin",
595
+ element
596
+ );
597
+ } else if (clones[index] && clones[index][1] !== element) {
598
+ clones[index][1].insertAdjacentElement("afterend", element);
599
+ } else {
600
+ template.element.insertAdjacentElement("beforebegin", element);
601
+ }
602
+ }
603
+
604
+ template.clones = new Map(clones);
605
+ } else {
606
+ template.clones.set(eid, element);
607
+ template.element.insertAdjacentElement("beforebegin", element);
608
+ }
624
609
  }
625
610
 
626
611
  async function renderValues(node, data, key, renderAs, keyPath, parent, index) {
627
- if (!data) return;
628
-
629
- let renderedNode = renderedNodes.get(node);
630
- if (!renderedNode) renderedNode = { key, renderAs };
631
-
632
- if (!renderedNode.key) renderedNode.key = key;
633
- else if (!key) key = renderedNode.key;
634
-
635
- if (!renderedNode.renderAs) renderedNode.renderAs = renderAs;
636
- else if (!renderAs) renderAs = renderedNode.renderAs;
637
-
638
- if (keyPath) renderedNode.keyPath = keyPath;
639
- else if (renderedNode.keyPath) keyPath = renderedNode.keyPath;
640
-
641
- if (parent) renderedNode.parent = parent;
642
-
643
- if (node.nodeType == 1) {
644
- if (node.hasAttribute("render-clone")) {
645
- parent = renderedNode;
646
-
647
- for (let eid of ["_id", "name", "key"]) {
648
- if (renderAs) eid = data[renderAs][eid];
649
- else eid = data[eid];
650
-
651
- if (!eid) continue;
652
-
653
- let oldEid = renderedNode.eid;
654
- let temp = renderedNode.template;
655
- if (!temp) {
656
- console.log("temp could not be found");
657
- } else if (!temp.clones) {
658
- if (temp.template) temp = temp.template;
659
- else console.log(temp);
660
-
661
- if (oldEid && oldEid !== eid) temp.clones.delete(oldEid);
662
-
663
- temp.clones.set(eid, node);
664
- }
665
-
666
- renderedNode.eid = eid;
667
-
668
- node.setAttribute("eid", eid);
669
- break;
670
- }
671
- }
672
-
673
- // Array.from(node.attributes).forEach(attr => {
674
- for (let attr of node.attributes) {
675
- let name = attr.name;
676
- let value = attr.value;
677
-
678
- let renderedAttribute = renderedNodes.get(attr);
679
- if (!renderedAttribute) {
680
- renderedAttribute = {
681
- placeholder: { name, value },
682
- key,
683
- renderAs,
684
- parent: renderedNode
685
- };
686
- }
687
-
688
- let namePlaceholder = renderedAttribute.placeholder.name || name;
689
- let valuePlaceholder = renderedAttribute.placeholder.value || value;
690
-
691
- name = await renderValue(
692
- attr,
693
- data,
694
- namePlaceholder,
695
- renderAs,
696
- renderedAttribute,
697
- index
698
- );
699
- value = await renderValue(
700
- attr,
701
- data,
702
- valuePlaceholder,
703
- renderAs,
704
- renderedAttribute,
705
- index
706
- );
707
- if (namePlaceholder.includes("{{") && name) {
708
- const attributes = name.match(/([^\s]+="[^"]*"|[^\s]+)/g) || [];
709
- attributes.forEach((attr) => {
710
- let [attributeName, attributeValue] = attr.split("=");
711
- attributeValue = attributeValue
712
- ? attributeValue.replace(/"/g, "")
713
- : "";
714
- node.setAttribute(attributeName, attributeValue);
715
- });
716
- } else if (name === undefined || name === null || name === "") {
717
- renderedNodes.delete(attr);
718
- node.removeAttribute(attr.name);
719
- } else if (
720
- name &&
721
- (value || value === "") &&
722
- (name !== attr.name || value !== attr.value)
723
- )
724
- node.setAttribute(name, value);
725
- }
726
- // });
727
-
728
- if (CoCreate.state) {
729
- if (node.hasAttribute("[state_id]"))
730
- CoCreate.state.initElement(node);
731
- }
732
-
733
- if (node.tagName === "SCRIPT" && node.src) {
734
- if (
735
- node.src.includes("CoCreate.js") ||
736
- node.src.includes("CoCreate.min.js")
737
- ) {
738
- node.remove();
739
- return;
740
- }
741
- }
742
-
743
- if (node.tagName === "LINK" && node.href) {
744
- if (
745
- node.href.includes("CoCreate.css") ||
746
- node.href.includes("CoCreate.min.css")
747
- ) {
748
- node.remove();
749
- return;
750
- }
751
- }
752
- if (node.hasAttribute("render") && !node.hasAttribute("render-clone")) {
753
- let renderKey = node.getAttribute("render") || key
754
- renderTemplate(node, data, renderKey);
755
- } else if (node.childNodes.length > 0) {
756
- // Array.from(node.childNodes).forEach(childNode => {
757
- for (let childNode of node.childNodes) {
758
- await renderValues(
759
- childNode,
760
- data,
761
- key,
762
- renderAs,
763
- keyPath,
764
- parent,
765
- index
766
- );
767
- }
768
- // });
769
- }
770
- } else if (node.nodeType == 3) {
771
- let valueType = node.parentElement.getAttribute("value-type");
772
-
773
- let textContent, text;
774
-
775
- if (!renderedNode.placeholder && node.textContent.match(/{{(.*?)}}/))
776
- renderedNode.placeholder = node.textContent;
777
-
778
- if (!renderedNode.placeholder) return;
779
- textContent = renderedNode.placeholder || node.textContent;
780
-
781
- text = await renderValue(
782
- node,
783
- data,
784
- textContent,
785
- renderAs,
786
- renderedNode,
787
- index
788
- );
789
-
790
- if (text || text == "") {
791
- if (text != renderedNode.text) {
792
- renderedNode.text = text;
793
-
794
- if (
795
- valueType == "text" ||
796
- valueType == "string" ||
797
- valueType != "html"
798
- ) {
799
- node.textContent = text;
800
- } else {
801
- const newNode = document.createElement("div");
802
- newNode.innerHTML = text;
803
- let parentElement = node.parentElement;
804
-
805
- let renderedParent = renderedNodes.get(parentElement);
806
- if (!renderedParent) {
807
- renderedParent = {
808
- placeholder: textContent,
809
- key,
810
- renderAs
811
- };
812
- }
813
- renderedParent.text = text;
814
- for (let newChild of newNode.childNodes) {
815
- renderedNode.element = newChild;
816
- renderedNodes.set(newChild, renderedNode);
817
- }
818
- renderedNodes.delete(node);
819
- node.replaceWith(...newNode.childNodes);
820
- }
821
-
822
- if (node.childNodes.length > 0) {
823
- // Array.from(node.childNodes).forEach(childNode => {
824
- for (let childNode of node.childNodes) {
825
- await renderValues(
826
- childNode,
827
- data,
828
- key,
829
- renderAs,
830
- keyPath,
831
- parent,
832
- index
833
- );
834
- }
835
- // });
836
- }
837
- }
838
- }
839
- }
612
+ if (!data) return;
613
+
614
+ let renderedNode = renderedNodes.get(node);
615
+ if (!renderedNode) renderedNode = { key, renderAs };
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;
625
+
626
+ if (parent) renderedNode.parent = parent;
627
+
628
+ if (node.nodeType == 1) {
629
+ if (node.hasAttribute("render-clone")) {
630
+ parent = renderedNode;
631
+
632
+ for (let eid of ["_id", "name", "key"]) {
633
+ if (renderAs) eid = data[renderAs][eid];
634
+ else eid = data[eid];
635
+
636
+ if (!eid) continue;
637
+
638
+ let oldEid = renderedNode.eid;
639
+ let temp = renderedNode.template;
640
+ if (!temp) {
641
+ console.log("temp could not be found");
642
+ } else if (!temp.clones) {
643
+ if (temp.template) temp = temp.template;
644
+ else console.log(temp);
645
+
646
+ if (oldEid && oldEid !== eid) temp.clones.delete(oldEid);
647
+
648
+ temp.clones.set(eid, node);
649
+ }
650
+
651
+ renderedNode.eid = eid;
652
+
653
+ node.setAttribute("eid", eid);
654
+ break;
655
+ }
656
+ }
657
+
658
+ for (let attr of node.attributes) {
659
+ let name = attr.name;
660
+ let value = attr.value;
661
+
662
+ let renderedAttribute = renderedNodes.get(attr);
663
+ if (!renderedAttribute) {
664
+ renderedAttribute = {
665
+ placeholder: { name, value },
666
+ key,
667
+ renderAs,
668
+ parent: renderedNode
669
+ };
670
+ }
671
+
672
+ let namePlaceholder = renderedAttribute.placeholder.name || name;
673
+ let valuePlaceholder = renderedAttribute.placeholder.value || value;
674
+
675
+ name = await renderValue(
676
+ attr,
677
+ data,
678
+ namePlaceholder,
679
+ renderAs,
680
+ renderedAttribute,
681
+ index
682
+ );
683
+ value = await renderValue(
684
+ attr,
685
+ data,
686
+ valuePlaceholder,
687
+ renderAs,
688
+ renderedAttribute,
689
+ index
690
+ );
691
+ if (namePlaceholder.includes("{{") && name) {
692
+ const attributes = name.match(/([^\s]+="[^"]*"|[^\s]+)/g) || [];
693
+ attributes.forEach((attr) => {
694
+ let [attributeName, attributeValue] = attr.split("=");
695
+ attributeValue = attributeValue
696
+ ? attributeValue.replace(/"/g, "")
697
+ : "";
698
+ node.setAttribute(attributeName, attributeValue);
699
+ });
700
+ } else if (name === undefined || name === null || name === "") {
701
+ renderedNodes.delete(attr);
702
+ node.removeAttribute(attr.name);
703
+ } else if (
704
+ name &&
705
+ (value || value === "") &&
706
+ (name !== attr.name || value !== attr.value)
707
+ )
708
+ node.setAttribute(name, value);
709
+ }
710
+
711
+ if (CoCreate.state) {
712
+ if (node.hasAttribute("[state_id]"))
713
+ CoCreate.state.initElement(node);
714
+ }
715
+
716
+ if (node.tagName === "SCRIPT" && node.src) {
717
+ if (
718
+ node.src.includes("CoCreate.js") ||
719
+ node.src.includes("CoCreate.min.js")
720
+ ) {
721
+ node.remove();
722
+ return;
723
+ }
724
+ }
725
+
726
+ if (node.tagName === "LINK" && node.href) {
727
+ if (
728
+ node.href.includes("CoCreate.css") ||
729
+ node.href.includes("CoCreate.min.css")
730
+ ) {
731
+ node.remove();
732
+ return;
733
+ }
734
+ }
735
+ if (node.hasAttribute("render") && !node.hasAttribute("render-clone")) {
736
+ 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) {
740
+ await renderValues(
741
+ childNode,
742
+ data,
743
+ key,
744
+ renderAs,
745
+ keyPath,
746
+ parent,
747
+ index
748
+ );
749
+ }
750
+ }
751
+ } else if (node.nodeType == 3) {
752
+ let valueType = node.parentElement.getAttribute("value-type");
753
+
754
+ let textContent, text;
755
+
756
+ if (!renderedNode.placeholder && node.textContent.match(/{{(.*?)}}/))
757
+ renderedNode.placeholder = node.textContent;
758
+
759
+ if (!renderedNode.placeholder) return;
760
+ textContent = renderedNode.placeholder || node.textContent;
761
+
762
+ text = await renderValue(
763
+ node,
764
+ data,
765
+ textContent,
766
+ renderAs,
767
+ renderedNode,
768
+ index
769
+ );
770
+
771
+ if (text || text == "") {
772
+ if (text != renderedNode.text) {
773
+ renderedNode.text = text;
774
+
775
+ if (
776
+ valueType == "text" ||
777
+ valueType == "string" ||
778
+ valueType != "html"
779
+ ) {
780
+ node.textContent = text;
781
+ } else {
782
+ const newNode = document.createElement("div");
783
+ newNode.innerHTML = text;
784
+ let parentElement = node.parentElement;
785
+
786
+ let renderedParent = renderedNodes.get(parentElement);
787
+ if (!renderedParent) {
788
+ renderedParent = {
789
+ placeholder: textContent,
790
+ key,
791
+ renderAs
792
+ };
793
+ }
794
+ renderedParent.text = text;
795
+ for (let newChild of newNode.childNodes) {
796
+ renderedNode.element = newChild;
797
+ renderedNodes.set(newChild, renderedNode);
798
+ }
799
+ renderedNodes.delete(node);
800
+ node.replaceWith(...newNode.childNodes);
801
+ }
802
+
803
+ if (node.childNodes.length > 0) {
804
+ for (let childNode of node.childNodes) {
805
+ await renderValues(
806
+ childNode,
807
+ data,
808
+ key,
809
+ renderAs,
810
+ keyPath,
811
+ parent,
812
+ index
813
+ );
814
+ }
815
+ }
816
+ }
817
+ }
818
+ }
840
819
  }
841
820
 
842
821
  async function renderValue(
843
- node,
844
- data,
845
- placeholder,
846
- renderAs,
847
- renderedNode,
848
- index
822
+ node,
823
+ data,
824
+ placeholder,
825
+ renderAs,
826
+ renderedNode,
827
+ index
849
828
  ) {
850
- let output = placeholder;
851
-
852
- // Updated regex to match {{ }}, {[ ]}, {( )}
853
- let regex = /\{\{([^{}]+?)\}\}|\{\[([^{}\[\]]+?)\]\}|\{\(([^{}()]+?)\)\}/;
854
- let omitted = {};
855
-
856
- let match;
857
- do {
858
- match = output.match(regex);
859
- if (match) {
860
- let value;
861
- let placeholderContent;
862
- let placeholderType;
863
-
864
- try {
865
- // Initialize placeholderContent and placeholderType for each match
866
- if (match[1] !== undefined) {
867
- // Matched {{ }}
868
- placeholderContent = match[1];
869
- placeholderType = "doubleCurly";
870
- } else if (match[2] !== undefined) {
871
- // Matched {[ ]}
872
- placeholderContent = match[2];
873
- placeholderType = "squareBrackets";
874
- } else if (match[3] !== undefined) {
875
- // Matched {( )}
876
- placeholderContent = match[3];
877
- placeholderType = "parentheses";
878
- }
879
-
880
- // Process based on the placeholder type
881
- if (placeholderContent === "index" && index >= 0) {
882
- // Handle {{index}} - index
883
- value = index;
884
- } else if (placeholderType === "squareBrackets") {
885
- // {[ ]} - Dot-notation
886
- value = getRenderValue(
887
- node,
888
- data,
889
- placeholderContent,
890
- renderAs
891
- );
892
- } else if (placeholderType === "parentheses") {
893
- // If using JSON Structure:
894
- try {
895
- let Data = JSON.parse(
896
- placeholderContent.replace(/'/g, '"')
897
- );
898
- if (
899
- Data.storage ||
900
- Data.database ||
901
- Data.array ||
902
- Data.object ||
903
- Data.index
904
- ) {
905
- Data.method = "object.read";
906
- let response = await CoCreate.crud.send(Data);
907
- value = response.object[0][Data.key];
908
- }
909
- } catch (error) {
910
- // If parsing fails, you can handle it as needed
911
- // {( )} - CSS Selector and JSON Structure
912
- // For CSS Selector handling, implement your logic here
913
- // Example:
914
-
915
- let valueElement = queryElements({
916
- selector: placeholderContent
917
- });
918
- if (valueElement && valueElement[0])
919
- value = valueElement[0].getValue();
920
-
921
- // For now, we'll omit the placeholder
922
- omitted[`<|${placeholderContent}|>`] = match[0];
923
- }
924
- } else if (placeholderType === "doubleCurly") {
925
- // {{ }} - Dot-notation and JSON Structure
926
- try {
927
- let Data = JSON.parse(
928
- placeholderContent.replace(/'/g, '"')
929
- );
930
- if (
931
- Data.storage ||
932
- Data.database ||
933
- Data.array ||
934
- Data.object ||
935
- Data.index
936
- ) {
937
- Data.method = "object.read";
938
- let response = await CoCreate.crud.send(Data);
939
- value = response.object[0][Data.key];
940
- }
941
- } catch (error) {
942
- // Treat as dot-notation
943
- value = getRenderValue(
944
- node,
945
- data,
946
- placeholderContent,
947
- renderAs
948
- );
949
- }
950
- } else {
951
- // If none of the conditions match, store for omission
952
- omitted[`<|${placeholderContent}|>`] = match[0];
953
- }
954
- } catch (error) {
955
- console.error(error);
956
- }
957
-
958
- // Replace the placeholder with the resolved value
959
- if (value !== undefined && value !== null) {
960
- if (typeof value === "object") {
961
- value = JSON.stringify(value, null, 2);
962
- }
963
- output = output.replaceAll(match[0], value);
964
- } else if (renderAs) {
965
- if (match[0].startsWith(`{{${renderAs}.`)) {
966
- output = output.replaceAll(match[0], "");
967
- } else if (placeholderContent) {
968
- output = output.replaceAll(
969
- match[0],
970
- `<|${placeholderContent}|>`
971
- );
972
- omitted[`<|${placeholderContent}|>`] = match[0];
973
- }
974
- } else {
975
- // If no value is found and no renderAs, exit the loop
976
- match = null;
977
- }
978
- }
979
- } while (match);
980
-
981
- // Restore any omitted placeholders
982
- for (let key of Object.keys(omitted)) {
983
- output = output.replaceAll(key, omitted[key]);
984
- }
985
-
986
- return output;
829
+ let output = placeholder;
830
+
831
+ // Determine the actual HTMLElement for processOperatorsAsync
832
+ // If the node is not an Element (nodeType 1), get its owner or parent element.
833
+ // This safely covers Attributes (ownerElement), Text, Comments, etc. (parentElement)
834
+ let contextElement = node;
835
+ if (node && node.nodeType !== 1) {
836
+ contextElement = node.ownerElement || node.parentElement;
837
+ }
838
+
839
+ // DEPRECATED: {[ ]} and {( )}
840
+ // Matches ONLY the innermost {{ }} brackets.
841
+ let regex = /\{\{((?:(?!\{\{|\}\})[\s\S])+?)\}\}/;
842
+ let omitted = {};
843
+
844
+ let match;
845
+ do {
846
+ match = output.match(regex);
847
+ if (match) {
848
+ let value;
849
+ let placeholderContent = match[1].trim();
850
+
851
+ try {
852
+ if (placeholderContent === "index" && index >= 0) {
853
+ value = index;
854
+ } else {
855
+ // --- UNIFIED PARSING PROCESS ---
856
+ let preProcessed = placeholderContent;
857
+
858
+ // 1. PRE-PROCESSOR FOR QUOTED OPERATORS
859
+ let quoteRegex = /(['"])(.*?)\1/g;
860
+ let quoteMatch;
861
+ let replacements = [];
862
+ while ((quoteMatch = quoteRegex.exec(preProcessed)) !== null) {
863
+ let quoteType = quoteMatch[1];
864
+ let content = quoteMatch[2];
865
+ if (content.trim().startsWith('$')) {
866
+ let resolved = await processOperatorsAsync(contextElement, content);
867
+ if (resolved === null || resolved === undefined) resolved = "";
868
+ replacements.push({
869
+ original: quoteMatch[0],
870
+ replacement: `${quoteType}${resolved}${quoteType}`
871
+ });
872
+ }
873
+ }
874
+
875
+ for (let r of replacements) {
876
+ preProcessed = preProcessed.replace(r.original, r.replacement);
877
+ }
878
+
879
+ // 2. PRE-PROCESSOR: Resolve unquoted DOM selectors, custom operators, and CRUD JSON
880
+ preProcessed = await processOperatorsAsync(
881
+ contextElement,
882
+ preProcessed
883
+ );
884
+
885
+ // 3. DATA LOOKUP: Resolve simple keys from the provided data object
886
+ // Keep `node` here because getRenderValue needs to look up the attribute cache!
887
+ value = getRenderValue(
888
+ node,
889
+ data,
890
+ preProcessed,
891
+ renderAs
892
+ );
893
+
894
+ // 4. EXPRESSION EVALUATOR: Handle Math and Ternary logic
895
+ if (value === undefined || value === null) {
896
+ const dataProxy = new Proxy(data || {}, {
897
+ get(target, prop) {
898
+ if (typeof prop !== "string") return target[prop];
899
+ if (prop in target) return target[prop];
900
+ return getRenderValue(node, data, prop, renderAs);
901
+ },
902
+ has(target, prop) {
903
+ if (typeof prop !== "string") return prop in target;
904
+ return true;
905
+ }
906
+ });
907
+
908
+ const parsedValue = safeParse(preProcessed, dataProxy);
909
+
910
+ if (parsedValue !== null) {
911
+ value = parsedValue;
912
+ } else if (preProcessed !== placeholderContent) {
913
+ value = preProcessed;
914
+ }
915
+ }
916
+ }
917
+ } catch (error) {
918
+ console.error(error);
919
+ }
920
+
921
+ // Replace the placeholder with the resolved value
922
+ if (value !== undefined && value !== null) {
923
+ if (typeof value === "object") {
924
+ value = JSON.stringify(value, null, 2);
925
+ }
926
+ output = output.replaceAll(match[0], value);
927
+ } else if (renderAs) {
928
+ if (match[0].startsWith(`{{${renderAs}.`)) {
929
+ output = output.replaceAll(match[0], "");
930
+ } else if (placeholderContent) {
931
+ output = output.replaceAll(
932
+ match[0],
933
+ `<|${placeholderContent}|>`
934
+ );
935
+ omitted[`<|${placeholderContent}|>`] = match[0];
936
+ }
937
+ } else {
938
+ match = null;
939
+ }
940
+ }
941
+ } while (match);
942
+
943
+ for (let key of Object.keys(omitted)) {
944
+ output = output.replaceAll(key, omitted[key]);
945
+ }
946
+
947
+ return output;
987
948
  }
988
949
 
989
950
  function getRenderValue(node, data, key, renderAs) {
990
- // TODO: handle operators directly from render placholders
991
- // <span>{{ $toLocaleDateString.object.created.on }}</span>
992
-
993
- let operator;
994
- if (key.startsWith("$")) {
995
- let keys = key.split(".");
996
- operator = keys.shift();
997
- if (keys.length > 0) {
998
- key = keys.join(".");
999
- }
1000
- }
1001
-
1002
- let value = getValueFromObject(data, key);
1003
-
1004
- if (!value && value !== "" && node) {
1005
- let parentTemplate = node;
1006
- do {
1007
- let parentNode = renderedNodes.get(parentTemplate);
1008
- if (parentNode) {
1009
- if (!value && (parentNode.parent || parentNode.template)) {
1010
- let Data,
1011
- eid,
1012
- parent = parentNode.parent || parentNode.template;
1013
- do {
1014
- if (key.includes("keyPath")) {
1015
- value = parentNode.keyPath || parent.keyPath;
1016
- } else if (key.includes("parentKey")) {
1017
- value =
1018
- parentNode.parentKey ||
1019
- parent.parentKey ||
1020
- parent.parent.parentKey;
1021
- } else if (key.includes("renderAs")) {
1022
- value = renderAs;
1023
- } else if (key === "object" || key === "_id")
1024
- value = ObjectId().toString();
1025
- else if (key === "uuid") {
1026
- value = uuid.generate(6);
1027
- } else if (parent.source) {
1028
- Data = parent.source.data;
1029
- } else if (parent.parent) {
1030
- parent = parent.parent;
1031
- } else if (parent.template) {
1032
- parent = parent.template;
1033
- } else {
1034
- parent = undefined;
1035
- }
1036
-
1037
- if (!Data && parent && parent.eid) eid = parent.eid;
1038
- } while (!value && parent && !Data);
1039
-
1040
- if (!value && Data) {
1041
- value = getValueFromObject(Data, key);
1042
- }
1043
- if (!value && key.includes(".")) {
1044
- let renderedData = getValueFromObject(
1045
- Data,
1046
- key.split(".")[0]
1047
- );
1048
- if (renderedData) {
1049
- let index;
1050
- if (
1051
- !parent.clones.size ||
1052
- parent.clones.size === 1
1053
- ) {
1054
- index = 0;
1055
- } else if (eid) {
1056
- index = Array.from(
1057
- parent.clones.keys()
1058
- ).indexOf(eid);
1059
- } else console.log("eid not found");
1060
-
1061
- if (index >= 0) {
1062
- Data = {
1063
- [key.split(".")[0]]: renderedData[index]
1064
- };
1065
- }
1066
- value = getValueFromObject(Data, key);
1067
- }
1068
- }
1069
- }
1070
- }
1071
-
1072
- if (!value && parentTemplate.parentElement) {
1073
- parentTemplate =
1074
- parentTemplate.parentElement.closest("[render]");
1075
- } else {
1076
- parentTemplate = undefined;
1077
- }
1078
- } while (!value && parentTemplate);
1079
- }
1080
-
1081
- if (operator) {
1082
- if (value) {
1083
- console.log("return processed render value using operator", value);
1084
- } else {
1085
- console.log("return render operator", operator);
1086
- }
1087
- }
1088
-
1089
- return value;
951
+ let operator;
952
+ if (key.startsWith("$")) {
953
+ let keys = key.split(".");
954
+ operator = keys.shift();
955
+ if (keys.length > 0) {
956
+ key = keys.join(".");
957
+ }
958
+ }
959
+
960
+ let value = getValueFromObject(data, key);
961
+
962
+ if (!value && value !== "" && node) {
963
+ let parentTemplate = node;
964
+ do {
965
+ let parentNode = renderedNodes.get(parentTemplate);
966
+ if (parentNode) {
967
+ if (!value && (parentNode.parent || parentNode.template)) {
968
+ let Data,
969
+ eid,
970
+ parent = parentNode.parent || parentNode.template;
971
+ do {
972
+ if (key.includes("keyPath")) {
973
+ value = parentNode.keyPath || parent.keyPath;
974
+ } else if (key.includes("parentKey")) {
975
+ value =
976
+ parentNode.parentKey ||
977
+ parent.parentKey ||
978
+ parent.parent.parentKey;
979
+ } else if (key.includes("renderAs")) {
980
+ value = renderAs;
981
+ } else if (key === "object" || key === "_id")
982
+ value = ObjectId().toString();
983
+ else if (key === "uuid") {
984
+ value = uuid.generate(6);
985
+ } else if (parent.source) {
986
+ Data = parent.source.data;
987
+ } else if (parent.parent) {
988
+ parent = parent.parent;
989
+ } else if (parent.template) {
990
+ parent = parent.template;
991
+ } else {
992
+ parent = undefined;
993
+ }
994
+
995
+ if (!Data && parent && parent.eid) eid = parent.eid;
996
+ } while (!value && parent && !Data);
997
+
998
+ if (!value && Data) {
999
+ value = getValueFromObject(Data, key);
1000
+ }
1001
+ if (!value && key.includes(".")) {
1002
+ let renderedData = getValueFromObject(
1003
+ Data,
1004
+ key.split(".")[0]
1005
+ );
1006
+ if (renderedData) {
1007
+ let index;
1008
+ if (
1009
+ !parent.clones.size ||
1010
+ parent.clones.size === 1
1011
+ ) {
1012
+ index = 0;
1013
+ } else if (eid) {
1014
+ index = Array.from(
1015
+ parent.clones.keys()
1016
+ ).indexOf(eid);
1017
+ } else console.log("eid not found");
1018
+
1019
+ if (index >= 0) {
1020
+ Data = {
1021
+ [key.split(".")[0]]: renderedData[index]
1022
+ };
1023
+ }
1024
+ value = getValueFromObject(Data, key);
1025
+ }
1026
+ }
1027
+ }
1028
+ }
1029
+
1030
+ if (!value && parentTemplate.parentElement) {
1031
+ parentTemplate =
1032
+ parentTemplate.parentElement.closest("[render]");
1033
+ } else {
1034
+ parentTemplate = undefined;
1035
+ }
1036
+ } while (!value && parentTemplate);
1037
+ }
1038
+
1039
+ if (operator) {
1040
+ if (value) {
1041
+ console.log("return processed render value using operator", value);
1042
+ } else {
1043
+ console.log("return render operator", operator);
1044
+ }
1045
+ }
1046
+
1047
+ return value;
1090
1048
  }
1091
1049
 
1092
1050
  async function renderKey(action) {
1093
- // TODO: custom render-keys
1094
- let element = action.element;
1095
- let form = action.form || document;
1096
- let params = action.params;
1097
-
1098
- if (!params) {
1099
- params = "render-key";
1100
- }
1101
-
1102
- let data = {};
1103
- let selector = `[${params}]`;
1104
- let elements = form.querySelectorAll(selector);
1105
- for (let el of elements) {
1106
- let attribute = el.getAttribute(params);
1107
- if (attribute) data[attribute] = await el.getValue();
1108
- }
1109
- data = dotNotationToObject(data);
1110
- let renderData = { data: { [params]: data } };
1111
-
1112
- let renderSelector = element.getAttribute("render-query");
1113
- if (!renderSelector) return;
1114
-
1115
- renderData.selector = renderSelector;
1116
- render(renderData);
1117
-
1118
- action.element.dispatchEvent(
1119
- new CustomEvent("renderKey", {
1120
- detail: { data }
1121
- })
1122
- );
1051
+ // TODO: custom render-keys
1052
+ let element = action.element;
1053
+ let form = action.form || document;
1054
+ let params = action.params;
1055
+
1056
+ if (!params) {
1057
+ params = "render-key";
1058
+ }
1059
+
1060
+ let data = {};
1061
+ let selector = `[${params}]`;
1062
+ let elements = form.querySelectorAll(selector);
1063
+ for (let el of elements) {
1064
+ let attribute = el.getAttribute(params);
1065
+ if (attribute) data[attribute] = await el.getValue();
1066
+ }
1067
+ data = dotNotationToObject(data);
1068
+ let renderData = { data: { [params]: data } };
1069
+
1070
+ let renderSelector = element.getAttribute("render-query");
1071
+ if (!renderSelector) return;
1072
+
1073
+ renderData.selector = renderSelector;
1074
+ render(renderData);
1075
+
1076
+ action.element.dispatchEvent(
1077
+ new CustomEvent("renderKey", {
1078
+ detail: { data }
1079
+ })
1080
+ );
1123
1081
  }
1124
1082
 
1125
1083
  Actions.init({
1126
- name: "renderKey",
1127
- callback: (action) => {
1128
- renderKey(action);
1129
- }
1084
+ name: "renderKey",
1085
+ callback: (action) => {
1086
+ renderKey(action);
1087
+ }
1130
1088
  });
1131
1089
 
1132
1090
  Actions.init({
1133
- name: "render",
1134
- callback: async (action) => {
1135
- if (!action.form) return;
1136
-
1137
- let elements;
1138
- if (action.params)
1139
- elements = queryElements({
1140
- element: action.element,
1141
- selector: action.params,
1142
- type: "selector"
1143
- });
1144
- else
1145
- elements = queryElements({
1146
- element: action.element,
1147
- prefix: "render"
1148
- });
1149
-
1150
- // TODO: data returned from form.getData() returns an array of objects each representing an storage, database and array in form. need a solution for handling what is the item to be rendered
1151
- let data = await action.form.getData();
1152
- let array = action.form.getAttribute("array");
1153
- if (array) data = data.find((obj) => obj.array === array);
1154
- for (let i = 0; i < elements.length; i++)
1155
- await render({ source: elements[i], data });
1156
-
1157
- action.element.dispatchEvent(
1158
- new CustomEvent("render", {
1159
- detail: {}
1160
- })
1161
- );
1162
- }
1091
+ name: "render",
1092
+ callback: async (action) => {
1093
+ if (!action.form) return;
1094
+
1095
+ let elements;
1096
+ if (action.params)
1097
+ elements = queryElements({
1098
+ element: action.element,
1099
+ selector: action.params,
1100
+ type: "selector"
1101
+ });
1102
+ else
1103
+ elements = queryElements({
1104
+ element: action.element,
1105
+ prefix: "render"
1106
+ });
1107
+
1108
+ let data = await action.form.getData();
1109
+ let array = action.form.getAttribute("array");
1110
+ if (array) data = data.find((obj) => obj.array === array);
1111
+ for (let i = 0; i < elements.length; i++)
1112
+ await render({ source: elements[i], data });
1113
+
1114
+ action.element.dispatchEvent(
1115
+ new CustomEvent("render", {
1116
+ detail: {}
1117
+ })
1118
+ );
1119
+ }
1163
1120
  });
1164
1121
 
1165
1122
  Observer.init({
1166
- name: "render",
1167
- types: ["addedNodes"],
1168
- selector: elementSelector,
1169
- callback: function (mutation) {
1170
- init(mutation.target);
1171
- }
1123
+ name: "render",
1124
+ types: ["addedNodes"],
1125
+ selector: elementSelector,
1126
+ callback: function (mutation) {
1127
+ init(mutation.target);
1128
+ }
1172
1129
  });
1173
1130
 
1174
1131
  Observer.init({
1175
- name: "fileRender",
1176
- types: ["attributes"],
1177
- attributeFilter: ["render-query"],
1178
- callback: async function (mutation) {
1179
- render({
1180
- element: mutation.target,
1181
- data: await mutation.target.getValue()
1182
- });
1183
- }
1132
+ name: "fileRender",
1133
+ types: ["attributes"],
1134
+ attributeFilter: ["render-query"],
1135
+ callback: async function (mutation) {
1136
+ render({
1137
+ element: mutation.target,
1138
+ data: await mutation.target.getValue()
1139
+ });
1140
+ }
1184
1141
  });
1185
1142
 
1186
1143
  Observer.init({
1187
- name: "render",
1188
- types: ["addedNodes"],
1189
- selector: "[render]",
1190
- callback: function (mutation) {
1191
- if (mutation.target.hasAttribute("render-clone")) return;
1192
- let parentElement = mutation.target.parentElement.closest("[render]");
1193
- if (!parentElement) return;
1194
-
1195
- let renderedNode = renderedNodes.get(parentElement);
1196
- let data;
1197
- if (renderedNode) {
1198
- if (renderedNode.renderData) {
1199
- data = renderedNode.renderData;
1200
- }
1201
- } else {
1202
- let sourceElement = mutation.target.parentElement.closest("[render-query]")
1203
- let sourceData = sources.get(sourceElement);
1204
- if (sourceData) {
1205
- data = sourceData.data;
1206
- }
1207
- }
1208
- if (data) {
1209
- // render({ element: mutation.target, data });
1210
- }
1211
- }
1144
+ name: "render",
1145
+ types: ["addedNodes"],
1146
+ selector: "[render-element]",
1147
+ callback: function (mutation) {
1148
+ if (mutation.target.hasAttribute("render-clone")) return;
1149
+ let parentElement = mutation.target.parentElement.closest("[render]");
1150
+ if (!parentElement) return;
1151
+
1152
+ let renderedNode = renderedNodes.get(parentElement);
1153
+ let data;
1154
+ if (renderedNode) {
1155
+ if (renderedNode.renderData) {
1156
+ data = renderedNode.renderData;
1157
+ }
1158
+ } else {
1159
+ let sourceElement = mutation.target.parentElement.closest("[render-query]")
1160
+ let sourceData = sources.get(sourceElement);
1161
+ if (sourceData) {
1162
+ data = sourceData.data;
1163
+ }
1164
+ }
1165
+ if (data) {
1166
+ render({ element: mutation.target, data });
1167
+ }
1168
+ }
1212
1169
  });
1213
1170
 
1214
- // Observer.init({
1215
- // name: 'render',
1216
- // types: ['addedNodes'],
1217
- // selector: '[render-clone]',
1218
- // callback: function (mutation) {
1219
- // let renderedNode = renderedNodes.get(mutation.target)
1220
- // if (!renderedNode) return
1221
-
1222
- // render({ source, element, data, key, index, currentIndex, update, remove })
1223
-
1224
- // let nextElement = mutation.target.nextElementSibling
1225
- // if (!nextElement) return
1226
-
1227
- // let nextRenderedNode = renderedNodes.get(nextElement)
1228
- // if (!nextRenderedNode) return
1229
-
1230
- // let clones
1231
- // if (nextRenderedNode.template)
1232
- // clones = nextRenderedNode.template.clones
1233
- // else if (nextRenderedNode.clones)
1234
- // clones = nextRenderedNode.clones
1235
-
1236
- // const cloneValueArray = Array.from(clones.values())
1237
- // let index = cloneValueArray.indexOf(nextElement);
1238
-
1239
- // const cloneArray = Array.from(clones)
1240
-
1241
- // cloneArray.splice(index, 0, [renderedNode.eid, mutation.target]);
1242
- // clones = new Map(cloneArray);
1243
-
1244
- // }
1245
- // })
1246
-
1247
1171
  Observer.init({
1248
- name: "renderNodesRemoved",
1249
- types: ["removedNodes"],
1250
- selector: "[render-clone]",
1251
- callback: function (mutation) {
1252
- if (mutation.target.parentElement) return;
1253
- let renderedNode = renderedNodes.get(mutation.target);
1254
- if (!renderedNode) return;
1255
- renderedNode.template.clones.delete(renderedNode.eid);
1256
- renderedNodes.delete(mutation.target);
1257
- }
1172
+ name: "renderNodesRemoved",
1173
+ types: ["removedNodes"],
1174
+ selector: "[render-clone]",
1175
+ callback: function (mutation) {
1176
+ if (mutation.target.parentElement) return;
1177
+ let renderedNode = renderedNodes.get(mutation.target);
1178
+ if (!renderedNode) return;
1179
+ renderedNode.template.clones.delete(renderedNode.eid);
1180
+ renderedNodes.delete(mutation.target);
1181
+ }
1258
1182
  });
1259
1183
 
1260
1184
  init();
1261
1185
 
1262
- export { render, renderValue, sources, renderedNodes };
1186
+ export { render, renderValue, sources, renderedNodes };