@cocreate/elements 1.17.33 → 1.18.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/CHANGELOG.md +32 -0
- package/CoCreate.config.js +2 -2
- package/LICENSE +683 -21
- package/demo/index.html +185 -23
- package/docs/index.html +27 -27
- package/package.json +9 -9
- package/src/form.js +143 -0
- package/src/index.js +860 -129
- package/webpack.config.js +79 -73
package/src/index.js
CHANGED
|
@@ -1,206 +1,937 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
+
|
|
23
|
+
import Observer from '@cocreate/observer';
|
|
24
|
+
import Actions from '@cocreate/actions';
|
|
25
|
+
import CRUD from '@cocreate/crud-client';
|
|
26
|
+
import { dotNotationToObject, queryData, sortData, getAttributes, getAttributeNames, checkValue } from '@cocreate/utils';
|
|
27
|
+
import '@cocreate/filter';
|
|
28
|
+
import { render } from '@cocreate/render';
|
|
5
29
|
import '@cocreate/element-prototype';
|
|
6
30
|
import './fetchSrc';
|
|
31
|
+
import { reset } from './form'
|
|
32
|
+
|
|
33
|
+
const selector = "[storage], [database], [array], [render-json]";
|
|
34
|
+
const elements = new Map();
|
|
35
|
+
const keys = new Map();
|
|
36
|
+
const forms = new Map(); // form, [elements]
|
|
37
|
+
const debounce = new Map();
|
|
38
|
+
|
|
39
|
+
function init(element) {
|
|
40
|
+
if (element && !(element instanceof HTMLCollection) && !Array.isArray(element))
|
|
41
|
+
element = [element]
|
|
42
|
+
else if (!element) {
|
|
43
|
+
element = document.querySelectorAll(selector)
|
|
44
|
+
initSocket();
|
|
45
|
+
}
|
|
7
46
|
|
|
47
|
+
let dataObjects = new Map();
|
|
48
|
+
for (let i = 0; i < element.length; i++) {
|
|
49
|
+
if (elements.has(element[i]) || element.tagName === 'FORM')
|
|
50
|
+
continue
|
|
8
51
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
52
|
+
let data = initElement(element[i]);
|
|
53
|
+
if (data) {
|
|
54
|
+
let dataKey = initDataKey(element[i], data)
|
|
55
|
+
dataObjects.set(dataKey.string, data)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
12
58
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
59
|
+
if (dataObjects && dataObjects.size > 0) {
|
|
60
|
+
for (let key of dataObjects.keys()) {
|
|
61
|
+
let { elements, data } = keys.get(key)
|
|
62
|
+
read(Array.from(elements.keys()), data, { string: key });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
17
65
|
}
|
|
18
66
|
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
let
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
doc.elements = [...item.elements, element]
|
|
29
|
-
else
|
|
30
|
-
doc.elements = [element]
|
|
31
|
-
|
|
32
|
-
// let collection = doc.collection;
|
|
33
|
-
// let document_id = doc.document_id;
|
|
34
|
-
// let filter = doc.filter;
|
|
35
|
-
let initialize = initializing.get(element)
|
|
36
|
-
if (!initialize) {
|
|
37
|
-
initializing.set(element, key);
|
|
38
|
-
documents.set(key, doc);
|
|
39
|
-
// els.set(key, doc);
|
|
40
|
-
// els.push(element)
|
|
41
|
-
} else {
|
|
42
|
-
// if (document_id && initialize.document_id != document_id || filter && JSON.stringify(initialize.filter) != JSON.stringify(filter)){
|
|
43
|
-
if (initialize != key) {
|
|
44
|
-
initializing.set(element, key);
|
|
45
|
-
documents.set(key, doc);
|
|
46
|
-
// els.set(key, doc);
|
|
47
|
-
// els.push(element)
|
|
48
|
-
}
|
|
67
|
+
function initElement(el) {
|
|
68
|
+
if (el.hasAttribute('render-json')) {
|
|
69
|
+
// TODO find the json template in the text or attributes
|
|
70
|
+
// if found add the node as an element, if the element has crud attributes also add as the element
|
|
71
|
+
for (let attribute of el.attributes) {
|
|
72
|
+
if (attribute.match(/{{(.*?)}}/)) {
|
|
73
|
+
let value = renderValue(attribute, undefined, attribute.value)
|
|
74
|
+
if (value)
|
|
75
|
+
attribute.value = value
|
|
49
76
|
}
|
|
50
77
|
}
|
|
78
|
+
if (el.innerHTML.match(/{{(.*?)}}/)) {
|
|
79
|
+
let value = renderValue(el, undefined, el.innerHTML)
|
|
80
|
+
if (value)
|
|
81
|
+
attribute.value = value
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// if (el.textContent.match(/{{(.*?)}}/)) {
|
|
85
|
+
// let value = renderValue(el, undefined, el.textContent)
|
|
86
|
+
// if (value)
|
|
87
|
+
// attribute.value = value
|
|
88
|
+
// }
|
|
89
|
+
|
|
90
|
+
// let Data = JSON.parse(match[1]);
|
|
91
|
+
// Data.method = read.object
|
|
92
|
+
// CRUD.send(Data)
|
|
93
|
+
|
|
51
94
|
}
|
|
52
95
|
|
|
53
|
-
|
|
54
|
-
}
|
|
96
|
+
// if (el.closest('.template')) return;
|
|
55
97
|
|
|
56
|
-
|
|
57
|
-
|
|
98
|
+
let data = getObject(el);
|
|
99
|
+
if (!data || !data.type) return
|
|
100
|
+
|
|
101
|
+
if (!elements.has(el)) {
|
|
102
|
+
elements.set(el, '')
|
|
103
|
+
|
|
104
|
+
if (['INPUT', 'TEXTAREA', 'SELECT'].includes(el.tagName)
|
|
105
|
+
|| (el.hasAttribute('contenteditable') && el.getAttribute('contenteditable') !== 'false')
|
|
106
|
+
|| el.contenteditable) {
|
|
107
|
+
el.addEventListener('input', function (e) {
|
|
108
|
+
const { object, key, isRealtime, isCrdt } = getAttributes(el);
|
|
109
|
+
if (isRealtime == "false" || key == "_id") return;
|
|
58
110
|
|
|
59
|
-
|
|
111
|
+
if (isCrdt == "true" && object && object !== 'pending') return
|
|
112
|
+
if (object && e.detail && e.detail.skip == true) return;
|
|
113
|
+
if (data.type !== 'object' && data[data.type] === 'pending') return
|
|
60
114
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
115
|
+
save(el);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (el.getFilter) {
|
|
121
|
+
data.filter = el.getFilter();
|
|
122
|
+
el.setFilter = (filter) => {
|
|
123
|
+
data.filter = filter
|
|
124
|
+
let dataKey = initDataKey(el, data)
|
|
125
|
+
read(el, data, dataKey)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const isRead = el.getAttribute('read');
|
|
130
|
+
if (isRead === 'false')
|
|
64
131
|
return;
|
|
132
|
+
|
|
133
|
+
return data;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function getObject(element) {
|
|
137
|
+
const data = getAttributes(element);
|
|
138
|
+
const crudType = ['storage', 'database', 'array', 'index', 'object']
|
|
139
|
+
|
|
140
|
+
for (let i = 0; i < crudType.length; i++) {
|
|
141
|
+
if (!checkValue(data[crudType[i]]))
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
if (data[crudType[i]] && data[crudType[i]].includes(",")) {
|
|
145
|
+
const array = data[crudType[i]].split(',');
|
|
146
|
+
data[crudType[i]] = []
|
|
147
|
+
|
|
148
|
+
for (let j = 0; j < array.length; j++) {
|
|
149
|
+
array[i].trim()
|
|
150
|
+
if (crudType[i] === 'object') {
|
|
151
|
+
data[crudType[i]].push({ _id: array[j] })
|
|
152
|
+
} else {
|
|
153
|
+
data[crudType[i]].push(array[j])
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
65
157
|
}
|
|
66
158
|
|
|
67
|
-
|
|
68
|
-
|
|
159
|
+
if (data.object || data.object === '') {
|
|
160
|
+
if (!data.array || data.array && !data.array.length) return
|
|
161
|
+
data.type = 'object'
|
|
162
|
+
} else if (data.index || data.index === '') {
|
|
163
|
+
if (!data.array || data.array && !data.array.length) return
|
|
164
|
+
data.type = 'index'
|
|
165
|
+
} else if (data.array || data.array === '')
|
|
166
|
+
data.type = 'array'
|
|
167
|
+
else if (data.database || data.database === '')
|
|
168
|
+
data.type = 'database'
|
|
169
|
+
else if (data.storage || data.storage === '')
|
|
170
|
+
data.type = 'storage'
|
|
171
|
+
else if (data.data)
|
|
172
|
+
data.type = 'data'
|
|
173
|
+
|
|
174
|
+
delete data.isRealtime
|
|
175
|
+
return data
|
|
176
|
+
}
|
|
69
177
|
|
|
70
|
-
|
|
71
|
-
|
|
178
|
+
function initDataKey(element, data) {
|
|
179
|
+
let dataKey = getDataKey(data)
|
|
180
|
+
if (keys.has(dataKey.string))
|
|
181
|
+
keys.get(dataKey.string).elements.set(element, '')
|
|
182
|
+
else
|
|
183
|
+
keys.set(dataKey.string, { elements: new Map([[element, '']]), data, dataKey });
|
|
184
|
+
|
|
185
|
+
elements.set(element, dataKey.string)
|
|
186
|
+
|
|
187
|
+
if (element.parentElement) {
|
|
188
|
+
let form = element.parentElement.closest('form')
|
|
189
|
+
if (form) {
|
|
190
|
+
if (!form.save)
|
|
191
|
+
form.save = () => save(form)
|
|
192
|
+
|
|
193
|
+
if (!form.getData)
|
|
194
|
+
form.getData = () => getData(form)
|
|
195
|
+
|
|
196
|
+
let formObject = forms.get(form)
|
|
197
|
+
if (formObject) {
|
|
198
|
+
formObject.elements.set(element, data)
|
|
199
|
+
if (formObject.types.has(data.type))
|
|
200
|
+
formObject.types.get(data.type).set(element, data)
|
|
201
|
+
else
|
|
202
|
+
formObject.types.set(data.type, new Map([[element, data]]));
|
|
203
|
+
} else
|
|
204
|
+
forms.set(form, { elements: new Map([[element, data]]), types: new Map([[data.type, new Map([[element, data]])]]) });
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (!element.read)
|
|
209
|
+
element.read = () => read(element)
|
|
210
|
+
if (!element.save)
|
|
211
|
+
element.save = () => save(element)
|
|
72
212
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
213
|
+
return dataKey
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function read(element, data, dataKey) {
|
|
217
|
+
if (!dataKey)
|
|
218
|
+
dataKey = elements.get(element)
|
|
219
|
+
if (!data)
|
|
220
|
+
data = { ...keys.get(dataKey).data }
|
|
221
|
+
|
|
222
|
+
let delayTimer = debounce.get(dataKey.string)
|
|
223
|
+
clearTimeout(delayTimer);
|
|
224
|
+
delayTimer = setTimeout(function () {
|
|
225
|
+
debounce.delete(dataKey.string)
|
|
226
|
+
// TODO: should server support string and string array for type object, methods create, read, delete
|
|
227
|
+
if (data.type === 'object') {
|
|
228
|
+
if (typeof data.object === 'string')
|
|
229
|
+
data.object = { _id: data.object }
|
|
230
|
+
|
|
231
|
+
if (!data.object._id.match(/^[0-9a-fA-F]{24}$/))
|
|
232
|
+
return
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
data.method = 'read' + '.' + data.type
|
|
236
|
+
CRUD.send(data).then((data) => {
|
|
237
|
+
setData(element, data);
|
|
238
|
+
})
|
|
239
|
+
}, 500);
|
|
240
|
+
debounce.set(dataKey.string, delayTimer)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function setData(element, data, action) {
|
|
244
|
+
if (!element) {
|
|
245
|
+
element = getDataElements(data)
|
|
246
|
+
if (!element.length) return
|
|
76
247
|
}
|
|
77
248
|
|
|
78
|
-
if (!
|
|
249
|
+
if (!(element instanceof HTMLCollection) && !Array.isArray(element))
|
|
250
|
+
element = [element]
|
|
251
|
+
|
|
252
|
+
let type = data.type
|
|
253
|
+
if (!type && action) {
|
|
254
|
+
type = action.split('.')[0]
|
|
255
|
+
} else if (type == 'key')
|
|
256
|
+
type = 'object'
|
|
257
|
+
|
|
258
|
+
for (let el of element) {
|
|
259
|
+
// if rendered in server side skip
|
|
260
|
+
if (el.hasAttribute('rendered'))
|
|
261
|
+
return el.removeAttribute('rendered');
|
|
262
|
+
|
|
263
|
+
if (!data[type])
|
|
264
|
+
continue;
|
|
265
|
+
|
|
266
|
+
action = el.getAttribute('actions')
|
|
267
|
+
if (action && ['database', 'array', 'object', 'key'].includes(action)) continue;
|
|
268
|
+
|
|
269
|
+
const { key, isRead, isListen, isCrdt } = getAttributes(el);
|
|
270
|
+
if (el.getFilter || el.renderValue)
|
|
271
|
+
filterData(el, data, type, key, action)
|
|
272
|
+
else {
|
|
273
|
+
let value = CRUD.getValueFromObject(data[type][0], key);
|
|
274
|
+
if (key) {
|
|
275
|
+
if (!data[type].length) continue;
|
|
276
|
+
if (isRead == "false" || isCrdt == "true") continue;
|
|
277
|
+
if (isListen == "false" && !data.method.startsWith('read')) continue;
|
|
278
|
+
}
|
|
279
|
+
el.setValue(value);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function filterData(element, data, type, key, action) {
|
|
285
|
+
if (key) {
|
|
286
|
+
if (!data.type) return
|
|
287
|
+
if (Array.isArray(data[type])) {
|
|
288
|
+
let Data = []
|
|
289
|
+
for (let doc of data[type]) {
|
|
290
|
+
if (doc[key]) {
|
|
291
|
+
if (Array.isArray(doc[key]))
|
|
292
|
+
Data.push(...doc[key])
|
|
293
|
+
else
|
|
294
|
+
Data.push(doc[key])
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
let data = Data
|
|
298
|
+
} else {
|
|
299
|
+
data = { [key]: data[type][key] }
|
|
300
|
+
}
|
|
301
|
+
}
|
|
79
302
|
|
|
80
|
-
if (
|
|
81
|
-
|
|
303
|
+
if (element.getFilter && action && !action.startsWith('read'))
|
|
304
|
+
checkFilters(element, data, type, action)
|
|
305
|
+
else if (element.renderValue)
|
|
306
|
+
element.renderValue(data);
|
|
307
|
+
// render({ element, data, key: type });
|
|
308
|
+
else if (data)
|
|
309
|
+
element.setValue(data)
|
|
82
310
|
|
|
83
|
-
|
|
311
|
+
const evt = new CustomEvent('fetchedData', { bubbles: true });
|
|
312
|
+
element.dispatchEvent(evt);
|
|
84
313
|
}
|
|
85
314
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
315
|
+
function checkFilters(element, data, type, action) {
|
|
316
|
+
let Data = element.getData()
|
|
317
|
+
if (!Data) return
|
|
318
|
+
|
|
319
|
+
let newData
|
|
320
|
+
if (type) {
|
|
321
|
+
Data = Data[type]
|
|
322
|
+
newData = data[type]
|
|
323
|
+
} else
|
|
324
|
+
newData = data
|
|
325
|
+
|
|
326
|
+
let filter = element.getFilter()
|
|
327
|
+
if (filter && filter.query)
|
|
328
|
+
newData = queryData(newData, filter.query)
|
|
329
|
+
if (!newData.length)
|
|
330
|
+
return
|
|
90
331
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
332
|
+
if (Array.isArray(Data)) {
|
|
333
|
+
if (Array.isArray(newData)) {
|
|
334
|
+
for (let i = 0; i < newData.length; i++) {
|
|
335
|
+
checkIndex(element, data, Data, newData[i], type, filter, action)
|
|
336
|
+
}
|
|
337
|
+
} else {
|
|
338
|
+
checkIndex(element, data, Data, newData, type, filter, action)
|
|
339
|
+
}
|
|
340
|
+
} else {
|
|
341
|
+
let primaryKey
|
|
342
|
+
if (type === 'object') {
|
|
343
|
+
primaryKey = '_id';
|
|
344
|
+
} else {
|
|
345
|
+
primaryKey = 'key';
|
|
346
|
+
}
|
|
96
347
|
|
|
97
|
-
|
|
98
|
-
|
|
348
|
+
if (Data[primaryKey] === newData[primaryKey]) {
|
|
349
|
+
Data = dotNotationToObject(newData, Data)
|
|
99
350
|
}
|
|
351
|
+
if (element.renderValue)
|
|
352
|
+
element.renderValue(data);
|
|
353
|
+
// render({ element, data, key: type });
|
|
354
|
+
else if (data)
|
|
355
|
+
element.setValue(data)
|
|
100
356
|
}
|
|
101
357
|
}
|
|
102
358
|
|
|
103
|
-
function
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
elements = els.get(key)
|
|
359
|
+
function checkIndex(element, data, Data, newData, type, filter, action) {
|
|
360
|
+
let index
|
|
361
|
+
if (type === 'object') {
|
|
362
|
+
index = Data.findIndex(obj => obj._id === newData._id);
|
|
108
363
|
} else {
|
|
109
|
-
|
|
110
|
-
if (eles && eles.length)
|
|
111
|
-
els.set(key, [...eles, ...elements])
|
|
112
|
-
else
|
|
113
|
-
els.set(key, elements)
|
|
364
|
+
index = Data.findIndex(obj => obj.name === newData.name);
|
|
114
365
|
}
|
|
115
|
-
if (!elements || elements.length == 0)
|
|
116
|
-
return
|
|
117
366
|
|
|
118
|
-
|
|
119
|
-
|
|
367
|
+
if (!data.filter)
|
|
368
|
+
data.filter = {}
|
|
120
369
|
|
|
121
|
-
|
|
122
|
-
if (!
|
|
370
|
+
if (action.startsWith('delete')) {
|
|
371
|
+
if (!index && index !== 0)
|
|
372
|
+
return
|
|
373
|
+
data.filter.remove = true
|
|
374
|
+
} else {
|
|
375
|
+
if (!index && index !== 0) {
|
|
376
|
+
data.filter.create = true
|
|
377
|
+
Data.push(newData)
|
|
378
|
+
} else {
|
|
379
|
+
data.filter.update = true
|
|
380
|
+
data.filter.currentIndex = index
|
|
381
|
+
Data[index] = dotNotationToObject(newData, Data[index])
|
|
382
|
+
}
|
|
123
383
|
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
384
|
+
if (filter && filter.sort) {
|
|
385
|
+
newData.isNewData = true
|
|
386
|
+
Data = sortData(Data, filter.sort)
|
|
387
|
+
index = Data.findIndex(obj => obj.isNewData);
|
|
128
388
|
}
|
|
389
|
+
|
|
390
|
+
if (index >= 0) {
|
|
391
|
+
if (data.filter.currentIndex === index)
|
|
392
|
+
delete data.filter.currentIndex
|
|
393
|
+
data.filter.index = index
|
|
394
|
+
if (element.renderValue)
|
|
395
|
+
element.renderValue(data);
|
|
396
|
+
// render({ element, data, key: type });
|
|
397
|
+
else if (data)
|
|
398
|
+
element.setValue(data)
|
|
399
|
+
}
|
|
400
|
+
|
|
129
401
|
}
|
|
130
402
|
}
|
|
131
403
|
|
|
132
|
-
function
|
|
133
|
-
let
|
|
134
|
-
let attributes = ["storage", "database", "
|
|
404
|
+
function getDataKey(data) {
|
|
405
|
+
let dataKey = {};
|
|
406
|
+
let attributes = ["host", "organization_id", "apikey", "method", "type", "storage", "database", "array", "index", "object", "key", "updateKey", "filter", "upsert", "namespace", "room", "broadcast", "broadcastSender", "broadcastBrowser"];
|
|
135
407
|
|
|
136
408
|
for (let attribute of attributes) {
|
|
137
|
-
|
|
409
|
+
if (attribute === 'key' && data.type === 'object')
|
|
410
|
+
continue
|
|
411
|
+
let value = data[attribute];
|
|
138
412
|
if (value) {
|
|
139
|
-
if (
|
|
140
|
-
|
|
413
|
+
if (Array.isArray(value)) {
|
|
414
|
+
dataKey[attribute] = [...value];
|
|
415
|
+
if (typeof value[0] === 'string')
|
|
416
|
+
dataKey[attribute].sort(); // Sort the values alphabetically
|
|
417
|
+
} else {
|
|
418
|
+
dataKey[attribute] = value;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const object = Object.fromEntries(Object.entries(dataKey).sort(([a], [b]) => a.localeCompare(b)));
|
|
424
|
+
const string = JSON.stringify(object);
|
|
425
|
+
|
|
426
|
+
return { string, object };
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function getDataElements(data) {
|
|
430
|
+
let element = []
|
|
431
|
+
let matchingKeys = getDataKeys(data)
|
|
432
|
+
for (let i = 0; i < matchingKeys.length; i++) {
|
|
433
|
+
let matchingElements = keys.get(matchingKeys[i])
|
|
434
|
+
if (matchingElements && matchingElements.elements && matchingElements.elements.size)
|
|
435
|
+
element.push(...matchingElements.elements.keys())
|
|
436
|
+
}
|
|
437
|
+
return element
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function getDataKeys(data) {
|
|
441
|
+
const matchingKeyStrings = [];
|
|
442
|
+
const targetKeys = ["type", "storage", "database", "array", "index", "object", 'filter'];
|
|
443
|
+
|
|
444
|
+
for (const [keyString, sortedKey] of keys.entries()) {
|
|
445
|
+
let hasMatch = true;
|
|
446
|
+
|
|
447
|
+
for (const key of targetKeys) {
|
|
448
|
+
if (data.hasOwnProperty(key)) {
|
|
449
|
+
if (!sortedKey.data.hasOwnProperty(key)) {
|
|
450
|
+
hasMatch = false;
|
|
451
|
+
break;
|
|
452
|
+
}
|
|
453
|
+
if (Array.isArray(sortedKey.data[key]) && Array.isArray(data[key])) {
|
|
454
|
+
// if key is object check _id
|
|
455
|
+
const matches = sortedKey.data[key].some(value => {
|
|
456
|
+
if (key === 'object') {
|
|
457
|
+
return data[key].some(obj => obj._id === value._id);
|
|
458
|
+
} else {
|
|
459
|
+
return data[key].includes(value)
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
if (!matches) {
|
|
463
|
+
hasMatch = false;
|
|
464
|
+
break;
|
|
465
|
+
}
|
|
466
|
+
} else if (sortedKey.data[key] !== data[key]) {
|
|
467
|
+
hasMatch = false;
|
|
468
|
+
break;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (hasMatch) {
|
|
474
|
+
matchingKeyStrings.push(keyString);
|
|
141
475
|
}
|
|
142
476
|
}
|
|
143
|
-
|
|
144
|
-
return
|
|
477
|
+
|
|
478
|
+
return matchingKeyStrings;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function getData(form) {
|
|
482
|
+
let dataKeys = new Map()
|
|
483
|
+
let formObject = forms.get(form)
|
|
484
|
+
for (let type of formObject.types.values()) {
|
|
485
|
+
for (let [element, data] of type.entries()) {
|
|
486
|
+
let Data = { ...data }
|
|
487
|
+
let dataKey = elements.get(element)
|
|
488
|
+
let value = element.getValue()
|
|
489
|
+
console.log(type, value, data)
|
|
490
|
+
|
|
491
|
+
if (!Data[Data.type] && Data.key) {
|
|
492
|
+
Data.method = 'create.' + Data.type
|
|
493
|
+
if (Data.type === 'object') {
|
|
494
|
+
if (typeof Data.object === 'string')
|
|
495
|
+
Data.object = { _id: Data.object }
|
|
496
|
+
|
|
497
|
+
if (Data.key)
|
|
498
|
+
Data.object[Data.key] = value
|
|
499
|
+
|
|
500
|
+
} else
|
|
501
|
+
Data[Data.type] = value
|
|
502
|
+
|
|
503
|
+
} else if (Data[Data.type] && Data.key) {
|
|
504
|
+
let isUpdate = element.getAttribute('update')
|
|
505
|
+
let isDelete = element.getAttribute('delete')
|
|
506
|
+
let splice = element.getAttribute('splice') // could work on string and array
|
|
507
|
+
let slice = element.getAttribute('slice') // could work on string and array
|
|
508
|
+
|
|
509
|
+
if (/\.([0-9]*)/g.test(Data.key)) {
|
|
510
|
+
|
|
511
|
+
if (splice || splice === "") {
|
|
512
|
+
Data[Data.type][Data.key] = { $splice: value }
|
|
513
|
+
} else if (slice) {
|
|
514
|
+
Data[Data.type][Data.key] = '$delete'
|
|
515
|
+
} else if (isUpdate) {
|
|
516
|
+
value = Data.key.replace(/\[.*?\]/, '[' + value + ']')
|
|
517
|
+
Data.updateKey[Data.key] = value
|
|
518
|
+
Data[Data.type][Data.key] = { $update: value } // $update is string use the value as the key name
|
|
519
|
+
// Data[Data.type][Data.key] = { $update: {[value]: value} } // $update is an object use the key as the value to use for the new key
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
} else if (Data.type = 'object')
|
|
523
|
+
Data.object[Data.key] = value
|
|
524
|
+
else {
|
|
525
|
+
Data[Data.type] = { [Data[Data.type]]: value }
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if (Data.isUpdate || Data.isUpdate === '') {
|
|
529
|
+
if (!Data.key) return
|
|
530
|
+
delete Data.isUpdate
|
|
531
|
+
Data.updateKey = { [Data.key]: value }
|
|
532
|
+
Data.method = 'update.' + Data.type
|
|
533
|
+
} else if (Data.isDelete || Data.isDelete === '') {
|
|
534
|
+
delete Data.isDelete
|
|
535
|
+
if (Data.type == 'object' && Data.key) {
|
|
536
|
+
Data.method = 'update.' + Data.type
|
|
537
|
+
// TODO: Data.type can be a string _id or an array for string _id needs to be converted to object
|
|
538
|
+
if (typeof Data[Data.type] === 'string')
|
|
539
|
+
Data[Data.type] = { _id: Data[Data.type], [Data.key]: undefined }
|
|
540
|
+
else if (Array.isArray(Data[Data.type])) {
|
|
541
|
+
console.log('Data.type is an array function incomplete')
|
|
542
|
+
} else if (typeof Data[Data.type] === 'object')
|
|
543
|
+
Data[Data.type][Data.key] = undefined
|
|
544
|
+
} else {
|
|
545
|
+
Data.method = 'delete.' + Data.type
|
|
546
|
+
}
|
|
547
|
+
} else if (Data.type !== 'object' && Data[Data.type] === '') {
|
|
548
|
+
// if (!Data.key)
|
|
549
|
+
// Data.method = 'read.' + Data.type
|
|
550
|
+
// else if (Data.key === 'name')
|
|
551
|
+
// Data.method = 'create.' + Data.type
|
|
552
|
+
} else if (Data.type !== 'object' && Data[Data.type]) {
|
|
553
|
+
// if (Data.key)
|
|
554
|
+
// Data.method = 'update.' + Data.type
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
//dataKey should be used to group
|
|
560
|
+
let key = dataKey + Data.method
|
|
561
|
+
if (dataKeys.has(key))
|
|
562
|
+
dataKeys.get(key).push(Data)
|
|
563
|
+
else
|
|
564
|
+
dataKeys.set(key, [Data])
|
|
565
|
+
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// TODO: group by methods so we can make one crud request per method
|
|
570
|
+
let dataArray = []
|
|
571
|
+
return dataArray
|
|
145
572
|
}
|
|
146
573
|
|
|
147
574
|
async function save(element) {
|
|
148
|
-
|
|
149
|
-
|
|
575
|
+
if (!element) return;
|
|
576
|
+
let data, value
|
|
577
|
+
if (element.tagName === "FORM") {
|
|
578
|
+
data = element.getData()
|
|
579
|
+
} else {
|
|
580
|
+
let isSave = element.getAttribute('save')
|
|
581
|
+
if (isSave === 'false') return
|
|
582
|
+
|
|
583
|
+
let form = element.closest('form');
|
|
584
|
+
if (form)
|
|
585
|
+
return save(form);
|
|
586
|
+
|
|
587
|
+
let dataKey = elements.get(element)
|
|
588
|
+
data = { ...keys.get(dataKey).data }
|
|
589
|
+
|
|
590
|
+
value = element.getValue();
|
|
591
|
+
|
|
592
|
+
if (typeof data[data.type] === 'string')
|
|
593
|
+
data[data.type] = { _id: data[data.type], [data.key]: value }
|
|
594
|
+
else if (Array.isArray(data[data.type])) {
|
|
595
|
+
console.log('data.type is an array function incomplete')
|
|
596
|
+
} else if (typeof data[data.type] === 'object')
|
|
597
|
+
data[data.type][data.key] = value
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
if (/\.([0-9]*)/g.test(data.key)) {
|
|
601
|
+
let splice = element.getAttribute('splice')
|
|
602
|
+
let slice = element.getAttribute('slice')
|
|
603
|
+
let update = element.getAttribute('update')
|
|
604
|
+
|
|
605
|
+
if (splice || splice === "") {
|
|
606
|
+
data[data.type][data.key] = { $splice: value }
|
|
607
|
+
} else if (slice) {
|
|
608
|
+
data[data.type][data.key] = '$delete'
|
|
609
|
+
} else if (update) {
|
|
610
|
+
value = data.key.replace(/\[.*?\]/, '[' + value + ']')
|
|
611
|
+
data.updateKey[data.key] = value
|
|
612
|
+
data[data.type][data.key] = { $update: value } // $update is string use the value as the key name
|
|
613
|
+
// data[data.type][data.key] = { $update: {[value]: value} } // $update is an object use the key as the value to use for the new key
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
data = [data]
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
for (let i = 0; i < data.length; i++) {
|
|
622
|
+
if (data[i].method && data[i].method.startsWith('create'))
|
|
623
|
+
element.setAttribute(data[i].type, 'pending');
|
|
624
|
+
else if (data[i].method && data[i].method.startsWith('update') && data[i].type == 'object' && typeof value == 'string' && window.CoCreate.crdt && !'crdt') {
|
|
625
|
+
return window.CoCreate.crdt.replaceText({
|
|
626
|
+
array: data[i].array,
|
|
627
|
+
key: data[i].key,
|
|
628
|
+
object: data[i].object._id,
|
|
629
|
+
value
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
data[i] = await CRUD.send(data[i]);
|
|
634
|
+
|
|
635
|
+
if (data[i] && (data[i].method.startsWith('create') || data[i].type !== 'object' && data[i].method.startsWith('update'))) {
|
|
636
|
+
setTypeValue(element, data[i])
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
}
|
|
640
|
+
|
|
150
641
|
}
|
|
151
642
|
|
|
152
|
-
function
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
643
|
+
function setTypeValue(element, data) {
|
|
644
|
+
// TODO: if an array name is updated, the attibute array="" needs to be updated.
|
|
645
|
+
|
|
646
|
+
if (!data) return;
|
|
647
|
+
|
|
648
|
+
let form
|
|
649
|
+
if (element.tagName === "FORM")
|
|
650
|
+
form = element
|
|
651
|
+
else if (element.parentElement)
|
|
652
|
+
form = element.parentElement.closest('form')
|
|
653
|
+
|
|
654
|
+
if (!form) {
|
|
655
|
+
if (data.type === 'object') {
|
|
656
|
+
element.setAttribute('object', data.object[0]._id)
|
|
657
|
+
} else {
|
|
658
|
+
element.setAttribute(data.type, data[data.type].name)
|
|
659
|
+
}
|
|
660
|
+
} else {
|
|
661
|
+
let formObject = forms.get(form)
|
|
662
|
+
let elements = formObject.types.get(data.type)
|
|
663
|
+
|
|
664
|
+
for (let [el, Data] of elements.entries()) {
|
|
665
|
+
if (data.type === 'object') {
|
|
666
|
+
if (!Data.object || Data.object === 'pending') {
|
|
667
|
+
Data.object = data.object[0]._id
|
|
668
|
+
el.setAttribute('object', data.object[0]._id)
|
|
669
|
+
}
|
|
670
|
+
} else if (!Data[data.type]) {
|
|
671
|
+
Data[data.type] = data[data.type].name
|
|
672
|
+
el.setAttribute(data.type, data[data.type].name)
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// const pass_ids = new Map();
|
|
677
|
+
|
|
678
|
+
// let pass_id = form.getAttribute('pass_id');
|
|
679
|
+
// if (pass_id) {
|
|
680
|
+
// // Set pass_ids to pass_ids if array is not set
|
|
681
|
+
// if (form.getAttribute('array') == array)
|
|
682
|
+
// pass_ids.set(pass_id, '');
|
|
683
|
+
// }
|
|
684
|
+
|
|
685
|
+
// let objectId = el.getAttribute('object');
|
|
686
|
+
// let key = el.getAttribute('key')
|
|
687
|
+
// // set object and pass_id attributes to the object id if the object id is pending
|
|
688
|
+
// if (key && (objectId == '' || objectId == 'pending')) {
|
|
689
|
+
// el.setAttribute('object', id);
|
|
690
|
+
// // Set the id attribute of the element
|
|
691
|
+
// if (key == '_id')
|
|
692
|
+
// el.setValue(id)
|
|
693
|
+
// let pass_id = el.getAttribute('pass_id');
|
|
694
|
+
// // Set the pass id to the pass_ids.
|
|
695
|
+
// if (pass_id) {
|
|
696
|
+
// pass_ids.set(pass_id, '');
|
|
697
|
+
// }
|
|
698
|
+
|
|
699
|
+
// if (el.hasAttribute('pass-object')) {
|
|
700
|
+
// let passObjectId = el.getAttribute('pass-object');
|
|
701
|
+
// // Set the pass object id if not set.
|
|
702
|
+
// if (passObjectId == '') {
|
|
703
|
+
// el.setAttribute('pass-object', id);
|
|
704
|
+
// let pass_id = el.getAttribute('pass_id');
|
|
705
|
+
// // Set the pass id to the pass_ids.
|
|
706
|
+
// if (pass_id) {
|
|
707
|
+
// pass_ids.set(pass_id, '');
|
|
708
|
+
// }
|
|
709
|
+
// }
|
|
710
|
+
// }
|
|
711
|
+
|
|
712
|
+
// // Set the object attribute of all pass_ids to the object attribute of all pass_ids
|
|
713
|
+
// if (pass_ids.size > 0) {
|
|
714
|
+
// for (let key of pass_ids.keys()) {
|
|
715
|
+
// let passEls = document.querySelectorAll(`[pass_id="${key}"]`)
|
|
716
|
+
// for (let passEl of passEls) {
|
|
717
|
+
// // if (passEl.getAttribute('array') == array){
|
|
718
|
+
// // Set the object id attribute to the passEl s object if it is not set.
|
|
719
|
+
// if (passEl.getAttribute('object') == '') {
|
|
720
|
+
// passEl.setAttribute('object', id);
|
|
721
|
+
// }
|
|
722
|
+
// // }
|
|
723
|
+
// }
|
|
724
|
+
// }
|
|
725
|
+
// }
|
|
726
|
+
// }
|
|
727
|
+
}
|
|
156
728
|
}
|
|
157
729
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
730
|
+
async function remove(element) {
|
|
731
|
+
if (element && !(element instanceof HTMLCollection) && !Array.isArray(element))
|
|
732
|
+
element = [element]
|
|
733
|
+
for (let i = 0; i < element.length; i++) {
|
|
734
|
+
let key = elements.get(element[i])
|
|
735
|
+
if (key) {
|
|
736
|
+
let els = keys.get(key).elements
|
|
737
|
+
els.delete(element[i])
|
|
738
|
+
elements.delete(key)
|
|
739
|
+
debounce.delete(element[i])
|
|
740
|
+
// TODO: delete element from forms map
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
function initSocket() {
|
|
746
|
+
const array = ['create', 'update', 'delete'];
|
|
747
|
+
const attributes = ["storage", "database", "array", "index", "object", 'filter'];
|
|
748
|
+
|
|
749
|
+
for (let i = 0; i < array.length; i++) {
|
|
750
|
+
for (let j = 0; j < attributes.length; j++) {
|
|
751
|
+
const action = array[i] + '.' + attributes[j];
|
|
752
|
+
|
|
753
|
+
CRUD.listen(action, function (data) {
|
|
754
|
+
setData(null, data, action);
|
|
167
755
|
});
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
CRUD.listen('sync', function (data) {
|
|
760
|
+
setData(null, data, action);
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
}
|
|
168
764
|
|
|
169
|
-
|
|
765
|
+
Observer.init({
|
|
766
|
+
name: 'render',
|
|
767
|
+
observe: ['addedNodes'],
|
|
768
|
+
target: '[render-clone]',
|
|
769
|
+
callback: function (mutation) {
|
|
770
|
+
let renderedNode = CoCreate.render.renderedNodes.get(mutation.target)
|
|
771
|
+
if (!renderedNode) return
|
|
772
|
+
|
|
773
|
+
if (!mutation.movedFrom) return
|
|
774
|
+
let draggedEl = mutation.target
|
|
775
|
+
let draggedFrom = mutation.movedFrom.parentElement
|
|
776
|
+
let droppedEl = mutation.target.nextElementSibling || mutation.target.previousElementSibling
|
|
777
|
+
let droppedIn = mutation.parentElement
|
|
778
|
+
|
|
779
|
+
if (!draggedFrom || !droppedIn) return
|
|
780
|
+
dndCrud(draggedEl, draggedFrom, droppedEl, droppedIn)
|
|
781
|
+
}
|
|
782
|
+
})
|
|
783
|
+
|
|
784
|
+
// TODO: has the potential to work on most of the crud elements specifically if the value is an object or an array
|
|
785
|
+
function dndCrud(draggedEl, draggedFrom, droppedEl, droppedIn) {
|
|
786
|
+
let from = dndCrudData(draggedEl, draggedFrom, 'remove')
|
|
787
|
+
let to = dndCrudData(droppedEl, droppedIn, 'add')
|
|
788
|
+
|
|
789
|
+
if (from && to && !draggedFrom.isSameNode(droppedIn)) {
|
|
790
|
+
let element = getDataElements(from.data)
|
|
791
|
+
if (!element.length) return
|
|
792
|
+
|
|
793
|
+
let match = element.find(obj => obj === droppedIn);
|
|
794
|
+
if (!match) {
|
|
795
|
+
if (from.keyPath.includes('.')) {
|
|
796
|
+
let test = { [from.keyPath]: undefined }
|
|
797
|
+
|
|
798
|
+
from.newData = dotNotationToObject(test, from.newData)
|
|
799
|
+
dndCrudSend(from.newData, 'update')
|
|
800
|
+
} else {
|
|
801
|
+
const index = from.keyPath.match(/\[(\d+)\]/)
|
|
802
|
+
let removeData = from.newData[from.newData.type].splice(index, 1)[0];
|
|
803
|
+
if (removeData) {
|
|
804
|
+
removeData.type = from.newData.type
|
|
805
|
+
removeData[removeData.type] = removeData
|
|
806
|
+
dndCrudSend(removeData, 'delete')
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
if (from.newData[from.newData.type].length)
|
|
810
|
+
dndCrudSend(from.newData, 'update')
|
|
811
|
+
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
if (to)
|
|
817
|
+
dndCrudSend(to.newData, 'update')
|
|
818
|
+
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
function dndCrudData(element, parent, operator) {
|
|
822
|
+
if (!elements.has(parent)) return
|
|
823
|
+
let data = parent.getData()
|
|
824
|
+
let newData = getObject(parent)
|
|
825
|
+
|
|
826
|
+
let { Data, sortName, sortDirection, keyPath, clones, index } = dndNewData(element, data)
|
|
827
|
+
newData[newData.type] = []
|
|
828
|
+
|
|
829
|
+
if (sortName) {
|
|
830
|
+
for (let i = 0; i < clones.length; i++) {
|
|
831
|
+
if (i > index) {
|
|
832
|
+
let previousData = data[data.type][index]
|
|
833
|
+
if (operator === 'add')
|
|
834
|
+
Data[sortName] = i + 1
|
|
835
|
+
else if (operator === 'remove')
|
|
836
|
+
Data[sortName] = i - 1
|
|
837
|
+
|
|
838
|
+
newData[newData.type].push({ ...previousData, ...Data })
|
|
839
|
+
}
|
|
170
840
|
}
|
|
841
|
+
} else {
|
|
842
|
+
newData[newData.type] = [{ ...data[data.type][index], ...Data }]
|
|
171
843
|
}
|
|
844
|
+
|
|
845
|
+
return { data, newData, keyPath, clones, index }
|
|
172
846
|
}
|
|
173
847
|
|
|
174
|
-
|
|
848
|
+
function dndNewData(element, data) {
|
|
849
|
+
let Data = {}
|
|
850
|
+
let query = data.filter.query
|
|
851
|
+
if (query && query.length) {
|
|
852
|
+
for (let i = 0; i < query.length; i++) {
|
|
853
|
+
if (query.operator === "$eq")
|
|
854
|
+
Data[query.key] = query.value
|
|
855
|
+
if (query.operator === "$ne")
|
|
856
|
+
Data[query.key] = query.value
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
let sortName, sortDirection
|
|
861
|
+
let sort = data.filter.sort
|
|
862
|
+
if (sort && sort.length) {
|
|
863
|
+
// for (let i = 0; i < sort.length; i++) {
|
|
864
|
+
for (let i = sort.length - 1; i >= 0; i--) {
|
|
865
|
+
if (typeof data[sort.key] === 'number') {
|
|
866
|
+
sortName = sort.key
|
|
867
|
+
sortDirection = sort.direction
|
|
868
|
+
break
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
let keyPath, clonesMap, clones, index
|
|
874
|
+
|
|
875
|
+
let renderedNode = CoCreate.render.renderedNodes.get(element)
|
|
876
|
+
if (renderedNode) {
|
|
877
|
+
keyPath = renderedNode.keyPath
|
|
878
|
+
clonesMap = renderedNode.template.clones
|
|
879
|
+
clones = Array.from(clonesMap.values());
|
|
880
|
+
index = clones.indexOf(element);
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
return { Data, sortName, sortDirection, keyPath, clones, index }
|
|
884
|
+
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
function dndCrudSend(data, crudType) {
|
|
888
|
+
if (CRUD) {
|
|
889
|
+
let action = data.type;
|
|
890
|
+
action = action.charAt(0).toUpperCase() + action.slice(1);
|
|
891
|
+
CRUD[crudType + action](data)
|
|
892
|
+
} else
|
|
893
|
+
console.log('dnd reordered data set as crud is unavailable')
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
Observer.init({
|
|
175
897
|
name: 'CoCreateElementsChildList',
|
|
176
898
|
observe: ['childList'],
|
|
177
899
|
target: selector,
|
|
178
900
|
callback: function (mutation) {
|
|
179
|
-
|
|
901
|
+
init(mutation.addedNodes);
|
|
180
902
|
}
|
|
181
903
|
});
|
|
182
904
|
|
|
183
|
-
|
|
905
|
+
Observer.init({
|
|
906
|
+
name: 'CoCreateElementsRemovedNodes',
|
|
907
|
+
observe: ['removedNodes'],
|
|
908
|
+
target: selector,
|
|
909
|
+
callback: function (mutation) {
|
|
910
|
+
if (mutation.target.parentElement) return
|
|
911
|
+
remove(mutation.target)
|
|
912
|
+
}
|
|
913
|
+
});
|
|
914
|
+
|
|
915
|
+
Observer.init({
|
|
184
916
|
name: 'CoCreateElementsAttributes',
|
|
185
917
|
observe: ['attributes'],
|
|
186
|
-
attributeName:
|
|
918
|
+
attributeName: getAttributeNames(['storage', 'database', 'array', 'object', 'key']),
|
|
187
919
|
target: selector,
|
|
188
920
|
callback: function (mutation) {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
initElements([mutation.target]);
|
|
921
|
+
remove(mutation.target)
|
|
922
|
+
init([mutation.target]);
|
|
192
923
|
}
|
|
193
924
|
});
|
|
194
925
|
|
|
195
|
-
|
|
196
|
-
name: "
|
|
197
|
-
endEvent: "
|
|
198
|
-
callback: (
|
|
199
|
-
|
|
926
|
+
Actions.init({
|
|
927
|
+
name: "save",
|
|
928
|
+
endEvent: "saved",
|
|
929
|
+
callback: (action) => {
|
|
930
|
+
const form = action.element.closest("form");
|
|
200
931
|
save(form);
|
|
201
|
-
}
|
|
932
|
+
}
|
|
202
933
|
});
|
|
203
934
|
|
|
204
935
|
init();
|
|
205
936
|
|
|
206
|
-
export default {
|
|
937
|
+
export default { init, read, save, getData, reset, elements, keys, forms, debounce, getAttributes };
|