@cocreate/events 1.29.0 → 1.29.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/CHANGELOG.md +7 -0
- package/package.json +1 -1
- package/src/index.js +660 -1116
package/src/index.js
CHANGED
|
@@ -26,1128 +26,672 @@ import observer from "@cocreate/observer";
|
|
|
26
26
|
import "@cocreate/element-prototype";
|
|
27
27
|
|
|
28
28
|
const CoCreateEvents = {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const root = rootSelector
|
|
227
|
-
? document.querySelector(rootSelector)
|
|
228
|
-
: null;
|
|
229
|
-
|
|
230
|
-
// Validate and filter threshold values
|
|
231
|
-
const validThreshold = Array.isArray(threshold)
|
|
232
|
-
? threshold.filter((value) => value >= 0 && value <= 1)
|
|
233
|
-
: [0];
|
|
234
|
-
|
|
235
|
-
// Returns true if attribute is present AND does not equal "false"
|
|
236
|
-
const onceAttr = el.getAttribute(`${prefix}-once`);
|
|
29
|
+
elements2: new Map(),
|
|
30
|
+
init: function (prefix, events) {
|
|
31
|
+
if (prefix && events) {
|
|
32
|
+
this.initPrefix(prefix, events);
|
|
33
|
+
} else {
|
|
34
|
+
this.initPrefix("toggle", ["click"]);
|
|
35
|
+
this.initPrefix("click", ["click"]);
|
|
36
|
+
this.initPrefix("hover", ["mouseover", "mouseout"]);
|
|
37
|
+
this.initPrefix("mouseover", ["mouseover"]);
|
|
38
|
+
this.initPrefix("mouseout", ["mouseout"]);
|
|
39
|
+
this.initPrefix("input", ["input"]);
|
|
40
|
+
this.initPrefix("change", ["change"]);
|
|
41
|
+
this.initPrefix("selected", ["click"]);
|
|
42
|
+
this.initPrefix("onload", ["onload"]);
|
|
43
|
+
this.initPrefix("observe", ["observer"]);
|
|
44
|
+
this.initPrefix("intersection", ["intersection"]);
|
|
45
|
+
this.initPrefix("resize", ["onload", "resize"]);
|
|
46
|
+
this.initPrefix("localstorage", ["onload"]);
|
|
47
|
+
this.initPrefix("focus", ["focus"]);
|
|
48
|
+
this.initPrefix("blur", ["blur"]);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let customEventEls = document.querySelectorAll("[event-name]");
|
|
52
|
+
let names = {};
|
|
53
|
+
for (let customEventEl of customEventEls) {
|
|
54
|
+
let name = customEventEl.getAttribute("event-name");
|
|
55
|
+
name = name.split(",");
|
|
56
|
+
for (let i = 0; i < name.length; i++) {
|
|
57
|
+
name[i].trim();
|
|
58
|
+
if (!names[name]) {
|
|
59
|
+
names[name] = name;
|
|
60
|
+
this.initPrefix(name);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const self = this;
|
|
66
|
+
observer.init({
|
|
67
|
+
name: "CoCreateEventName",
|
|
68
|
+
types: ["addedNodes"],
|
|
69
|
+
selector: `[event-name]`,
|
|
70
|
+
callback: function (mutation) {
|
|
71
|
+
let name = mutation.target.getAttribute("event-name");
|
|
72
|
+
self.initPrefix(name);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
initPrefix: function (prefix, events) {
|
|
78
|
+
const self = this;
|
|
79
|
+
|
|
80
|
+
action.init({
|
|
81
|
+
name: prefix,
|
|
82
|
+
endEvent: `${prefix}End`,
|
|
83
|
+
callback: (data) => {
|
|
84
|
+
this.__updateElements(
|
|
85
|
+
data.element,
|
|
86
|
+
prefix,
|
|
87
|
+
null,
|
|
88
|
+
null,
|
|
89
|
+
data.params
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// We look for the prefix as an attribute, or common options
|
|
95
|
+
let selector = `[${prefix}], [${prefix}\\.group], [${prefix}\\.query]`;
|
|
96
|
+
|
|
97
|
+
observer.init({
|
|
98
|
+
name: "CoCreateEventAttributes",
|
|
99
|
+
types: ["attributes", "addedNodes"],
|
|
100
|
+
// removed attributeFilter to allow discovery of dynamic attributes (prefix-src, prefix.option, etc.)
|
|
101
|
+
selector,
|
|
102
|
+
callback: function (mutation) {
|
|
103
|
+
// Filter relevant mutations here since attributeFilter is gone
|
|
104
|
+
if (mutation.type === 'attributes') {
|
|
105
|
+
if (mutation.attributeName.startsWith(prefix)) {
|
|
106
|
+
self.initElements([mutation.target], prefix, events);
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
self.initElements([mutation.target], prefix, events);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
let elements = document.querySelectorAll(selector);
|
|
115
|
+
this.initElements(elements, prefix, events);
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
elements: new Map(),
|
|
119
|
+
initElements: function (elements, prefix, events = []) {
|
|
120
|
+
const self = this;
|
|
121
|
+
for (const el of elements) {
|
|
122
|
+
let isEventable = true;
|
|
123
|
+
let prefixes = this.elements.get(el);
|
|
124
|
+
if (!prefixes) {
|
|
125
|
+
prefixes = { [prefix]: { events } };
|
|
126
|
+
this.elements.set(el, prefixes);
|
|
127
|
+
} else if (!prefixes[prefix]) {
|
|
128
|
+
prefixes[prefix] = { events };
|
|
129
|
+
} else {
|
|
130
|
+
isEventable = false;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
let customEvents = el.getAttribute(`${prefix}.events`);
|
|
134
|
+
if (customEvents) {
|
|
135
|
+
customEvents = customEvents.split(",");
|
|
136
|
+
for (let i = 0; i < customEvents.length; i++)
|
|
137
|
+
customEvents[i] = customEvents[i].trim();
|
|
138
|
+
|
|
139
|
+
for (let i = 0; i < events.length; i++)
|
|
140
|
+
el.removeEventListener(events[i], eventFunction);
|
|
141
|
+
|
|
142
|
+
events = customEvents;
|
|
143
|
+
prefixes[prefix].events = events;
|
|
144
|
+
isEventable = true;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (!events || !isEventable) continue;
|
|
148
|
+
|
|
149
|
+
if (events.includes("onload")) {
|
|
150
|
+
this.__updateElements(el, prefix);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (events.includes("observer")) {
|
|
154
|
+
let observerSelector = (
|
|
155
|
+
el.getAttribute(`${prefix}.selector`) ||
|
|
156
|
+
el.getAttribute(`${prefix}.query`) || ""
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
let attributeFilter = (
|
|
160
|
+
el.getAttribute(`${prefix}.attributeFilter`) || ""
|
|
161
|
+
)
|
|
162
|
+
.split(/\s*,\s*/)
|
|
163
|
+
.filter((item) => item);
|
|
164
|
+
|
|
165
|
+
let observerTypes = (
|
|
166
|
+
el.getAttribute(`${prefix}.types`) || ""
|
|
167
|
+
)
|
|
168
|
+
.split(/\s*,\s*/)
|
|
169
|
+
.filter((item) => item);
|
|
170
|
+
|
|
171
|
+
let observerConfig = {
|
|
172
|
+
types: observerTypes,
|
|
173
|
+
callback: function (mutation) {
|
|
174
|
+
self.__updateElements(el, prefix);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
if (observerSelector) {
|
|
179
|
+
if (!observerTypes.length &&
|
|
180
|
+
!attributeFilter.length
|
|
181
|
+
) {
|
|
182
|
+
observerConfig.types.push("addedNodes");
|
|
183
|
+
}
|
|
184
|
+
observerConfig.selector = observerSelector;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (attributeFilter.length) {
|
|
188
|
+
observerConfig.types.push("attributes");
|
|
189
|
+
observerConfig.attributeFilter = attributeFilter;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (observerConfig.types.length) {
|
|
193
|
+
observer.init(observerConfig);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (events.includes("resize")) {
|
|
198
|
+
const resizeObserver = new ResizeObserver((entries) => {
|
|
199
|
+
const lastEntry = entries[entries.length - 1];
|
|
200
|
+
const { width, height } = lastEntry.contentRect;
|
|
201
|
+
// Note: We're setting a standard attribute here for use by logic later
|
|
202
|
+
el.setAttribute(`${prefix}-if-value`, width + "px");
|
|
203
|
+
self.__updateElements(el, prefix);
|
|
204
|
+
});
|
|
205
|
+
resizeObserver.observe(el);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (events.includes("intersection")) {
|
|
209
|
+
const rootSelector = el.getAttribute(`${prefix}.root`);
|
|
210
|
+
const rootMargin =
|
|
211
|
+
el.getAttribute(`${prefix}.rootMargin`) || "0px";
|
|
212
|
+
const threshold = el.getAttribute(`${prefix}.threshold`)
|
|
213
|
+
? JSON.parse(el.getAttribute(`${prefix}.threshold`))
|
|
214
|
+
: [0];
|
|
215
|
+
const trigger = el.getAttribute(`${prefix}.trigger`) || "both"; // Default to "both"
|
|
216
|
+
|
|
217
|
+
const root = rootSelector
|
|
218
|
+
? document.querySelector(rootSelector)
|
|
219
|
+
: null;
|
|
220
|
+
|
|
221
|
+
const validThreshold = Array.isArray(threshold)
|
|
222
|
+
? threshold.filter((value) => value >= 0 && value <= 1)
|
|
223
|
+
: [0];
|
|
224
|
+
|
|
225
|
+
const onceAttr = el.getAttribute(`${prefix}.once`);
|
|
237
226
|
const once = onceAttr !== null && onceAttr !== "false";
|
|
238
227
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
el,
|
|
260
|
-
prefix
|
|
261
|
-
);
|
|
262
|
-
// Disconnect if once is true
|
|
228
|
+
const intersectionObserver = new IntersectionObserver(
|
|
229
|
+
(entries) => {
|
|
230
|
+
entries.forEach((entry) => {
|
|
231
|
+
const isVisible = entry.isIntersecting;
|
|
232
|
+
|
|
233
|
+
if (
|
|
234
|
+
(trigger === "visible" && isVisible) ||
|
|
235
|
+
(trigger === "not-visible" && !isVisible) ||
|
|
236
|
+
trigger === "both"
|
|
237
|
+
) {
|
|
238
|
+
el.setAttribute(
|
|
239
|
+
`${prefix}-is-visible`,
|
|
240
|
+
isVisible ? "true" : "false"
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
self.__updateElements(
|
|
244
|
+
el,
|
|
245
|
+
prefix
|
|
246
|
+
);
|
|
247
|
+
|
|
263
248
|
if (once) {
|
|
264
249
|
intersectionObserver.unobserve(entry.target);
|
|
265
250
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
for (let style of newValue) {
|
|
709
|
-
let [property, value] = style.split(":");
|
|
710
|
-
element.style[property] = value;
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
} else {
|
|
714
|
-
if (attrName === "value") {
|
|
715
|
-
oldValue = await element.getValue();
|
|
716
|
-
} else if (
|
|
717
|
-
attrName === "text" ||
|
|
718
|
-
attrName === "html" ||
|
|
719
|
-
element.getAttribute(attrName)
|
|
720
|
-
) {
|
|
721
|
-
attrValues = element
|
|
722
|
-
.getAttribute(attrName)
|
|
723
|
-
.split(" ")
|
|
724
|
-
.map((x) => x.trim());
|
|
725
|
-
oldValue =
|
|
726
|
-
values.filter((x) => attrValues.includes(x))[0] || "";
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
let newValue = this.__getNextValue(values, oldValue);
|
|
730
|
-
if (deactivate) newValue = "";
|
|
731
|
-
if (attrName === "class") {
|
|
732
|
-
if (oldValue) {
|
|
733
|
-
oldValue.split(" ").forEach((className) => {
|
|
734
|
-
element.classList.remove(className);
|
|
735
|
-
});
|
|
736
|
-
if (values.length === 1) {
|
|
737
|
-
return;
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
if (newValue) {
|
|
742
|
-
newValue.split(" ").forEach((className) => {
|
|
743
|
-
element.classList.add(className);
|
|
744
|
-
});
|
|
745
|
-
}
|
|
746
|
-
} else if (attrName === "value") {
|
|
747
|
-
element.setValue(newValue, false);
|
|
748
|
-
} else if (
|
|
749
|
-
["$click", "$focus", "$blur", "$save", "$read"].includes(
|
|
750
|
-
attrName
|
|
751
|
-
)
|
|
752
|
-
) {
|
|
753
|
-
element[attrName.substring(1)]();
|
|
754
|
-
} else if (attrName) {
|
|
755
|
-
// TODO: removeAttribute vs setting empty value, how best to define. operator $remove ???
|
|
756
|
-
if (newValue === "" && element.hasAttribute(attrName)) {
|
|
757
|
-
element.removeAttribute(attrName);
|
|
758
|
-
} else {
|
|
759
|
-
element.setAttribute(attrName, newValue);
|
|
760
|
-
}
|
|
761
|
-
} else if (targetPosition) {
|
|
762
|
-
if (domTextEditor) {
|
|
763
|
-
CoCreate.text.insertAdjacentElement({
|
|
764
|
-
domTextEditor,
|
|
765
|
-
position: targetPosition,
|
|
766
|
-
target: element,
|
|
767
|
-
elementValue: newValue
|
|
768
|
-
});
|
|
769
|
-
} else if (targetPosition === "replace") {
|
|
770
|
-
element.insertAdjacentHTML("beforebegin", newValue); // Insert before oldElement
|
|
771
|
-
element.remove(); // Remove oldElement
|
|
772
|
-
} else {
|
|
773
|
-
element.insertAdjacentHTML(targetPosition, newValue);
|
|
774
|
-
}
|
|
775
|
-
} else {
|
|
776
|
-
if (
|
|
777
|
-
!attrName &&
|
|
778
|
-
["click", "focus", "blur"].includes(newValue)
|
|
779
|
-
) {
|
|
780
|
-
element[newValue]();
|
|
781
|
-
} else if (["click", "focus", "blur"].includes(attrName)) {
|
|
782
|
-
element[attrName]();
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
},
|
|
788
|
-
|
|
789
|
-
__getNextValue: function (values, val) {
|
|
790
|
-
let size = values.length;
|
|
791
|
-
let nn = values.indexOf(val);
|
|
792
|
-
if (nn == -1) {
|
|
793
|
-
return values[0];
|
|
794
|
-
} else {
|
|
795
|
-
return values[(nn + 1) % size];
|
|
796
|
-
}
|
|
797
|
-
}
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
},
|
|
254
|
+
{ root, rootMargin, threshold: validThreshold }
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
intersectionObserver.observe(el);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
for (let i = 0; i < events.length; i++) {
|
|
261
|
+
if (
|
|
262
|
+
events[i] === "onload" ||
|
|
263
|
+
events[i] === "observer" ||
|
|
264
|
+
events[i] === "intersection" ||
|
|
265
|
+
events[i] === "resize"
|
|
266
|
+
) {
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
el.removeEventListener(events[i], eventFunction);
|
|
271
|
+
|
|
272
|
+
const options = {};
|
|
273
|
+
// Updated to use dot notation for event options
|
|
274
|
+
const attributes = ["bubbles", "cancelable", "composed"];
|
|
275
|
+
|
|
276
|
+
for (let j = 0; j < attributes.length; j++) {
|
|
277
|
+
const attrValue = el.getAttribute(
|
|
278
|
+
`${events[i]}.${attributes[j]}`
|
|
279
|
+
);
|
|
280
|
+
if (attrValue !== null) {
|
|
281
|
+
options[attributes[j]] = attrValue !== "false";
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
el.addEventListener(events[i], eventFunction, options);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function eventFunction(event) {
|
|
289
|
+
const target = event.currentTarget;
|
|
290
|
+
if (target) {
|
|
291
|
+
let prefixes = self.elements.get(target);
|
|
292
|
+
if (
|
|
293
|
+
prefixes[prefix].prev === event.type &&
|
|
294
|
+
["mouseover", "mouseout"].includes(event.type)
|
|
295
|
+
)
|
|
296
|
+
return;
|
|
297
|
+
else prefixes[prefix].prev = event.type;
|
|
298
|
+
|
|
299
|
+
// Updated to use dot notation for 'actions' attribute check if applicable
|
|
300
|
+
let attribute = target.getAttribute("actions") || "";
|
|
301
|
+
if (attribute.includes(prefix)) return;
|
|
302
|
+
|
|
303
|
+
// Handle lifecycle using dot notation
|
|
304
|
+
const lifecycleAttributes = [
|
|
305
|
+
"preventDefault",
|
|
306
|
+
"stopPropagation",
|
|
307
|
+
"stopImmediatePropagation"
|
|
308
|
+
];
|
|
309
|
+
|
|
310
|
+
for (let i = 0; i < lifecycleAttributes.length; i++) {
|
|
311
|
+
const attr = `${prefix}.${lifecycleAttributes[i]}`;
|
|
312
|
+
const attrValue = target.getAttribute(attr);
|
|
313
|
+
|
|
314
|
+
if (attrValue !== null && attrValue !== "false") {
|
|
315
|
+
event[lifecycleAttributes[i]]();
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
self.__updateElements(
|
|
320
|
+
target,
|
|
321
|
+
prefix,
|
|
322
|
+
prefixes[prefix].events
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
|
|
329
|
+
__updateElements: async function (rootElement, prefix, events, params) {
|
|
330
|
+
if (!rootElement.isConnected) return;
|
|
331
|
+
|
|
332
|
+
// 1. Determine targets based on prefix.group option
|
|
333
|
+
let sourceElements = [rootElement];
|
|
334
|
+
let sourceGroup = rootElement.getAttribute(`${prefix}.group`);
|
|
335
|
+
if (sourceGroup) {
|
|
336
|
+
// Looking for targets that identify with this group
|
|
337
|
+
// Using dot notation for the target marker as well for consistency
|
|
338
|
+
sourceElements = document.querySelectorAll(`[${prefix}\\.group="${sourceGroup}"]`);
|
|
339
|
+
// sourceElements = queryElements({
|
|
340
|
+
// element: rootElement,
|
|
341
|
+
// selector: `[${prefix}\\.group="${sourceGroup}"]`
|
|
342
|
+
// });
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// 2. Iterate through sourceElements
|
|
346
|
+
for (let sourceElement of sourceElements) {
|
|
347
|
+
|
|
348
|
+
// Handle Global Options for this sourceElement processing
|
|
349
|
+
|
|
350
|
+
// onEvent
|
|
351
|
+
let onEvent = sourceElement.getAttribute(`${prefix}.on`);
|
|
352
|
+
if (onEvent) {
|
|
353
|
+
await new Promise((resolve, reject) => {
|
|
354
|
+
const handleEvent = () => {
|
|
355
|
+
document.removeEventListener(onEvent, handleEvent);
|
|
356
|
+
resolve();
|
|
357
|
+
};
|
|
358
|
+
document.addEventListener(onEvent, handleEvent);
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// setInterval
|
|
363
|
+
let interval = sourceElement.getAttribute(`${prefix}.setInterval`);
|
|
364
|
+
if (interval !== null) {
|
|
365
|
+
let intervalMs = parseInt(interval, 10) || 0;
|
|
366
|
+
if (!sourceElement.eventsInterval) {
|
|
367
|
+
sourceElement.eventsInterval = {};
|
|
368
|
+
}
|
|
369
|
+
let intervalEntry = sourceElement.eventsInterval[prefix];
|
|
370
|
+
if (intervalMs > 0) {
|
|
371
|
+
if (!intervalEntry || intervalEntry.delay !== intervalMs) {
|
|
372
|
+
if (intervalEntry) {
|
|
373
|
+
clearInterval(intervalEntry.id);
|
|
374
|
+
}
|
|
375
|
+
sourceElement.eventsInterval[prefix] = {
|
|
376
|
+
delay: intervalMs,
|
|
377
|
+
id: setInterval(() => {
|
|
378
|
+
if (!sourceElement.isConnected) {
|
|
379
|
+
const entry =
|
|
380
|
+
sourceElement.eventsInterval &&
|
|
381
|
+
sourceElement.eventsInterval[prefix];
|
|
382
|
+
if (entry) {
|
|
383
|
+
clearInterval(entry.id);
|
|
384
|
+
delete sourceElement.eventsInterval[prefix];
|
|
385
|
+
}
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
// Recursive call for interval
|
|
389
|
+
this.__updateElements(
|
|
390
|
+
rootElement,
|
|
391
|
+
prefix,
|
|
392
|
+
events,
|
|
393
|
+
params
|
|
394
|
+
);
|
|
395
|
+
}, intervalMs)
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
} else if (intervalEntry) {
|
|
399
|
+
clearInterval(intervalEntry.id);
|
|
400
|
+
delete sourceElement.eventsInterval[prefix];
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// setTimeout
|
|
405
|
+
let delayTime = sourceElement.getAttribute(`${prefix}.setTimeout`);
|
|
406
|
+
if (delayTime) {
|
|
407
|
+
delayTime = parseInt(delayTime, 10) || 0;
|
|
408
|
+
if (delayTime > 0) {
|
|
409
|
+
await new Promise((resolve) => setTimeout(resolve, delayTime));
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (!sourceElement.isConnected) return;
|
|
414
|
+
|
|
415
|
+
// once
|
|
416
|
+
let once = sourceElement.getAttribute(`${prefix}.once`);
|
|
417
|
+
if (once || once === "") {
|
|
418
|
+
if (!sourceElement.eventsOnce) {
|
|
419
|
+
sourceElement.eventsOnce = [prefix];
|
|
420
|
+
} else if (sourceElement.eventsOnce.includes(prefix)) {
|
|
421
|
+
continue;
|
|
422
|
+
} else {
|
|
423
|
+
sourceElement.eventsOnce.push(prefix);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// --- Resolve Targets (Look up query) ---
|
|
428
|
+
// We check for query on the sourceElement to see if targets should be redirected
|
|
429
|
+
let querySelector = sourceElement.getAttribute(`${prefix}.query`);
|
|
430
|
+
|
|
431
|
+
// Reset targets to sourceElement by default or use query
|
|
432
|
+
let targetElements = [sourceElement];
|
|
433
|
+
|
|
434
|
+
// If query exists or we have params, resolve targets
|
|
435
|
+
if (querySelector || params) {
|
|
436
|
+
targetElements = queryElements({
|
|
437
|
+
element: sourceElement,
|
|
438
|
+
prefix: prefix,
|
|
439
|
+
selector: querySelector || params
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// --- 6. Execute Action on Targets ---
|
|
444
|
+
for (let targetElement of targetElements) {
|
|
445
|
+
await this.setValue(
|
|
446
|
+
rootElement,
|
|
447
|
+
sourceElement,
|
|
448
|
+
targetElement,
|
|
449
|
+
prefix,
|
|
450
|
+
events
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
rootElement.dispatchEvent(
|
|
457
|
+
new CustomEvent(`${prefix}End`, {
|
|
458
|
+
detail: {}
|
|
459
|
+
})
|
|
460
|
+
);
|
|
461
|
+
},
|
|
462
|
+
|
|
463
|
+
setValue: async function (
|
|
464
|
+
rootElement,
|
|
465
|
+
sourceElement,
|
|
466
|
+
targetElement,
|
|
467
|
+
prefix,
|
|
468
|
+
events
|
|
469
|
+
) {
|
|
470
|
+
if (events && events.includes("mouseout")) {
|
|
471
|
+
if (targetElement.matches(":hover")) {
|
|
472
|
+
targetElement.addEventListener(
|
|
473
|
+
"mouseout",
|
|
474
|
+
() => {
|
|
475
|
+
this.setValue(
|
|
476
|
+
rootElement,
|
|
477
|
+
sourceElement,
|
|
478
|
+
targetElement,
|
|
479
|
+
prefix,
|
|
480
|
+
events
|
|
481
|
+
);
|
|
482
|
+
},
|
|
483
|
+
{ once: true }
|
|
484
|
+
);
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
let deactivate = false;
|
|
490
|
+
if (rootElement !== sourceElement) {
|
|
491
|
+
deactivate = true;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// 3. Iterate Attributes of the Source Element (el) in order
|
|
495
|
+
for (let attribute of sourceElement.attributes) {
|
|
496
|
+
const name = attribute.name;
|
|
497
|
+
|
|
498
|
+
// Optimization: Skip attributes that don't match the prefix
|
|
499
|
+
// UPDATED: All checks below use lowercase because DOM attribute iteration is lowercase
|
|
500
|
+
if (!name.startsWith(prefix)
|
|
501
|
+
|| name.length <= prefix.length + 1
|
|
502
|
+
|| name === `${prefix}.on`
|
|
503
|
+
|| name === `${prefix}.settimeout`
|
|
504
|
+
|| name === `${prefix}.setinterval`
|
|
505
|
+
|| name === `${prefix}.once`
|
|
506
|
+
|| name === `${prefix}.group`
|
|
507
|
+
|| name === `${prefix}.query`
|
|
508
|
+
|| name === `${prefix}.root`
|
|
509
|
+
|| name === `${prefix}.rootmargin`
|
|
510
|
+
|| name === `${prefix}.threshold`
|
|
511
|
+
|| name === `${prefix}.trigger`
|
|
512
|
+
|| name === `${prefix}.selector`
|
|
513
|
+
|| name === `${prefix}.attributefilter`
|
|
514
|
+
|| name === `${prefix}.types`
|
|
515
|
+
|| name === `${prefix}.bubbles`
|
|
516
|
+
|| name === `${prefix}.cancelable`
|
|
517
|
+
|| name === `${prefix}.composed`
|
|
518
|
+
|| name === `${prefix}.preventdefault`
|
|
519
|
+
|| name === `${prefix}.stoppropagation`
|
|
520
|
+
|| name === `${prefix}.stopimmediatepropagation`
|
|
521
|
+
// Skip .position option as well since it's used as config
|
|
522
|
+
|| name === `${prefix}.position`) {
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Parse Target Name and Type
|
|
527
|
+
let targetName = name.substring(prefix.length + 1);
|
|
528
|
+
let separator = name[prefix.length]; // '.' or '-'
|
|
529
|
+
|
|
530
|
+
let value = attribute.value;
|
|
531
|
+
if (!value && sourceElement && sourceElement.getValue) {
|
|
532
|
+
value = await sourceElement.getValue();
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Parse comma-separated values to support rotation
|
|
536
|
+
let values = [];
|
|
537
|
+
if (typeof value === 'string') {
|
|
538
|
+
values = value.split(',').map(v => v.trim());
|
|
539
|
+
} else if (Array.isArray(value)) {
|
|
540
|
+
values = value;
|
|
541
|
+
} else {
|
|
542
|
+
values = [value];
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
let oldValue = "";
|
|
546
|
+
let newValue = "";
|
|
547
|
+
|
|
548
|
+
switch (targetName) {
|
|
549
|
+
case 'style':
|
|
550
|
+
if (targetElement.getAttribute("style")) {
|
|
551
|
+
let styles = targetElement.getAttribute("style").split(";").map(x => x.trim().replace(/\s/g, ""));
|
|
552
|
+
oldValue = values.find(x => styles.includes(x.replace(/\s/g, ""))) || "";
|
|
553
|
+
}
|
|
554
|
+
newValue = this.__getNextValue(values, oldValue, deactivate);
|
|
555
|
+
|
|
556
|
+
if (oldValue) {
|
|
557
|
+
let [prop, val] = oldValue.split(":");
|
|
558
|
+
if (prop) targetElement.style[prop.trim()] = "";
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (newValue) {
|
|
562
|
+
let styles = newValue.split(";");
|
|
563
|
+
for(let s of styles) {
|
|
564
|
+
let [prop, val] = s.split(":");
|
|
565
|
+
if (prop && val) targetElement.style[prop.trim()] = val.trim();
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
break;
|
|
569
|
+
|
|
570
|
+
case 'class':
|
|
571
|
+
let currentClasses = Array.from(targetElement.classList);
|
|
572
|
+
oldValue = values.find(val => {
|
|
573
|
+
let requiredClasses = val.split(" ");
|
|
574
|
+
return requiredClasses.every(c => currentClasses.includes(c));
|
|
575
|
+
}) || "";
|
|
576
|
+
|
|
577
|
+
newValue = this.__getNextValue(values, oldValue, deactivate);
|
|
578
|
+
|
|
579
|
+
if (oldValue) {
|
|
580
|
+
oldValue.split(" ").forEach(c => targetElement.classList.remove(c));
|
|
581
|
+
if (values.length === 1) return;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
if (newValue) {
|
|
585
|
+
newValue.split(" ").forEach(c => targetElement.classList.add(c));
|
|
586
|
+
}
|
|
587
|
+
break;
|
|
588
|
+
|
|
589
|
+
// UPDATED: Added lowercase cases for HTML compatibility
|
|
590
|
+
case 'insertadjacenthtml':
|
|
591
|
+
case 'insertadjacenttext':
|
|
592
|
+
case 'insertAdjacentHTML':
|
|
593
|
+
case 'insertAdjacentText':
|
|
594
|
+
// 1. Get Position from Attribute Value (No rotation, strict)
|
|
595
|
+
let position = attribute.value;
|
|
596
|
+
if (!position) return;
|
|
597
|
+
|
|
598
|
+
// 2. Get Content from Source Element
|
|
599
|
+
let content = "";
|
|
600
|
+
if (sourceElement && sourceElement.getValue) {
|
|
601
|
+
content = await sourceElement.getValue();
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// 3. Execute Insertion
|
|
605
|
+
// Check for CoCreate Rich Text integration
|
|
606
|
+
let domTextEditor;
|
|
607
|
+
if (targetElement.parentElement) {
|
|
608
|
+
domTextEditor = targetElement.parentElement.closest("[contenteditable]");
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
if (domTextEditor && typeof CoCreate !== 'undefined' && CoCreate.text) {
|
|
612
|
+
CoCreate.text.insertAdjacentElement({
|
|
613
|
+
domTextEditor,
|
|
614
|
+
position: position,
|
|
615
|
+
target: targetElement,
|
|
616
|
+
elementValue: content
|
|
617
|
+
});
|
|
618
|
+
} else {
|
|
619
|
+
// Standard JS Execution
|
|
620
|
+
// Check lowercase match since switch case might have matched that
|
|
621
|
+
if (targetName === 'insertAdjacentHTML' || targetName === 'insertadjacenthtml') {
|
|
622
|
+
targetElement.insertAdjacentHTML(position, content);
|
|
623
|
+
} else {
|
|
624
|
+
targetElement.insertAdjacentText(position, content);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
break;
|
|
628
|
+
|
|
629
|
+
case 'value':
|
|
630
|
+
if (targetElement.getValue) {
|
|
631
|
+
oldValue = await targetElement.getValue();
|
|
632
|
+
} else {
|
|
633
|
+
oldValue = targetElement.value;
|
|
634
|
+
}
|
|
635
|
+
if (!values.includes(oldValue)) oldValue = "";
|
|
636
|
+
newValue = this.__getNextValue(values, oldValue, deactivate);
|
|
637
|
+
|
|
638
|
+
if (targetElement.setValue) {
|
|
639
|
+
targetElement.setValue(newValue);
|
|
640
|
+
} else {
|
|
641
|
+
targetElement.value = newValue;
|
|
642
|
+
}
|
|
643
|
+
break;
|
|
644
|
+
|
|
645
|
+
default:
|
|
646
|
+
if (separator === '.') {
|
|
647
|
+
// Dot Notation: Property or Method
|
|
648
|
+
if (targetElement[targetName] !== undefined) {
|
|
649
|
+
// It's a property or function
|
|
650
|
+
oldValue = targetElement[targetName];
|
|
651
|
+
if (!values.includes(oldValue)) oldValue = "";
|
|
652
|
+
newValue = this.__getNextValue(values, oldValue, deactivate);
|
|
653
|
+
|
|
654
|
+
if (typeof targetElement[targetName] === 'function') {
|
|
655
|
+
targetElement[targetName](newValue);
|
|
656
|
+
} else {
|
|
657
|
+
targetElement[targetName] = newValue;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
} else {
|
|
661
|
+
// Dash Notation: Attribute
|
|
662
|
+
// Fallback to attribute
|
|
663
|
+
oldValue = targetElement.getAttribute(targetName);
|
|
664
|
+
if (!values.includes(oldValue)) oldValue = "";
|
|
665
|
+
newValue = this.__getNextValue(values, oldValue, deactivate);
|
|
666
|
+
|
|
667
|
+
// Set Attribute with Namespace check
|
|
668
|
+
if (targetName.includes('.')) {
|
|
669
|
+
const realName = targetName.replace('.', ':');
|
|
670
|
+
targetElement.setAttribute(realName, newValue);
|
|
671
|
+
} else if (targetName.includes(':')) {
|
|
672
|
+
targetElement.setAttribute(targetName, newValue);
|
|
673
|
+
} else {
|
|
674
|
+
targetElement.setAttribute(targetName, newValue);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
break;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
},
|
|
682
|
+
|
|
683
|
+
__getNextValue: function (values, val, deativate) {
|
|
684
|
+
if (deativate) return "";
|
|
685
|
+
let size = values.length;
|
|
686
|
+
let nn = values.indexOf(val);
|
|
687
|
+
if (nn == -1) {
|
|
688
|
+
return values[0];
|
|
689
|
+
} else {
|
|
690
|
+
return values[(nn + 1) % size];
|
|
691
|
+
}
|
|
692
|
+
}
|
|
798
693
|
};
|
|
799
694
|
|
|
800
|
-
const eventElements = new Map();
|
|
801
|
-
const eventKeys = new Map();
|
|
802
|
-
const elementAttributes = new Map();
|
|
803
|
-
|
|
804
|
-
function replaceKey(prefix, element, key, values) {
|
|
805
|
-
let currentValue = key;
|
|
806
|
-
|
|
807
|
-
if (eventElements.has(element)) {
|
|
808
|
-
currentValue = eventElements.get(element).get(key);
|
|
809
|
-
}
|
|
810
|
-
let value = CoCreateEvents.__getNextValue(values, currentValue);
|
|
811
|
-
eventKeys.set(key, value);
|
|
812
|
-
eventElements.set(element, eventKeys);
|
|
813
|
-
|
|
814
|
-
let attributes = elementAttributes.get(element);
|
|
815
|
-
if (!attributes) {
|
|
816
|
-
attributes = {};
|
|
817
|
-
elementAttributes.set(element, attributes);
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
for (let attribute of element.attributes) {
|
|
821
|
-
let newName = attribute.name;
|
|
822
|
-
let newValue = attribute.value;
|
|
823
|
-
let attrName = attribute.name;
|
|
824
|
-
let attrValue = attribute.value;
|
|
825
|
-
let setAttr = false;
|
|
826
|
-
|
|
827
|
-
if (attrName === "clone-data") console.log("clone-data");
|
|
828
|
-
if (
|
|
829
|
-
attrValue.includes(currentValue) ||
|
|
830
|
-
(attributes[attrName] &&
|
|
831
|
-
attributes[attrName].valueKeys &&
|
|
832
|
-
attributes[attrName].valueKeys[key])
|
|
833
|
-
) {
|
|
834
|
-
if (!attributes[attrName])
|
|
835
|
-
attributes[attrName] = { originalValue: attrValue };
|
|
836
|
-
else if (!attributes[attrName].originalValue)
|
|
837
|
-
attributes[attrName].originalValue = attrValue;
|
|
838
|
-
if (!attributes[attrName].valueKeys)
|
|
839
|
-
attributes[attrName].valueKeys = { [key]: value };
|
|
840
|
-
else attributes[attrName].valueKeys[key] = value;
|
|
841
|
-
|
|
842
|
-
newValue = attributes[attrName].originalValue;
|
|
843
|
-
for (let valueKey of Object.keys(attributes[attrName].valueKeys)) {
|
|
844
|
-
const valueKeyRegex = new RegExp(valueKey, "g");
|
|
845
|
-
newValue = newValue.replace(valueKeyRegex, value);
|
|
846
|
-
}
|
|
847
|
-
setAttr = true;
|
|
848
|
-
}
|
|
849
|
-
// if (attrName.includes(currentValue) || attributes[attrName] && attributes[attrName].nameKeys && attributes[attrName].nameKeys[key]){
|
|
850
|
-
// if (!attributes[attrName])
|
|
851
|
-
// attributes[attrName] = { originalName: attrName }
|
|
852
|
-
// else if (!attributes[attrName].originalName)
|
|
853
|
-
// attributes[attrName].originalName = attrName
|
|
854
|
-
|
|
855
|
-
// if (!attributes[attrName].nameKeys)
|
|
856
|
-
// attributes[attrName].nameKeys = {[key]: value}
|
|
857
|
-
|
|
858
|
-
// newName = attributes[attrName].originalName
|
|
859
|
-
// for (let nameKey of Object.keys(attributes[attrName].nameKeys)) {
|
|
860
|
-
// let r
|
|
861
|
-
// if (attrName === nameKey)
|
|
862
|
-
// r = nameKey
|
|
863
|
-
// else
|
|
864
|
-
// r = attributes[attrName].nameKeys[nameKey]
|
|
865
|
-
|
|
866
|
-
// const nameKeyRegex = new RegExp(r, "g");
|
|
867
|
-
// newName = newName.replace(nameKeyRegex, value);
|
|
868
|
-
// }
|
|
869
|
-
|
|
870
|
-
// attributes[attrName].nameKeys[key] = value;
|
|
871
|
-
|
|
872
|
-
// element.removeAttribute(attrName);
|
|
873
|
-
// setAttr = true;
|
|
874
|
-
|
|
875
|
-
// }
|
|
876
|
-
if (setAttr) element.setAttribute(newName, newValue);
|
|
877
|
-
}
|
|
878
|
-
let html = element.innerHTML;
|
|
879
|
-
if (
|
|
880
|
-
html.indexOf(currentValue) !== -1 ||
|
|
881
|
-
(element.htmlKeys && element.htmlKeys[key])
|
|
882
|
-
) {
|
|
883
|
-
if (!element.originalHtml) element.originalHtml = html;
|
|
884
|
-
if (!element.htmlKeys) element.htmlKeys = { [key]: value };
|
|
885
|
-
else element.htmlKeys[key] = value;
|
|
886
|
-
|
|
887
|
-
let newHTML = element.originalHtml;
|
|
888
|
-
if (newHTML) {
|
|
889
|
-
for (let elementKey of Object.keys(element.htmlKeys)) {
|
|
890
|
-
const htmlKeyRegex = new RegExp(elementKey, "g");
|
|
891
|
-
newHTML = newHTML.replace(htmlKeyRegex, value);
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
element.innerHTML = newHTML;
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
function evaluateCondition(condition, values) {
|
|
900
|
-
let orConditions = condition.split(/\s*\|\|\s*/);
|
|
901
|
-
|
|
902
|
-
if (orConditions.length > 1) {
|
|
903
|
-
return orConditions.some((cond) => checkCondition(cond, values));
|
|
904
|
-
} else {
|
|
905
|
-
let andConditions = condition.split(/\s*&&\s*/);
|
|
906
|
-
return andConditions.every((cond) => checkCondition(cond, values));
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
function checkCondition(condition, value) {
|
|
911
|
-
let isNegated = condition.startsWith("!");
|
|
912
|
-
if (isNegated) {
|
|
913
|
-
condition = condition.substring(1);
|
|
914
|
-
}
|
|
915
|
-
const operatorMatch = condition.match(/(<=|>=|<|>)(.+)/);
|
|
916
|
-
if (operatorMatch) condition = operatorMatch[2].trim();
|
|
917
|
-
|
|
918
|
-
let parse = true;
|
|
919
|
-
if (condition === "true") {
|
|
920
|
-
return !!value[0];
|
|
921
|
-
} else if (condition === "false") {
|
|
922
|
-
return !value[0];
|
|
923
|
-
}
|
|
924
|
-
// else if (condition === "[]" && typeof value[0] === "string") {
|
|
925
|
-
// parse = false;
|
|
926
|
-
// }
|
|
927
|
-
|
|
928
|
-
// TODO: why parse updated conditin to boolean false
|
|
929
|
-
// if (parse && condition !== "false") condition = parseCondition(condition);
|
|
930
|
-
|
|
931
|
-
if (
|
|
932
|
-
typeof condition === "number" ||
|
|
933
|
-
(typeof condition === "string" && /\d/.test(condition))
|
|
934
|
-
) {
|
|
935
|
-
condition = parseNumberCondition(condition);
|
|
936
|
-
} else if (typeof value[0] === "object" && typeof condition === "string") {
|
|
937
|
-
condition = parseJsonCondition(condition);
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
let result;
|
|
941
|
-
|
|
942
|
-
if (condition === "$template") {
|
|
943
|
-
result = /{{\s*([\w\W]+)\s*}}/g.test(value[0]);
|
|
944
|
-
} else if (Array.isArray(value) && !(typeof condition === "object")) {
|
|
945
|
-
if (operatorMatch) {
|
|
946
|
-
condition = parseFloat(condition);
|
|
947
|
-
result = value.some((v) => {
|
|
948
|
-
v = parseFloat(v);
|
|
949
|
-
switch (operatorMatch[1]) {
|
|
950
|
-
case "<":
|
|
951
|
-
return v < condition;
|
|
952
|
-
case ">":
|
|
953
|
-
return v > condition;
|
|
954
|
-
case "<=":
|
|
955
|
-
return v <= condition;
|
|
956
|
-
case ">=":
|
|
957
|
-
return v >= condition;
|
|
958
|
-
default:
|
|
959
|
-
return false;
|
|
960
|
-
}
|
|
961
|
-
});
|
|
962
|
-
} else result = value.includes(condition);
|
|
963
|
-
} else if (Array.isArray(value)) {
|
|
964
|
-
// TODO: handle comparing array to array, vs querying the array items for a match
|
|
965
|
-
result = queryData(value, condition);
|
|
966
|
-
} else if (
|
|
967
|
-
typeof value === "object" &&
|
|
968
|
-
value !== null &&
|
|
969
|
-
typeof parsedCond === "object"
|
|
970
|
-
) {
|
|
971
|
-
result = queryData(value, condition);
|
|
972
|
-
} else {
|
|
973
|
-
result = value === condition;
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
result = isNegated ? !result : result;
|
|
977
|
-
return result;
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
function parseJsonCondition(condition) {
|
|
981
|
-
try {
|
|
982
|
-
// Attempt to parse the condition as JSON
|
|
983
|
-
let parsedJson = JSON.parse(condition);
|
|
984
|
-
return parsedJson;
|
|
985
|
-
} catch (e) {
|
|
986
|
-
return condition;
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
function parseNumberCondition(condition) {
|
|
990
|
-
if (typeof condition === "string") {
|
|
991
|
-
const num = parseFloat(condition);
|
|
992
|
-
return !isNaN(num) ? num : condition;
|
|
993
|
-
}
|
|
994
|
-
return condition;
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
// Helper function for delay using Promises
|
|
998
|
-
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
999
|
-
|
|
1000
|
-
/**
|
|
1001
|
-
* Waits for a specific element property to meet a readiness condition,
|
|
1002
|
-
* based on retry attributes defined on the element.
|
|
1003
|
-
* Incorporates optional exponential backoff for retry delays.
|
|
1004
|
-
* Returns a Promise that resolves to true if the condition is met within
|
|
1005
|
-
* the allowed attempts, and false otherwise.
|
|
1006
|
-
*
|
|
1007
|
-
* @param {HTMLElement} element The element with the onload-retry-* attributes.
|
|
1008
|
-
* @returns {Promise<boolean>} A Promise resolving to true if ready, false if timed out.
|
|
1009
|
-
*/
|
|
1010
|
-
async function checkElementReadinessWithBackoff(element) {
|
|
1011
|
-
const propertyName = element.getAttribute("onload-retry-property");
|
|
1012
|
-
|
|
1013
|
-
if (!propertyName) {
|
|
1014
|
-
return true; // No retry needed
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
// --- Retry Parameters ---
|
|
1018
|
-
const maxAttempts =
|
|
1019
|
-
parseInt(element.getAttribute("onload-retry-attempts") || "3", 10) || 3;
|
|
1020
|
-
// Initial delay (used for the first wait and as the base for backoff)
|
|
1021
|
-
const initialDelayMs =
|
|
1022
|
-
parseInt(element.getAttribute("onload-retry-delay") || "100", 10) ||
|
|
1023
|
-
100;
|
|
1024
|
-
// Exponential backoff factor (e.g., 2 for doubling). <= 1 means fixed delay.
|
|
1025
|
-
// Default to 1 (no backoff) if missing or invalid.
|
|
1026
|
-
const backoffFactor =
|
|
1027
|
-
parseFloat(
|
|
1028
|
-
element.getAttribute("onload-retry-backoff-factor") || "1"
|
|
1029
|
-
) || 1;
|
|
1030
|
-
// Optional maximum delay cap. Use Infinity if missing, invalid, or <= 0.
|
|
1031
|
-
const maxDelayMs = parseInt(
|
|
1032
|
-
element.getAttribute("onload-retry-max-delay") || "0",
|
|
1033
|
-
10
|
|
1034
|
-
);
|
|
1035
|
-
const effectiveMaxDelay =
|
|
1036
|
-
maxDelayMs && maxDelayMs > 0 ? maxDelayMs : Infinity;
|
|
1037
|
-
|
|
1038
|
-
console.log(
|
|
1039
|
-
`Checking readiness for property "${propertyName}" on element ${
|
|
1040
|
-
element.id || ""
|
|
1041
|
-
}. Max attempts: ${maxAttempts}, Initial Delay: ${initialDelayMs}ms, Backoff Factor: ${backoffFactor}, Max Delay: ${
|
|
1042
|
-
effectiveMaxDelay === Infinity ? "None" : effectiveMaxDelay + "ms"
|
|
1043
|
-
}.`
|
|
1044
|
-
);
|
|
1045
|
-
|
|
1046
|
-
// Initialize the delay to be used for the *first* wait.
|
|
1047
|
-
let currentDelay = initialDelayMs;
|
|
1048
|
-
|
|
1049
|
-
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
1050
|
-
console.log(
|
|
1051
|
-
`Attempt ${attempt}/${maxAttempts}: Checking "${propertyName}"...`
|
|
1052
|
-
);
|
|
1053
|
-
try {
|
|
1054
|
-
const propertyValue = element[propertyName];
|
|
1055
|
-
// Example check: Adjust condition as needed
|
|
1056
|
-
if (propertyValue !== undefined && propertyValue > 0) {
|
|
1057
|
-
console.log(
|
|
1058
|
-
`Property "${propertyName}" ready on attempt ${attempt}. Value: ${propertyValue}`
|
|
1059
|
-
);
|
|
1060
|
-
return true; // Condition met
|
|
1061
|
-
}
|
|
1062
|
-
} catch (error) {
|
|
1063
|
-
console.error(
|
|
1064
|
-
`Error accessing property "${propertyName}" on attempt ${attempt}:`,
|
|
1065
|
-
error
|
|
1066
|
-
);
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
// If condition not met and more attempts remain:
|
|
1070
|
-
if (attempt < maxAttempts) {
|
|
1071
|
-
// Determine the actual delay for this wait, applying the cap
|
|
1072
|
-
const delayForThisWait = Math.min(currentDelay, effectiveMaxDelay);
|
|
1073
|
-
|
|
1074
|
-
console.log(
|
|
1075
|
-
`Waiting ${Math.round(
|
|
1076
|
-
delayForThisWait
|
|
1077
|
-
)}ms before next attempt...`
|
|
1078
|
-
);
|
|
1079
|
-
await delay(delayForThisWait);
|
|
1080
|
-
|
|
1081
|
-
// Calculate the delay for the *next* potential wait, only if backoff is active
|
|
1082
|
-
if (backoffFactor > 1) {
|
|
1083
|
-
currentDelay = currentDelay * backoffFactor;
|
|
1084
|
-
// Note: We apply the cap just before the actual delay,
|
|
1085
|
-
// but the base 'currentDelay' keeps growing exponentially.
|
|
1086
|
-
// Alternatively, you could cap 'currentDelay' itself here:
|
|
1087
|
-
// currentDelay = Math.min(currentDelay * backoffFactor, effectiveMaxDelay);
|
|
1088
|
-
// Let's stick to capping just before delay() for now.
|
|
1089
|
-
}
|
|
1090
|
-
// If backoffFactor <= 1, currentDelay remains initialDelayMs (implicitly handled by initialization)
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
// If loop finishes without condition being met
|
|
1095
|
-
console.warn(
|
|
1096
|
-
`Readiness check failed for property "${propertyName}" on element ${
|
|
1097
|
-
element.id || ""
|
|
1098
|
-
} after ${maxAttempts} attempts.`
|
|
1099
|
-
);
|
|
1100
|
-
return false; // Timed out
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
// --- Example Usage (calling code doesn't change) ---
|
|
1104
|
-
|
|
1105
|
-
async function handleElementProcessingWithBackoff(element) {
|
|
1106
|
-
// Call the function that now supports backoff
|
|
1107
|
-
const isReady = await checkElementReadinessWithBackoff(element);
|
|
1108
|
-
|
|
1109
|
-
if (isReady) {
|
|
1110
|
-
console.log(
|
|
1111
|
-
`Element ${element.id || ""} is ready. Proceeding with actions.`
|
|
1112
|
-
);
|
|
1113
|
-
// --- Execute subsequent actions ---
|
|
1114
|
-
if (element.hasAttribute("onload-attribute")) {
|
|
1115
|
-
/* ... */
|
|
1116
|
-
}
|
|
1117
|
-
if (element.hasAttribute("onload-query")) {
|
|
1118
|
-
/* ... */
|
|
1119
|
-
}
|
|
1120
|
-
} else {
|
|
1121
|
-
console.warn(
|
|
1122
|
-
`Skipping actions for element ${
|
|
1123
|
-
element.id || ""
|
|
1124
|
-
} because readiness check failed.`
|
|
1125
|
-
);
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
// --- Example Elements ---
|
|
1130
|
-
|
|
1131
|
-
// With Backoff (Delays: 100, 200, 400, 800 - capped)
|
|
1132
|
-
// <span id="elem1" onload-retry-property="scrollWidth"
|
|
1133
|
-
// onload-retry-attempts="5" onload-retry-delay="100"
|
|
1134
|
-
// onload-retry-backoff-factor="2" onload-retry-max-delay="500">
|
|
1135
|
-
// Content 1
|
|
1136
|
-
// </span>
|
|
1137
|
-
// Waits would be: 100ms, 200ms, 400ms, 500ms (capped)
|
|
1138
|
-
|
|
1139
|
-
// No Backoff (factor missing/invalid/<=1)
|
|
1140
|
-
// <span id="elem2" onload-retry-property="clientHeight"
|
|
1141
|
-
// onload-retry-attempts="4" onload-retry-delay="300">
|
|
1142
|
-
// Content 2
|
|
1143
|
-
// </span>
|
|
1144
|
-
// Waits would be: 300ms, 300ms, 300ms
|
|
1145
|
-
|
|
1146
|
-
// const element1 = document.getElementById('elem1');
|
|
1147
|
-
// if (element1) handleElementProcessingWithBackoff(element1);
|
|
1148
|
-
// const element2 = document.getElementById('elem2');
|
|
1149
|
-
// if (element2) handleElementProcessingWithBackoff(element2);
|
|
1150
|
-
|
|
1151
695
|
CoCreateEvents.init();
|
|
1152
696
|
|
|
1153
|
-
export default CoCreateEvents;
|
|
697
|
+
export default CoCreateEvents;
|