@cocreate/render 1.24.32 → 1.26.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
@@ -1,531 +1,744 @@
1
+ /********************************************************************************
2
+ * Copyright (C) 2023 CoCreate and Contributors.
3
+ *
4
+ * This program is free software: you can redistribute it and/or modify
5
+ * it under the terms of the GNU Affero General Public License as published
6
+ * by the Free Software Foundation, either version 3 of the License, or
7
+ * (at your option) any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful,
10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ * GNU Affero General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU Affero General Public License
15
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
16
+ ********************************************************************************/
17
+
18
+ // Commercial Licensing Information:
19
+ // For commercial use of this software without the copyleft provisions of the AGPLv3,
20
+ // you must obtain a commercial license from CoCreate LLC.
21
+ // For details, visit <https://cocreate.app/licenses/> or contact us at sales@cocreate.app.
22
+
1
23
  /*globals CustomEvent*/
2
- import action from '@cocreate/actions';
3
- import observer from '@cocreate/observer';
24
+ import Actions from '@cocreate/actions';
25
+ import Observer from '@cocreate/observer';
4
26
  import uuid from '@cocreate/uuid';
5
- import { queryDocumentSelector, getValueFromObject, dotNotationToObject } from '@cocreate/utils';
27
+ import { queryElements, getValueFromObject, dotNotationToObject, ObjectId } from '@cocreate/utils';
6
28
  import '@cocreate/element-prototype';
7
29
  import './index.css';
8
- // import api from '@cocreate/api';
9
-
10
- const CoCreateRender = {
11
-
12
- __getValue: function(data, attrValue, el) {
13
- let result = /{{\s*([\w\W]+)\s*}}/g.exec(attrValue);
14
- if (result) {
15
- let value = getValueFromObject(data, result[1].trim());
16
- if (!value && value !== '') {
17
- let parentTemplate
18
- if (el && el.nodeType == 1) {
19
- if (el.hasAttribute('templateid') && el.parentElement)
20
- parentTemplate = el.parentElement.closest('[templateid]')
21
- else
22
- parentTemplate = el.closest('[templateid]')
23
- }
24
-
25
- if (parentTemplate) {
26
- do {
27
- if (parentTemplate.renderedData)
28
- value = getValueFromObject(parentTemplate.renderedData, result[1].trim());
29
- if (!value && parentTemplate.parentElement)
30
- parentTemplate = parentTemplate.parentElement.closest('[templateid]')
31
- else
32
- parentTemplate = ""
33
- } while (!value && parentTemplate)
34
- }
35
- }
36
- return value;
37
- }
38
- return false;
39
- },
40
-
41
- __createObject: function (data, path) {
42
- try {
43
- if (!path) return data;
44
-
45
- let keys = path.split('.');
46
- let newObject = data;
47
-
48
- for (var i = keys.length - 1; i >= 0; i--) {
49
- newObject = {[keys[i]]: newObject};
50
- }
51
- return newObject;
52
-
53
- } catch (error) {
54
- console.log("Error in getValueFromObject", error);
55
- return false;
56
- }
57
- },
58
-
59
- __replaceValue: function(data, inputValue, renderKey, el) {
60
- let outputValue = inputValue;
61
- let placeholders = inputValue.match(/{{([A-Za-z0-9_.,\[\]\- ]*)}}/g);
62
- if (placeholders) {
63
- for (let placeholder of placeholders) {
64
- let value = this.__getValue(data, placeholder, el);
65
-
66
- if (value || value === "") {
67
- if (typeof(value) == "object")
68
- value = this.generateString(value)
69
-
70
- outputValue = outputValue.replace(placeholder, value);
71
- } else if (renderKey && placeholder.includes(`{{${renderKey}.`)) {
72
- outputValue = '';
73
- }
74
- }
75
- }
76
- return outputValue;
77
- },
78
-
79
- generateString: function(value, str = ''){
80
- // let str = '';
81
- // do {
82
- let flag = true;
83
- if (str)
84
- flag = false;
85
- for (const key of Object.keys(value)) {
86
- let val = value[key]
87
- if (typeof(val) == "object") {
88
- str += `${key}: \n`;
89
- this.generateString(val, str)
90
- }
91
- else
92
- str += `${key}: ${val}\n`;
93
- }
94
- // } while()
95
- if (flag)
96
- return str
97
- },
98
-
99
- render: function(template, data) {
100
- const self = this;
101
- let type = '';
102
- let arrayData = data;
103
-
104
- let exclude = template.getAttribute('render-exclude') || ''
105
- if (exclude) {
106
- exclude = exclude.replace(/ /g, '').split(",")
107
- if (!Array.isArray(exclude))
108
- exclude = [exclude]
109
- }
110
-
111
- const isRenderObject = template.hasAttribute('render-object');
112
- if (isRenderObject) {
113
- const renderObject = template.getAttribute('render-object');
114
- type = renderObject || ''
115
- }
116
-
117
- const isRenderArray = template.hasAttribute('render-array');
118
- if (isRenderArray){
119
- var renderArray = template.getAttribute('render-array');
120
- if (renderArray)
121
- arrayData = data[renderArray];
122
- type = renderArray || '';
123
- }
124
-
125
- let renderKey = template.getAttribute('render-key') || type;
126
-
127
- if (!template.renderedKeys)
128
- template.renderedKeys = new Map()
129
-
130
- if (isRenderObject && type) {
131
- let Data = getValueFromObject(arrayData, type);
132
- let array = self.renderObject(Data, renderKey, exclude)
133
- for (let item of array) {
134
- if (!template.renderedKeys.has(item[renderKey].key)){
135
- template.renderedKeys.set(item[renderKey].key, '')
136
- let cloneEl = this.cloneEl(template);
137
- cloneEl.setAttribute('renderedKey', item[renderKey].key)
138
- self.setValue([cloneEl], item, renderArray, renderKey);
139
- template.insertAdjacentElement('beforebegin', cloneEl);
140
- }
141
- }
142
- } else {
143
-
144
- if (!Array.isArray(arrayData))
145
- arrayData = getValueFromObject(data, type);
146
-
147
- if (!arrayData) {
148
- let cloneEl = this.cloneEl(template);
149
- self.setValue([cloneEl], data, renderArray, renderKey);
150
- template.insertAdjacentElement('beforebegin', cloneEl);
151
- }
152
-
153
- if (!Array.isArray(arrayData))
154
- arrayData = [arrayData]
155
-
156
- if (type && Array.isArray(arrayData)) {
157
- arrayData.forEach((item) => {
158
- if (!template.renderedKeys.has(item)){
159
- template.renderedKeys.set(item, '')
160
-
161
- let cloneEl = this.cloneEl(template);
162
- let object = self.__createObject(item, renderKey);
163
- self.setValue([cloneEl], object, renderArray, renderKey);
164
- template.insertAdjacentElement('beforebegin', cloneEl);
165
- }
166
- });
167
- }
168
- }
169
- },
170
-
171
- renderObject: function(data, renderKey, exclude) {
172
- let array = []
173
- if (!data)
174
- return array
175
- for (const key of Object.keys(data)) {
176
- let value = data[key]
177
- if (!exclude.includes(key)) {
178
- let type = 'string';
179
- if (typeof(value) == "object")
180
- if (Array.isArray(value))
181
- type = 'array'
182
- else
183
- type = 'object'
184
- array.push({[renderKey]: {key, value, type}})
185
- }
186
- }
187
- return array
188
- },
189
-
190
- cloneEl: function(template) {
191
- let cloneEl = template.cloneNode(true);
192
-
193
- let templateId = cloneEl.getAttribute('template_id');
194
- if (!templateId)
195
- templateId = cloneEl.getAttribute('template');
196
-
197
- if (templateId) {
198
- cloneEl.setAttribute('templateId', templateId);
199
- cloneEl.removeAttribute('template_id');
200
- cloneEl.removeAttribute('template');
201
- }
202
-
203
- if (cloneEl.tagName == 'TEMPLATE'){
204
- cloneEl = template.content.firstElementChild
205
- for (let attribute of template.attributes){
206
- let attrName = attribute.name;
207
- let attrValue = attribute.value || '';
208
- cloneEl.setAttribute(attrName, attrValue);
209
- }
210
- }
211
- else {
212
- cloneEl.classList.remove('template');
213
- cloneEl.removeAttribute('template');
214
- }
215
- cloneEl.setAttribute('render-clone', '');
216
-
217
- return cloneEl;
218
- },
219
-
220
- document_id: '',
221
- setValue: function(els, data, renderArray, renderKey){
222
- if (!data) return;
223
- let isRenderKey
224
- if (data.renderKey)
225
- isRenderKey = true
226
-
227
- const that = this;
228
- Array.from(els).forEach(el => {
229
- let updateData;
230
-
231
- if (el.nodeType == 1) {
232
- if (el.hasAttribute('render-clone')) {
233
- if (!el.renderedData)
234
- el.renderedData = {...data}
235
- else if (Object.keys(el.renderedData)[0] == Object.keys(data)[0]) {
236
- el.renderedData = {...data}
237
- }
238
-
239
- }
240
-
241
- if (el.renderMap && !isRenderKey) {
242
- let placeholder = el.renderMap.get(el)
243
- if (placeholder){
244
- renderKey = placeholder.renderKey
245
- renderArray = placeholder.renderArray
246
- if (renderArray && Array.isArray(data[renderArray]))
247
- updateData = data[renderArray][0]
248
- else if (data[renderArray])
249
- updateData = data[renderArray]
250
-
251
- if (renderKey) {
252
- if (updateData)
253
- updateData = {[renderKey]: updateData}
254
- else
255
- updateData = {[renderKey]: data}
256
- }
257
-
258
- let textContent = placeholder.placeholder
259
- let text = that.__replaceValue(updateData, textContent, renderKey, el);
260
- if (text && text != el.renderedValue)
261
- el.innerHTML = placeholder.placeholder
262
- }
263
- }
264
-
265
- Array.from(el.attributes).forEach(attr=>{
266
- let attr_name = attr.name.toLowerCase();
267
- let attrValue = attr.value;
268
- let placeholder
269
- if (attr.renderMap)
270
- placeholder = attr.renderMap.get(attr)
271
- else
272
- that.renderMap(attr, attr.value, renderArray, renderKey)
273
-
274
- if (placeholder && !isRenderKey){
275
- let temp = placeholder.placeholder;
276
- // let updateData
277
- // renderKey = placeholder.renderKey
278
- // renderArray = placeholder.renderArray
279
- // if (renderArray && Array.isArray(data[renderArray]))
280
- // updateData = data[renderArray][0]
281
- // else if (data[renderArray])
282
- // updateData = data[renderArray]
283
-
284
- // if (renderKey) {
285
- // if (updateData)
286
- // updateData = {[renderKey]: updateData}
287
- // else
288
- // updateData = {[renderKey]: data}
289
- // }
290
- if (updateData)
291
- attrValue = that.__replaceValue(updateData, temp, renderKey, el);
292
- if (attrValue == attr.value)
293
- attrValue = undefined
294
- }
295
- else
296
- attrValue = that.__replaceValue(data, attrValue, renderKey, el);
297
-
298
- // TODO: support attibute name replace if has {{}}
299
- // attr_name = that.__replaceValue(data, attr_name, renderKey, el);
300
-
301
- if (attrValue || attrValue == "") {
302
- el.setAttribute(attr_name, attrValue);
303
- }
304
- });
305
-
306
- if (CoCreate.pass) {
307
- if (el.hasAttribute('[pass_id]'))
308
- CoCreate.pass.initElement(el)
309
- }
310
-
311
- if (el.tagName === 'SCRIPT' && el.src) {
312
- if (el.src.includes('CoCreate.js') || el.src.includes('CoCreate.min.js')) {
313
- el.remove()
314
- return
315
- }
316
- }
317
-
318
- if (el.tagName === 'LINK' && el.href) {
319
- if (el.href.includes('CoCreate.css') || el.href.includes('CoCreate.min.css')) {
320
- el.remove()
321
- return
322
- }
323
- }
324
-
325
- if (el.childNodes.length > 0) {
326
- that.setValue(el.childNodes, updateData || data, renderArray, renderKey);
327
- }
328
-
329
- if ((el.tagName == 'TEMPLATE' || el.hasAttribute('template') || el.classList.contains('template')) && !el.hasAttribute('template_id')) {
330
- if (el.getAttribute('render-object') || el.getAttribute('render-array'))
331
- that.render(el, data);
332
- }
333
-
334
- }
335
-
336
- if (el.nodeType == 3) {
337
- let valueType = el.parentElement.getAttribute('value-type')
338
-
339
- let textContent, placeholder, text;
340
- if (el.renderMap)
341
- placeholder = el.renderMap.get(el)
342
- if (placeholder && !isRenderKey) {
343
- let updateData = data;
344
- textContent = placeholder.placeholder
345
- renderKey = placeholder.renderKey
346
- renderArray = placeholder.renderArray
347
- if (renderArray && Array.isArray(data[renderArray]))
348
- updateData = data[renderArray][0]
349
- else
350
- updateData = data[renderArray]
351
- if (renderKey)
352
- updateData = {[renderKey]: updateData}
353
- text = that.__replaceValue(updateData, textContent, renderKey, el);
354
- }
355
-
356
- if (!placeholder && !text) {
357
- textContent = el.textContent;
358
- if (!el.renderMap)
359
- that.renderMap(el, textContent, renderArray, renderKey)
360
- text = that.__replaceValue(data, textContent, renderKey, el);
361
- }
362
-
363
- if (text || text == "") {
364
- if (text != el.renderedValue) {
365
- el.renderedValue = text
366
- if (valueType == 'text' || valueType == 'string'){
367
- el.renderedValue = text
368
- el.textContent = text;
369
- } else {
370
- const newNode = document.createElement('div');
371
- newNode.innerHTML = text;
372
- let parentElement = el.parentElement
373
- parentElement.renderedValue = text
374
- if (!parentElement.renderMap) {
375
- that.renderMap(parentElement, textContent, renderArray, renderKey)
376
- } else if (!parentElement['renderMap'].has(parentElement))
377
- that.renderMap(parentElement, textContent, renderArray, renderKey)
378
- el.replaceWith(...newNode.childNodes)
379
-
380
- if (el.childNodes.length > 0) {
381
- that.setValue(el.childNodes, updateData || data, renderArray, renderKey);
382
- }
383
- }
384
- }
385
- }
386
- }
387
- });
388
- },
389
-
390
- renderMap: function(node, placeholder, renderArray, renderKey) {
391
- if (!node.renderMap) {
392
- node['renderMap'] = new Map();
393
- }
394
- if (placeholder && renderKey)
395
- node['renderMap'].set(node, {placeholder, renderArray, renderKey})
396
- },
397
-
398
- data: function({selector, data, elements}) {
399
- if (selector) {
400
- let template = queryDocumentSelector(selector);
401
- if (!template) return;
402
- if (template.tagName == 'TEMPLATE' || template.hasAttribute('template') || template.classList.contains('template')) {
403
- template.renderedKeys = undefined
404
- this.render(template, data);
405
- }
406
- else
407
- this.setValue([template], data);
408
- } else if (elements) {
409
- if (elements.length == 1 && (elements[0].tagName == 'TEMPLATE' || elements[0].hasAttribute('template') || elements[0].classList.contains('template'))){
410
- this.render(elements[0], data);
411
- }
412
- else
413
- this.setValue(elements, data);
414
- }
415
-
416
- }
417
-
418
- };
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
+
35
+ function init(element) {
36
+ if (element && !(element instanceof HTMLCollection) && !Array.isArray(element))
37
+ element = [element]
38
+ else if (!element)
39
+ element = document.querySelectorAll(elementSelector)
40
+
41
+ for (let i = 0; i < element.length; i++) {
42
+ let source = sources.get(element[i])
43
+ if (!source) {
44
+ sources.set(element[i], { element: element[i] })
45
+ element[i].renderValue = (data) => render({ source: element[i], data })
46
+ element[i].getData = () => sources.get(element[i]).data
47
+ }
48
+ }
49
+ }
50
+
51
+ async function render({ source, element, data, key, index, currentIndex, update, remove }) {
52
+ if (!element) {
53
+ if (source) {
54
+ element = queryElements({ element: source, prefix: 'render' })
55
+ if (!element && source.children.length > 0) {
56
+ for (const child of source.children) {
57
+ if (child.matches('template, [template], .template, [render]')) {
58
+ element = child;
59
+ break; // Found the desired element, no need to continue the loop
60
+ }
61
+ }
62
+ }
63
+
64
+ if (!element) {
65
+ element = source.querySelector('template, [template], .template, [render]');
66
+ }
67
+ }
68
+
69
+ if (!element) return;
70
+ }
71
+
72
+ if (source) {
73
+ let sourceData = sources.get(source)
74
+ if (!sourceData) {
75
+ sourceData = { element: source, data }
76
+ sources.set(source, sourceData)
77
+ }
78
+
79
+ source = sourceData
80
+ if (!source.data)
81
+ source.data = data
82
+ } else if (data)
83
+ source = { data }
84
+
85
+ if (data.filter) {
86
+ index = index || data.filter.index
87
+ update = update || data.filter.update
88
+ remove = remove || data.filter.remove
89
+ }
90
+
91
+ if (!Array.isArray(element) && !(element instanceof HTMLCollection) && !(element instanceof NodeList))
92
+ element = [element]
93
+
94
+ for (let i = 0; i < element.length; i++) {
95
+ if (!key)
96
+ key = element[i].getAttribute('render')
97
+
98
+ let renderedNode = renderedNodes.get(element[i])
99
+ if (source) {
100
+ if (!renderedNode) {
101
+ renderedNode = { element: element[i], source, clones: new Map(), renderAs: new Map() }
102
+ renderedNodes.set(element[i], renderedNode)
103
+ }
104
+ }
105
+
106
+ if (remove) {
107
+ if (renderedNode.source) {
108
+ if (key)
109
+ renderedNode.source.data[key].splice(index, 1)
110
+ else {
111
+
112
+ }
113
+ }
114
+
115
+ let clone = Array.from(renderedNode.clones)[index]
116
+ if (!clone) return
117
+
118
+ renderedNode.clones.delete(clone[0])
119
+ renderedNodes.delete(clone[1])
120
+ clone[1].remove()
121
+ } else if (key || Array.isArray(data)) {
122
+ if (update) {
123
+ let clone
124
+
125
+ if (!currentIndex)
126
+ currentIndex = data.filter.currentIndex
127
+
128
+ if (currentIndex >= 0)
129
+ clone = Array.from(renderedNode.clones)[currentIndex]
130
+ else
131
+ clone = Array.from(renderedNode.clones)[index]
132
+
133
+ if (!clone) return
134
+
135
+ await renderValues(clone[1], data);
136
+ if (currentIndex >= 0)
137
+ insertElement(renderedNode, clone[1], index, currentIndex)
138
+ }
139
+
140
+ // TODO: if $auto here every subsequent clone will have same value, not replacing here will apply unique value to each clone
141
+ if (key === '$auto')
142
+ key = key.replace(/\$auto/g, uuid.generate(6));
143
+
144
+ if (key)
145
+ element[i].setAttribute('render', key);
146
+
147
+ renderTemplate(element[i], data, key, index);
148
+ } else
149
+ await renderValues(element[i], data);
150
+
151
+ }
152
+
153
+ }
154
+
155
+ async function renderTemplate(template, data, key, index, keyPath) {
156
+ if (!key)
157
+ key = template.getAttribute('render')
158
+
159
+ let templateData = renderedNodes.get(template)
160
+ if (!templateData) {
161
+ templateData = { element: template, clones: new Map() }
162
+ renderedNodes.set(template, templateData)
163
+ }
164
+
165
+ templateData.parent = template.parentElement.closest('[render]')
166
+ if (templateData.parent)
167
+ templateData.parent = renderedNodes.get(templateData.parent)
168
+
169
+ if (!templateData.keyPath) {
170
+ if (keyPath)
171
+ templateData.keyPath = keyPath
172
+ else if (templateData.parent && templateData.parent.keyPath)
173
+ templateData.keyPath = templateData.parent.keyPath
174
+ else if (key)
175
+ templateData.keyPath = key
176
+ }
177
+
178
+ template = templateData
179
+
180
+ let renderData
181
+ if (key)
182
+ renderData = getRenderValue(template.element, data, key)
183
+ else if (Array.isArray(data))
184
+ renderData = data
185
+
186
+ if (!renderData) return
187
+
188
+ if (index === 0) {
189
+ for (const [key, element] of template.clones) {
190
+ renderedNodes.delete(element)
191
+ element.remove()
192
+ template.clones.delete(key)
193
+ }
194
+ // template.data = renderData
195
+ }
196
+ // else if (index) {
197
+ // updates data that has already been rendered
198
+ // template.data = dotNotationToObject(renderData, template.data)
199
+ // }
200
+
201
+ let renderAs = template.element.getAttribute('render-as') || key;
202
+ template.renderAs = renderAs
203
+
204
+ if (key && !Array.isArray(renderData)) {
205
+ let exclude = template.element.getAttribute('render-exclude') || ''
206
+ if (exclude) {
207
+ exclude = exclude.replace(/ /g, '').split(",")
208
+ if (!Array.isArray(exclude))
209
+ exclude = [exclude]
210
+ }
211
+
212
+ const keys = Object.keys(renderData)
213
+ for (let i = 0; i < keys.length; i++) {
214
+ if (exclude.includes(keys[i]))
215
+ continue
216
+
217
+ let value = renderData[keys[i]]
218
+ let type = 'string';
219
+
220
+ if (Array.isArray(value))
221
+ type = 'array'
222
+ else if (typeof (value) == "object")
223
+ type = 'object'
224
+
225
+ let Data = { [renderAs]: { key: keys[i], value, type } }
226
+
227
+ let clone = cloneTemplate(template);
228
+ let renderedKey = key.split('.')
229
+ renderedKey = renderedKey[renderedKey.length - 1];
230
+ //renderedKey needs to remove the parent.renderAs/key
231
+
232
+ clone.key = keys[i]
233
+ clone.keyPath = template.keyPath + '.' + renderedKey
234
+ clone.parentKey = renderedKey
235
+ clone.renderKey = key
236
+ clone.element.setAttribute('renderedKey', keys[i])
237
+ await renderValues(clone.element, Data, keys[i], renderAs);
238
+ insertElement(template, clone.element, index);
239
+
240
+ }
241
+ } else {
242
+ if (!key && !Array.isArray(renderData)) {
243
+ key = 'data'
244
+ renderData = getValueFromObject(renderData, key);
245
+ if (!renderAs)
246
+ renderAs = key
247
+ }
248
+
249
+ if (!renderData) {
250
+ let clone = cloneTemplate(template);
251
+ clone.keyPath = template.keyPath
252
+ await renderValues(clone.element, data, key, renderAs);
253
+ insertElement(template, clone.element, index);
254
+ } else {
255
+ if (!Array.isArray(renderData))
256
+ renderData = [renderData]
257
+
258
+ for (let i = 0; i < renderData.length; i++) {
259
+ let clone = cloneTemplate(template);
260
+ clone.keyPath = template.keyPath || '' + `[${i}]`
261
+
262
+ let object
263
+ if (renderAs) {
264
+ object = { [renderAs]: renderData[i] }
265
+ if (renderAs.includes('.'))
266
+ object = dotNotationToObject(object);
267
+ } else
268
+ object = renderData[i]
269
+
270
+ await renderValues(clone.element, object, key, renderAs);
271
+ insertElement(template, clone.element, index);
272
+ }
273
+ }
274
+ }
275
+ }
276
+
277
+ function cloneTemplate(template) {
278
+ let clone = template.element.cloneNode(true);
279
+
280
+ if (clone.tagName == 'TEMPLATE') {
281
+ clone = template.element.content.firstElementChild
282
+ for (let attribute of template.element.attributes) {
283
+ clone.setAttribute(attribute.name, attribute.value || '');
284
+ }
285
+ } else {
286
+ clone.classList.remove('template');
287
+ clone.removeAttribute('template');
288
+ }
289
+
290
+ clone.setAttribute('render-clone', '');
291
+
292
+ let renderAs = clone.getAttribute('render-as')
293
+ if (renderAs) {
294
+ clone = clone.outerHTML.replace(/\$auto/g, renderAs);
295
+ } else {
296
+ clone = clone.outerHTML;
297
+ }
298
+
299
+ if (typeof clone === 'string') {
300
+ let container = document.createElement('div');
301
+ container.innerHTML = clone;
302
+ clone = container.firstChild
303
+ container.remove()
304
+ }
305
+ let renderedNode = { template, element: clone }
306
+ renderedNodes.set(clone, renderedNode)
307
+ return renderedNode;
308
+ }
309
+
310
+ function insertElement(template, element, index, currentIndex) {
311
+ let eid = element.getAttribute('eid')
312
+ if (!eid)
313
+ console.log('attribute eid not found')
314
+
315
+ if (index !== null && index >= 0) {
316
+ const clones = Array.from(template.clones);
317
+
318
+ let item
319
+ if (currentIndex) {
320
+ item = clones.splice(currentIndex, 1)[0];
321
+ } else {
322
+ item = [eid, element];
323
+ }
324
+
325
+ clones.splice(index, 0, item);
326
+ if (clones[index + 1] && clones[index][1] !== element)
327
+ clones[index][1].insertAdjacentElement('beforebegin', element);
328
+ else if (clones[index] && clones[index][1] !== element)
329
+ clones[index][1].insertAdjacentElement('afterend', element);
330
+ else
331
+ template.element.insertAdjacentElement('beforebegin', element);
332
+ template.clones = new Map(clones);
333
+ } else {
334
+ template.clones.set(eid, element)
335
+ template.element.insertAdjacentElement('beforebegin', element);
336
+ }
337
+ }
338
+
339
+ async function renderValues(node, data, key, renderAs, keyPath, parent) {
340
+ if (!data) return;
341
+
342
+ let renderedNode = renderedNodes.get(node)
343
+ if (!renderedNode)
344
+ renderedNode = { key, renderAs }
345
+
346
+ if (!renderedNode.key)
347
+ renderedNode.key = key
348
+ else if (!key)
349
+ key = renderedNode.key
350
+
351
+ if (!renderedNode.renderAs)
352
+ renderedNode.renderAs = renderAs
353
+ else if (!renderAs)
354
+ renderAs = renderedNode.renderAs
355
+
356
+ if (keyPath)
357
+ renderedNode.keyPath = keyPath
358
+ else if (renderedNode.keyPath)
359
+ keyPath = renderedNode.keyPath
360
+
361
+ if (parent)
362
+ renderedNode.parent = parent
363
+
364
+ if (node.nodeType == 1) {
365
+ if (node.hasAttribute('render-clone')) {
366
+ parent = renderedNode
367
+
368
+ for (let eid of ['_id', 'name', 'key']) {
369
+ if (renderAs)
370
+ eid = data[renderAs][eid]
371
+ else
372
+ eid = data[eid]
373
+
374
+ if (!eid) continue
375
+
376
+ let oldEid = renderedNode.eid
377
+ let temp = renderedNode.template
378
+ if (!temp) {
379
+ console.log('temp could not be found')
380
+ } else if (!temp.clones) {
381
+ if (temp.template)
382
+ temp = temp.template
383
+ else
384
+ console.log(temp)
385
+
386
+ if (oldEid && oldEid !== eid)
387
+ temp.clones.delete(oldEid)
388
+
389
+ temp.clones.set(eid, node)
390
+ }
391
+
392
+ renderedNode.eid = eid
393
+
394
+ node.setAttribute('eid', eid)
395
+ break;
396
+ }
397
+ }
398
+
399
+ // Array.from(node.attributes).forEach(attr => {
400
+ for (let attr of node.attributes) {
401
+ let name = attr.name;
402
+ let value = attr.value;
403
+
404
+ let renderedAttribute = renderedNodes.get(attr)
405
+ if (!renderedAttribute) {
406
+ renderedAttribute = { placeholder: { name, value }, key, renderAs, parent: renderedNode }
407
+ }
408
+
409
+ let namePlaceholder = renderedAttribute.placeholder.name || name;
410
+ let valuePlaceholder = renderedAttribute.placeholder.value || value;
411
+
412
+ name = await renderValue(attr, data, namePlaceholder, renderAs, renderedAttribute);
413
+ value = await renderValue(attr, data, valuePlaceholder, renderAs, renderedAttribute);
414
+
415
+ if (name === undefined && name === null) {
416
+ renderedNodes.delete(attr)
417
+ node.removeAttribute(attr.name);
418
+ } else if ((value || value === "") && (name !== attr.name || value !== attr.value))
419
+ node.setAttribute(name, value);
420
+ }
421
+ // });
422
+
423
+ if (CoCreate.pass) {
424
+ if (node.hasAttribute('[pass_id]'))
425
+ CoCreate.pass.initElement(node)
426
+ }
427
+
428
+ if (node.tagName === 'SCRIPT' && node.src) {
429
+ if (node.src.includes('CoCreate.js') || node.src.includes('CoCreate.min.js')) {
430
+ node.remove()
431
+ return
432
+ }
433
+ }
434
+
435
+ if (node.tagName === 'LINK' && node.href) {
436
+ if (node.href.includes('CoCreate.css') || node.href.includes('CoCreate.min.css')) {
437
+ node.remove()
438
+ return
439
+ }
440
+ }
441
+ if (node.getAttribute('render') && !node.hasAttribute('render-clone')) {
442
+ renderTemplate(node, data);
443
+ } else if (node.childNodes.length > 0) {
444
+ // Array.from(node.childNodes).forEach(childNode => {
445
+ for (let childNode of node.childNodes) {
446
+ await renderValues(childNode, data, key, renderAs, keyPath, parent);
447
+ }
448
+ // });
449
+ }
450
+
451
+
452
+ } else if (node.nodeType == 3) {
453
+ let valueType = node.parentElement.getAttribute('value-type')
454
+
455
+ let textContent, text;
456
+
457
+ if (!renderedNode.placeholder && node.textContent.match(/{{(.*?)}}/))
458
+ renderedNode.placeholder = node.textContent
459
+
460
+ if (!renderedNode.placeholder)
461
+ return
462
+ textContent = renderedNode.placeholder || node.textContent;
463
+
464
+ text = await renderValue(node, data, textContent, renderAs, renderedNode);
465
+
466
+ if (text || text == "") {
467
+ if (text != renderedNode.text) {
468
+ renderedNode.text = text
469
+
470
+ if (valueType == 'text' || valueType == 'string' || valueType != "html") {
471
+ node.textContent = text;
472
+ } else {
473
+ const newNode = document.createElement('div');
474
+ newNode.innerHTML = text;
475
+ let parentElement = node.parentElement
476
+
477
+ let renderedParent = renderedNodes.get(parentElement)
478
+ if (!renderedParent) {
479
+ renderedParent = { placeholder: textContent, key, renderAs }
480
+ }
481
+ renderedParent.text = text
482
+ for (let newChild of newNode.childNodes) {
483
+ renderedNode.element = newChild
484
+ renderedNodes.set(newChild, renderedNode)
485
+ }
486
+ renderedNodes.delete(node)
487
+ node.replaceWith(...newNode.childNodes)
488
+ }
489
+
490
+ if (node.childNodes.length > 0) {
491
+ // Array.from(node.childNodes).forEach(childNode => {
492
+ for (let childNode of node.childNodes) {
493
+ await renderValues(childNode, data, key, renderAs, keyPath, parent);
494
+ }
495
+ // });
496
+ }
497
+ }
498
+ }
499
+ }
500
+ }
501
+
502
+ async function renderValue(node, data, placeholder, renderAs, renderedNode) {
503
+ let output = placeholder;
504
+
505
+ if (placeholder.match(/{{(.*?)}}/)) {
506
+ if (renderedNode) {
507
+ renderedNodes.set(node, renderedNode)
508
+ }
509
+
510
+ let match
511
+ do {
512
+ // match = output.match(/{{([A-Za-z0-9_.,\[\]\- ]*)}}/); // works by getting inner matches first
513
+ // match = output.match(/{{([^}]*)}}/);
514
+ match = output.match(/{{(.*?)}}/);
515
+ if (match) {
516
+ let value
517
+ try {
518
+ let Data = JSON.parse('{' + match[1].replace(/'/g, '"') + '}');
519
+ if (Data.storage || Data.database || Data.array || Data.object || Data.index) {
520
+ Data.method = 'read.object'
521
+ value = await CoCreate.crud.send(Data)
522
+ value = value.object[0][Data.key]
523
+ }
524
+ } catch (error) {
525
+ value = getRenderValue(node, data, match[1], renderAs)
526
+ }
527
+
528
+ if (value || value === "") {
529
+ if (typeof value === "object")
530
+ value = JSON.stringify(value, null, 2)
531
+
532
+ output = output.replace(match[0], value);
533
+ } else if (renderAs) {
534
+ if (match[0].startsWith(`{{${renderAs}.`)) {
535
+ output = output.replace(match[0], "");
536
+ } else {
537
+ match = null
538
+ }
539
+ } else {
540
+ match = null
541
+ }
542
+
543
+ }
544
+
545
+ } while (match)
546
+ }
547
+ return output;
548
+ }
549
+
550
+ function getRenderValue(node, data, key, renderAs) {
551
+ let value = getValueFromObject(data, key);
552
+
553
+ if (!value && value !== '' && node) {
554
+
555
+ let parentTemplate = node
556
+ do {
557
+ let parentNode = renderedNodes.get(parentTemplate)
558
+ if (parentNode) {
559
+
560
+ if (!value && (parentNode.parent || parentNode.template)) {
561
+ let Data, eid, parent = parentNode.parent || parentNode.template
562
+ do {
563
+ if (key.includes('keyPath'))
564
+ value = parentNode.keyPath || parent.keyPath
565
+ else if (key.includes('parentKey')) {
566
+ value = parentNode.parentKey || parent.parentKey || parent.parent.parentKey
567
+ } else if (key.includes('renderAs')) {
568
+ value = renderAs
569
+ } else if (key === 'object' || key === '_id')
570
+ value = ObjectId()
571
+ else if (key === 'uuid')
572
+ value = uuid.generate(6)
573
+ else if (parent.source)
574
+ Data = parent.source.data
575
+ else if (parent.parent)
576
+ parent = parent.parent
577
+ else if (parent.template)
578
+ parent = parent.template
579
+ else
580
+ parent = undefined
581
+
582
+ if (!Data && parent && parent.eid)
583
+ eid = parent.eid
584
+
585
+
586
+ } while (!value && parent && !Data)
587
+
588
+ if (!value && Data)
589
+ value = getValueFromObject(Data, key);
590
+
591
+ if (!value && key.includes('.')) {
592
+ let renderedData = getValueFromObject(Data, key.split('.')[0]);
593
+ if (renderedData) {
594
+ let index
595
+ if (!parent.clones.size || parent.clones.size === 1)
596
+ index = 0
597
+ else if (eid)
598
+ index = Array.from(parent.clones.keys()).indexOf(eid);
599
+ else
600
+ console.log('eid not found')
601
+
602
+ if (index >= 0)
603
+ Data = { [key.split('.')[0]]: renderedData[index] }
604
+
605
+ value = getValueFromObject(Data, key);
606
+ }
607
+
608
+ }
609
+ }
610
+ }
611
+
612
+ if (!value && parentTemplate.parentElement)
613
+ parentTemplate = parentTemplate.parentElement.closest('[render]')
614
+ else
615
+ parentTemplate = undefined
616
+
617
+ } while (!value && parentTemplate)
618
+ }
619
+ return value
620
+ }
419
621
 
420
622
  function renderKey(element, params) {
421
- // TODO: custom render-keys
422
- const form = element.closest("form") || document;
423
- if (!params)
424
- params = 'render-key'
425
-
426
- let data = {}
427
- let selector = `[${params}]`
428
- let elements = form.querySelectorAll(selector);
429
- for (let el of elements) {
430
- let attribute = el.getAttribute(params)
431
- if (attribute)
432
- data[attribute] = el.getValue()
433
- }
434
- data = dotNotationToObject(data)
435
- let renderData = {data: {[params]: data}}
436
-
437
- let templateSelector = `[template_id='${params}']`
438
- let template = document.querySelectorAll(templateSelector);
439
- if (template)
440
- renderData.elements = template
441
- else
442
- renderData.selector = `[template='${params}']`
443
-
444
- CoCreateRender.data(renderData);
445
-
446
- document.dispatchEvent(new CustomEvent('renderKey', {
447
- detail: { data }
448
- }));
623
+ // TODO: custom render-keys
624
+ const form = element.closest("form") || document;
625
+ if (!params)
626
+ params = 'render-key'
627
+
628
+ let data = {}
629
+ let selector = `[${params}]`
630
+ let elements = form.querySelectorAll(selector);
631
+ for (let el of elements) {
632
+ let attribute = el.getAttribute(params)
633
+ if (attribute)
634
+ data[attribute] = el.getValue()
635
+ }
636
+ data = dotNotationToObject(data)
637
+ let renderData = { data: { [params]: data } }
638
+
639
+ let renderSelector = element.getAttribute('render-selector')
640
+ if (!renderSelector) return
641
+
642
+ renderData.selector = renderSelector
643
+ render(renderData);
644
+
645
+ document.dispatchEvent(new CustomEvent('renderKey', {
646
+ detail: { data }
647
+ }));
449
648
  }
450
649
 
451
- action.init({
452
- name: "renderKey",
453
- callback: (btn, params) => {
454
- renderKey(btn, params);
455
- }
650
+ Actions.init({
651
+ name: "renderKey",
652
+ callback: (action) => {
653
+ renderKey(action.element, action.params);
654
+ }
456
655
  });
457
656
 
458
- observer.init({
459
- name: 'render',
460
- observe: ['addedNodes'],
461
- target: '[render]',
462
- callback: function(mutation) {
463
- let element = mutation.target
464
- element.removeAttribute('render')
465
-
466
- let parentElement = element.parentElement
467
- if (parentElement) {
468
- let el = element
469
- let parentKeys = [];
470
- let renderedData = new Map();
471
- do {
472
- let data;
473
- el = el.parentElement
474
- if (el) {
475
- if (el.hasAttribute('render-clone'))
476
- data = el.renderedData
477
- if (data) {
478
- renderedData.set(data, '')
479
- }
480
- let parentKey = el.getAttribute('parentKey')
481
- if (parentKey && parentKey != null) {
482
- if (/^\d+$/.test(parentKey))
483
- parentKey = `[${parentKey}]`
484
- parentKeys.push(parentKey)
485
- }
486
- }
487
- } while (el)
488
-
489
- let renderKey = element.getAttribute('render-key')
490
-
491
- let parentKey;
492
- parentKey = parentElement.getAttribute('parentKey')
493
- if (renderKey == '$auto' || parentKey || parentKeys) {
494
- let template = element.outerHTML
495
- if (renderKey) {
496
- template = template.replace(/\$auto/g, uuid.generate(6));
497
- }
498
- if (parentKeys.length > 0) {
499
- let string = ''
500
- if (parentKeys.length == 1){
501
- string = parentKeys[0]
502
- }
503
- else
504
- string = parentKeys.reverse().join('.')
505
- string = string.replace(/.\[/g, '[');
506
- template = template.replace(/\$parents/g, string);
507
- }
508
- if (parentKey) {
509
- template = template.replace(/\$parent/g, parentKey);
510
- }
511
-
512
- parentElement.innerHTML = template
513
-
514
- }
515
-
516
- element = parentElement.firstElementChild
517
- let obj = {}
518
- let array = Array.from(renderedData.keys())
519
- for (let data of array.reverse()){
520
- obj = {...obj, ...data}
521
- }
522
-
523
- CoCreateRender.data({
524
- elements: [element],
525
- data: obj
526
- });
527
- }
528
- }
657
+ Observer.init({
658
+ name: 'render',
659
+ observe: ['addedNodes'],
660
+ target: elementSelector,
661
+ callback: function (mutation) {
662
+ init(mutation.target)
663
+ }
664
+ })
665
+
666
+ Observer.init({
667
+ name: 'fileRender',
668
+ observe: ['attributes'],
669
+ attributeName: ['render-selector', 'render-closest', 'render-parent', 'render-next', 'render-previous'],
670
+ callback: function (mutation) {
671
+ render({
672
+ element: mutation.target, data: mutation.target.getValue()
673
+ });
674
+ }
529
675
  });
530
676
 
531
- export default CoCreateRender;
677
+
678
+ Observer.init({
679
+ name: 'render',
680
+ observe: ['addedNodes'],
681
+ target: '[render]',
682
+ callback: function (mutation) {
683
+ if (mutation.target.hasAttribute('render-clone'))
684
+ return
685
+ let parentElement = mutation.target.parentElement.closest(['render'])
686
+ if (!parentElement) return
687
+
688
+ let renderedNode = renderedNodes.get(parentElement)
689
+ let data = renderedNode.source.data
690
+
691
+ render({ element: mutation.target, data });
692
+
693
+ }
694
+ });
695
+
696
+ // Observer.init({
697
+ // name: 'render',
698
+ // observe: ['addedNodes'],
699
+ // target: '[render-clone]',
700
+ // callback: function (mutation) {
701
+ // let renderedNode = renderedNodes.get(mutation.target)
702
+ // if (!renderedNode) return
703
+
704
+ // render({ source, element, data, key, index, currentIndex, update, remove })
705
+
706
+ // let nextElement = mutation.target.nextElementSibling
707
+ // if (!nextElement) return
708
+
709
+ // let nextRenderedNode = renderedNodes.get(nextElement)
710
+ // if (!nextRenderedNode) return
711
+
712
+ // let clones
713
+ // if (nextRenderedNode.template)
714
+ // clones = nextRenderedNode.template.clones
715
+ // else if (nextRenderedNode.clones)
716
+ // clones = nextRenderedNode.clones
717
+
718
+ // const cloneValueArray = Array.from(clones.values())
719
+ // let index = cloneValueArray.indexOf(nextElement);
720
+
721
+ // const cloneArray = Array.from(clones)
722
+
723
+ // cloneArray.splice(index, 0, [renderedNode.eid, mutation.target]);
724
+ // clones = new Map(cloneArray);
725
+
726
+ // }
727
+ // })
728
+
729
+ Observer.init({
730
+ name: 'renderNodesRemoved',
731
+ observe: ['removedNodes'],
732
+ target: '[render-clone]',
733
+ callback: function (mutation) {
734
+ if (mutation.target.parentElement) return
735
+ let renderedNode = renderedNodes.get(mutation.target)
736
+ if (!renderedNode) return
737
+ renderedNode.template.clones.delete(renderedNode.eid)
738
+ renderedNodes.delete(mutation.target)
739
+ }
740
+ })
741
+
742
+ init()
743
+
744
+ export default { render, sources, renderedNodes }