@cocreate/render 1.42.1 → 1.43.1

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