@cocreate/events 1.29.0 → 1.30.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 +14 -0
- package/package.json +1 -1
- package/src/index.js +605 -1121
package/src/index.js
CHANGED
|
@@ -20,1134 +20,618 @@
|
|
|
20
20
|
// you must obtain a commercial license from CoCreate LLC.
|
|
21
21
|
// For details, visit <https://cocreate.app/licenses/> or contact us at sales@cocreate.app.
|
|
22
22
|
|
|
23
|
-
import { queryElements
|
|
23
|
+
import { queryElements } from "@cocreate/utils";
|
|
24
24
|
import action from "@cocreate/actions";
|
|
25
25
|
import observer from "@cocreate/observer";
|
|
26
26
|
import "@cocreate/element-prototype";
|
|
27
|
+
import { getAttribute } from "@cocreate/element-prototype/src/getAttribute";
|
|
27
28
|
|
|
28
29
|
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
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
30
|
+
elements: new Map(),
|
|
31
|
+
|
|
32
|
+
init: function (prefix, events) {
|
|
33
|
+
if (prefix && events) {
|
|
34
|
+
this.initPrefix(prefix, events);
|
|
35
|
+
} else {
|
|
36
|
+
const defaults = {
|
|
37
|
+
"toggle": ["click"],
|
|
38
|
+
"click": ["click"],
|
|
39
|
+
"hover": ["mouseover", "mouseout"],
|
|
40
|
+
"mouseover": ["mouseover"],
|
|
41
|
+
"mouseout": ["mouseout"],
|
|
42
|
+
"input": ["input"],
|
|
43
|
+
"change": ["change"],
|
|
44
|
+
"selected": ["click"],
|
|
45
|
+
"onload": ["onload"],
|
|
46
|
+
"observe": ["observer"],
|
|
47
|
+
"intersection": ["intersection"],
|
|
48
|
+
"resize": ["onload", "resize"],
|
|
49
|
+
"localstorage": ["onload"],
|
|
50
|
+
"focus": ["focus"],
|
|
51
|
+
"blur": ["blur"]
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
for (const [p, e] of Object.entries(defaults)) {
|
|
55
|
+
this.initPrefix(p, e);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Initialize from existing elements with [event-name]
|
|
60
|
+
const customEventEls = document.querySelectorAll("[event-name]");
|
|
61
|
+
const processedNames = new Set();
|
|
62
|
+
|
|
63
|
+
for (const customEventEl of customEventEls) {
|
|
64
|
+
let names = customEventEl.getAttribute("event-name");
|
|
65
|
+
if (!names) continue;
|
|
66
|
+
|
|
67
|
+
names.split(",").forEach(name => {
|
|
68
|
+
name = name.trim();
|
|
69
|
+
if (name && !processedNames.has(name)) {
|
|
70
|
+
processedNames.add(name);
|
|
71
|
+
this.initPrefix(name);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Observe for new elements with [event-name]
|
|
77
|
+
observer.init({
|
|
78
|
+
name: "CoCreateEventName",
|
|
79
|
+
types: ["addedNodes"],
|
|
80
|
+
selector: `[event-name]`,
|
|
81
|
+
callback: (mutation) => {
|
|
82
|
+
const names = mutation.target.getAttribute("event-name");
|
|
83
|
+
if (names) {
|
|
84
|
+
names.split(",").forEach(name => {
|
|
85
|
+
this.initPrefix(name.trim());
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
initPrefix: function (prefix, events) {
|
|
93
|
+
action.init({
|
|
94
|
+
name: prefix,
|
|
95
|
+
endEvent: `${prefix}End`,
|
|
96
|
+
callback: (data) => {
|
|
97
|
+
this.__updateElements(
|
|
98
|
+
data.element,
|
|
99
|
+
prefix,
|
|
100
|
+
null,
|
|
101
|
+
null,
|
|
102
|
+
data.params
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Updated selector to use dash for options: prefix-group, prefix-query
|
|
108
|
+
const selector = `[${prefix}], [${prefix}-group], [${prefix}-query]`;
|
|
109
|
+
|
|
110
|
+
observer.init({
|
|
111
|
+
name: "CoCreateEventAttributes",
|
|
112
|
+
types: ["attributes", "addedNodes"],
|
|
113
|
+
selector,
|
|
114
|
+
callback: (mutation) => {
|
|
115
|
+
if (mutation.type === 'attributes') {
|
|
116
|
+
if (mutation.attributeName.startsWith(prefix)) {
|
|
117
|
+
this.initElements([mutation.target], prefix, events);
|
|
118
|
+
}
|
|
119
|
+
} else {
|
|
120
|
+
this.initElements([mutation.target], prefix, events);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const elements = document.querySelectorAll(selector);
|
|
126
|
+
this.initElements(elements, prefix, events);
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
initElements: function (elements, prefix, events = []) {
|
|
130
|
+
for (const el of elements) {
|
|
131
|
+
let isEventable = true;
|
|
132
|
+
let elData = this.elements.get(el);
|
|
133
|
+
|
|
134
|
+
if (!elData) {
|
|
135
|
+
elData = { [prefix]: { events } };
|
|
136
|
+
this.elements.set(el, elData);
|
|
137
|
+
} else if (!elData[prefix]) {
|
|
138
|
+
elData[prefix] = { events };
|
|
139
|
+
} else {
|
|
140
|
+
isEventable = false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Check for custom events defined on the element using dash
|
|
144
|
+
let customEventsAttr = el.getAttribute(`${prefix}-events`);
|
|
145
|
+
if (customEventsAttr) {
|
|
146
|
+
// Remove listeners for previously assumed events before overriding
|
|
147
|
+
events.forEach(event => el.removeEventListener(event, eventFunction));
|
|
148
|
+
|
|
149
|
+
events = customEventsAttr.split(",").map(e => e.trim());
|
|
150
|
+
elData[prefix].events = events;
|
|
151
|
+
isEventable = true;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (!events || !isEventable) continue;
|
|
155
|
+
|
|
156
|
+
// --- Handle Special Events ---
|
|
157
|
+
|
|
158
|
+
if (events.includes("onload")) {
|
|
159
|
+
this.__updateElements(el, prefix);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (events.includes("observer")) {
|
|
163
|
+
this._initObserver(el, prefix);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (events.includes("resize")) {
|
|
167
|
+
this._initResizeObserver(el, prefix);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (events.includes("intersection")) {
|
|
171
|
+
this._initIntersectionObserver(el, prefix);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// --- Handle Standard Events ---
|
|
175
|
+
|
|
176
|
+
for (const eventName of events) {
|
|
177
|
+
if (["onload", "observer", "intersection", "resize"].includes(eventName)) {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Remove existing listener to prevent duplicates
|
|
182
|
+
el.removeEventListener(eventName, eventFunction);
|
|
183
|
+
|
|
184
|
+
const options = {};
|
|
185
|
+
// Updated to use dash for options
|
|
186
|
+
const attributes = ["bubbles", "cancelable", "composed"];
|
|
187
|
+
|
|
188
|
+
for (const attr of attributes) {
|
|
189
|
+
const attrValue = el.getAttribute(`${eventName}-${attr}`);
|
|
190
|
+
if (attrValue !== null) {
|
|
191
|
+
options[attr] = attrValue !== "false";
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
el.addEventListener(eventName, eventFunction, options);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const self = this;
|
|
200
|
+
function eventFunction(event) {
|
|
201
|
+
const target = event.currentTarget;
|
|
202
|
+
if (!target) return;
|
|
203
|
+
|
|
204
|
+
const elData = self.elements.get(target);
|
|
205
|
+
if (!elData || !elData[prefix]) return;
|
|
206
|
+
|
|
207
|
+
const prefixData = elData[prefix];
|
|
208
|
+
|
|
209
|
+
// Debounce/Prevent duplicate mouse events if needed
|
|
210
|
+
if (prefixData.prev === event.type && ["mouseover", "mouseout"].includes(event.type)) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
prefixData.prev = event.type;
|
|
214
|
+
|
|
215
|
+
// Check for 'actions' blocking
|
|
216
|
+
const actionsAttr = target.getAttribute("actions") || "";
|
|
217
|
+
if (actionsAttr.includes(prefix)) return;
|
|
218
|
+
|
|
219
|
+
// Handle lifecycle controls (using dash)
|
|
220
|
+
const lifecycleAttributes = [
|
|
221
|
+
{ attr: "prevent-default", method: "preventDefault" },
|
|
222
|
+
{ attr: "stop-propagation", method: "stopPropagation" },
|
|
223
|
+
{ attr: "stop-immediate-propagation", method: "stopImmediatePropagation" }
|
|
224
|
+
];
|
|
225
|
+
|
|
226
|
+
for (const { attr, method } of lifecycleAttributes) {
|
|
227
|
+
const attrValue = target.getAttribute(`${prefix}-${attr}`);
|
|
228
|
+
if (attrValue !== null && attrValue !== "false") {
|
|
229
|
+
event[method]();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
self.__updateElements(target, prefix, prefixData.events);
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
_initObserver: function (el, prefix) {
|
|
238
|
+
// Updated to dash: prefix-selector, prefix-query, prefix-attribute-filter, prefix-types
|
|
239
|
+
let selector = el.getAttribute(`${prefix}-selector`) || el.getAttribute(`${prefix}-query`) || "";
|
|
240
|
+
let attributeFilter = (el.getAttribute(`${prefix}-attribute-filter`) || "")
|
|
241
|
+
.split(/\s*,\s*/)
|
|
242
|
+
.filter(item => item);
|
|
243
|
+
|
|
244
|
+
let types = (el.getAttribute(`${prefix}-types`) || "")
|
|
245
|
+
.split(/\s*,\s*/)
|
|
246
|
+
.filter(item => item);
|
|
247
|
+
|
|
248
|
+
const config = {
|
|
249
|
+
types: types,
|
|
250
|
+
callback: () => {
|
|
251
|
+
this.__updateElements(el, prefix);
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
if (selector) {
|
|
256
|
+
if (!types.length && !attributeFilter.length) {
|
|
257
|
+
config.types.push("addedNodes");
|
|
258
|
+
}
|
|
259
|
+
config.selector = selector;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (attributeFilter.length) {
|
|
263
|
+
config.types.push("attributes");
|
|
264
|
+
config.attributeFilter = attributeFilter;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (config.types.length) {
|
|
268
|
+
observer.init(config);
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
_initResizeObserver: function (el, prefix) {
|
|
273
|
+
const resizeObserver = new ResizeObserver((entries) => {
|
|
274
|
+
const lastEntry = entries[entries.length - 1];
|
|
275
|
+
const { width } = lastEntry.contentRect;
|
|
276
|
+
// Keeps dash style for this status attribute
|
|
277
|
+
el.setAttribute(`${prefix}-if-value`, width + "px");
|
|
278
|
+
this.__updateElements(el, prefix);
|
|
279
|
+
});
|
|
280
|
+
resizeObserver.observe(el);
|
|
281
|
+
},
|
|
282
|
+
|
|
283
|
+
_initIntersectionObserver: function (el, prefix) {
|
|
284
|
+
// Updated to dash
|
|
285
|
+
const rootSelector = el.getAttribute(`${prefix}-root`);
|
|
286
|
+
const rootMargin = el.getAttribute(`${prefix}-root-margin`) || "0px";
|
|
287
|
+
const thresholdRaw = el.getAttribute(`${prefix}-threshold`);
|
|
288
|
+
const threshold = thresholdRaw ? JSON.parse(thresholdRaw) : [0];
|
|
289
|
+
const trigger = el.getAttribute(`${prefix}-trigger`) || "both";
|
|
290
|
+
|
|
291
|
+
const root = rootSelector ? document.querySelector(rootSelector) : null;
|
|
292
|
+
const validThreshold = Array.isArray(threshold)
|
|
293
|
+
? threshold.filter(v => v >= 0 && v <= 1)
|
|
294
|
+
: [0];
|
|
295
|
+
|
|
296
|
+
const once = el.getAttribute(`${prefix}-once`);
|
|
297
|
+
const isOnce = once !== null && once !== "false";
|
|
298
|
+
|
|
299
|
+
const intersectionObserver = new IntersectionObserver(
|
|
300
|
+
(entries) => {
|
|
301
|
+
entries.forEach((entry) => {
|
|
302
|
+
const isVisible = entry.isIntersecting;
|
|
303
|
+
|
|
304
|
+
if (
|
|
305
|
+
(trigger === "visible" && isVisible) ||
|
|
306
|
+
(trigger === "not-visible" && !isVisible) ||
|
|
307
|
+
trigger === "both"
|
|
308
|
+
) {
|
|
309
|
+
el.setAttribute(`${prefix}-is-visible`, isVisible ? "true" : "false");
|
|
310
|
+
this.__updateElements(el, prefix);
|
|
311
|
+
|
|
312
|
+
if (isOnce) {
|
|
313
|
+
intersectionObserver.unobserve(entry.target);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
},
|
|
318
|
+
{ root, rootMargin, threshold: validThreshold }
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
intersectionObserver.observe(el);
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
__updateElements: async function (rootElement, prefix, events, params) {
|
|
325
|
+
if (!rootElement.isConnected) return;
|
|
326
|
+
|
|
327
|
+
// 1. Determine targets based on prefix-group
|
|
328
|
+
let sourceElements = [rootElement];
|
|
329
|
+
const sourceGroup = rootElement.getAttribute(`${prefix}-group`);
|
|
330
|
+
|
|
331
|
+
if (sourceGroup) {
|
|
332
|
+
sourceElements = document.querySelectorAll(`[${prefix}-group="${sourceGroup}"]`);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// 2. Iterate through sourceElements
|
|
336
|
+
for (const sourceElement of sourceElements) {
|
|
337
|
+
|
|
338
|
+
// --- Global Options (Dash) ---
|
|
339
|
+
|
|
340
|
+
// onEvent
|
|
341
|
+
const onEvent = sourceElement.getAttribute(`${prefix}-on`);
|
|
342
|
+
if (onEvent) {
|
|
343
|
+
await new Promise((resolve) => {
|
|
344
|
+
const handleEvent = () => {
|
|
345
|
+
document.removeEventListener(onEvent, handleEvent);
|
|
346
|
+
resolve();
|
|
347
|
+
};
|
|
348
|
+
document.addEventListener(onEvent, handleEvent);
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// setInterval
|
|
353
|
+
const intervalAttr = sourceElement.getAttribute(`${prefix}-set-interval`);
|
|
354
|
+
if (intervalAttr !== null) {
|
|
355
|
+
const intervalMs = parseInt(intervalAttr, 10) || 0;
|
|
356
|
+
|
|
357
|
+
if (!sourceElement.eventsInterval) {
|
|
358
|
+
sourceElement.eventsInterval = {};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const currentInterval = sourceElement.eventsInterval[prefix];
|
|
362
|
+
|
|
363
|
+
if (intervalMs > 0) {
|
|
364
|
+
// Start or update interval
|
|
365
|
+
if (!currentInterval || currentInterval.delay !== intervalMs) {
|
|
366
|
+
if (currentInterval) clearInterval(currentInterval.id);
|
|
367
|
+
|
|
368
|
+
sourceElement.eventsInterval[prefix] = {
|
|
369
|
+
delay: intervalMs,
|
|
370
|
+
id: setInterval(() => {
|
|
371
|
+
if (!sourceElement.isConnected) {
|
|
372
|
+
const entry = sourceElement.eventsInterval?.[prefix];
|
|
373
|
+
if (entry) {
|
|
374
|
+
clearInterval(entry.id);
|
|
375
|
+
delete sourceElement.eventsInterval[prefix];
|
|
376
|
+
}
|
|
377
|
+
return;
|
|
265
378
|
}
|
|
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
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
let ifCondition = element.getAttribute(`${prefix}-if`);
|
|
524
|
-
let elseIfCondition = element.getAttribute(`${prefix}-else-if`);
|
|
525
|
-
let elseCondition = element.getAttribute(`${prefix}-else`);
|
|
526
|
-
|
|
527
|
-
let ifValue = element.getAttribute(`${prefix}-if-value`);
|
|
528
|
-
if (ifCondition) {
|
|
529
|
-
if (!ifValue && ifValue !== "")
|
|
530
|
-
ifValue = (await element.getValue()) || values;
|
|
531
|
-
//values // await element.getValue()
|
|
532
|
-
else if (ifValue || ifValue === "") {
|
|
533
|
-
ifValue = [ifValue];
|
|
534
|
-
} else {
|
|
535
|
-
ifValue = values;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
if (!Array.isArray(ifValue)) {
|
|
539
|
-
ifValue = [ifValue];
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
if (ifCondition) {
|
|
544
|
-
// If the 'if' condition is not met, stop processing this element.
|
|
545
|
-
if (!evaluateCondition(ifCondition, ifValue)) {
|
|
546
|
-
return;
|
|
547
|
-
}
|
|
548
|
-
} else if (elseCondition) {
|
|
549
|
-
// If there's no 'if' condition but there is an 'else' condition,
|
|
550
|
-
// stop processing if the 'else' condition is met.
|
|
551
|
-
// This creates an "execute unless" behavior for the 'else' attribute.
|
|
552
|
-
if (evaluateCondition(elseIfCondition, ifValue)) {
|
|
553
|
-
return;
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
let targetText = element.getAttribute(`${prefix}-text`);
|
|
558
|
-
let targetHtml = element.getAttribute(`${prefix}-html`);
|
|
559
|
-
let targetKey = element.getAttribute(`${prefix}-key`);
|
|
560
|
-
|
|
561
|
-
if (prefix === "selected") {
|
|
562
|
-
if (el !== element) {
|
|
563
|
-
element.removeAttribute("selected");
|
|
564
|
-
} else {
|
|
565
|
-
element.setAttribute("selected", "");
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
let deactivate = false;
|
|
570
|
-
if (el !== element) {
|
|
571
|
-
deactivate = true;
|
|
572
|
-
}
|
|
573
|
-
if (events && events.includes("click")) {
|
|
574
|
-
// TODO: handling of attributes that represent states aria-expanded, aria-selected etc.
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
// values = values.map(x => x.trim());
|
|
578
|
-
values = values.map((x) => {
|
|
579
|
-
if (typeof x === "string") x = x.trim(); // Update x with the trimmed value
|
|
580
|
-
let prop = element.getAttribute(`${prefix}-property`);
|
|
581
|
-
if (prop) {
|
|
582
|
-
x = `${prop}:${x}`; // Update x with prop if it exists
|
|
583
|
-
}
|
|
584
|
-
return x; // Return the updated x
|
|
585
|
-
});
|
|
586
|
-
let targetElements;
|
|
587
|
-
if (element.hasAttribute(`${prefix}-query`)) {
|
|
588
|
-
targetElements = queryElements({
|
|
589
|
-
element,
|
|
590
|
-
prefix,
|
|
591
|
-
selector: params
|
|
592
|
-
});
|
|
593
|
-
} else {
|
|
594
|
-
targetElements = [element];
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
let action =
|
|
598
|
-
element.getAttribute(`${prefix}-action`) ||
|
|
599
|
-
element.getAttribute(`${prefix}-method`);
|
|
600
|
-
for (let i = 0; i < targetElements.length; i++) {
|
|
601
|
-
if (action) {
|
|
602
|
-
if (targetElements[i][action]) {
|
|
603
|
-
targetElements[i][action]();
|
|
604
|
-
}
|
|
605
|
-
} else if (
|
|
606
|
-
!targetAttribute &&
|
|
607
|
-
targetAttribute !== "" &&
|
|
608
|
-
!targetPosition &&
|
|
609
|
-
["click", "focus", "blur"].includes(prefix)
|
|
610
|
-
) {
|
|
611
|
-
// TODO: click causes an infinite loop if targetElement[i] is a child of element do to event propactaion
|
|
612
|
-
if (
|
|
613
|
-
(targetElements[i] === element &&
|
|
614
|
-
element.hasAttribute(prefix)) ||
|
|
615
|
-
(params &&
|
|
616
|
-
targetElements[i] !== element &&
|
|
617
|
-
!element.contains(targetElements[i]))
|
|
618
|
-
)
|
|
619
|
-
targetElements[i][prefix]();
|
|
620
|
-
} else {
|
|
621
|
-
this.setValue(
|
|
622
|
-
prefix,
|
|
623
|
-
targetElements[i],
|
|
624
|
-
targetAttribute,
|
|
625
|
-
values,
|
|
626
|
-
targetKey,
|
|
627
|
-
deactivate,
|
|
628
|
-
events,
|
|
629
|
-
targetPosition
|
|
630
|
-
);
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
el.dispatchEvent(
|
|
636
|
-
new CustomEvent(`${prefix}End`, {
|
|
637
|
-
detail: {}
|
|
638
|
-
})
|
|
639
|
-
);
|
|
640
|
-
},
|
|
641
|
-
|
|
642
|
-
setValue: async function (
|
|
643
|
-
prefix,
|
|
644
|
-
element,
|
|
645
|
-
attrName,
|
|
646
|
-
values,
|
|
647
|
-
key,
|
|
648
|
-
deactivate,
|
|
649
|
-
events,
|
|
650
|
-
targetPosition
|
|
651
|
-
) {
|
|
652
|
-
if (events && events.includes("mouseout")) {
|
|
653
|
-
if (element.matches(":hover")) {
|
|
654
|
-
element.addEventListener(
|
|
655
|
-
"mouseout",
|
|
656
|
-
() => {
|
|
657
|
-
this.setValue(
|
|
658
|
-
prefix,
|
|
659
|
-
element,
|
|
660
|
-
attrName,
|
|
661
|
-
values,
|
|
662
|
-
key,
|
|
663
|
-
deactivate,
|
|
664
|
-
events,
|
|
665
|
-
targetPosition
|
|
666
|
-
);
|
|
667
|
-
},
|
|
668
|
-
{ once: true }
|
|
669
|
-
);
|
|
670
|
-
return;
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
let attrValues, oldValue;
|
|
675
|
-
if (key) {
|
|
676
|
-
key = `{{${key}}}`;
|
|
677
|
-
// let value = values[0]
|
|
678
|
-
replaceKey(prefix, element, key, values);
|
|
679
|
-
} else {
|
|
680
|
-
// TODO: if html in iframe
|
|
681
|
-
let domTextEditor;
|
|
682
|
-
if (element.parentElement) {
|
|
683
|
-
domTextEditor =
|
|
684
|
-
element.parentElement.closest("[contenteditable]");
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
if (attrName === "style") {
|
|
688
|
-
if (element.getAttribute(attrName)) {
|
|
689
|
-
attrValues = element
|
|
690
|
-
.getAttribute(attrName)
|
|
691
|
-
.split(";")
|
|
692
|
-
.map((x) => x.trim());
|
|
693
|
-
attrValues = attrValues.map(function (item, index) {
|
|
694
|
-
return item.replace(" ", "");
|
|
695
|
-
});
|
|
696
|
-
oldValue =
|
|
697
|
-
values.filter((x) => attrValues.includes(x))[0] || "";
|
|
698
|
-
}
|
|
699
|
-
let newValue = this.__getNextValue(values, oldValue);
|
|
700
|
-
|
|
701
|
-
if (oldValue) {
|
|
702
|
-
let [property, value] = oldValue.split(":");
|
|
703
|
-
element.style[property] = "";
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
if (newValue != "") {
|
|
707
|
-
newValue = newValue.split(";").map((x) => x.trim());
|
|
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
|
-
}
|
|
379
|
+
// Recursive call
|
|
380
|
+
this.__updateElements(rootElement, prefix, events, params);
|
|
381
|
+
}, intervalMs)
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
} else if (currentInterval) {
|
|
385
|
+
// Clear interval if set to 0
|
|
386
|
+
clearInterval(currentInterval.id);
|
|
387
|
+
delete sourceElement.eventsInterval[prefix];
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// setTimeout
|
|
392
|
+
const delayAttr = sourceElement.getAttribute(`${prefix}-set-timeout`);
|
|
393
|
+
if (delayAttr) {
|
|
394
|
+
const delayTime = parseInt(delayAttr, 10) || 0;
|
|
395
|
+
if (delayTime > 0) {
|
|
396
|
+
await new Promise(resolve => setTimeout(resolve, delayTime));
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (!sourceElement.isConnected) return;
|
|
401
|
+
|
|
402
|
+
// once
|
|
403
|
+
const onceAttr = sourceElement.getAttribute(`${prefix}-once`);
|
|
404
|
+
if (onceAttr !== null) {
|
|
405
|
+
if (!sourceElement.eventsOnce) {
|
|
406
|
+
sourceElement.eventsOnce = [prefix];
|
|
407
|
+
} else if (sourceElement.eventsOnce.includes(prefix)) {
|
|
408
|
+
continue; // Skip if already ran
|
|
409
|
+
} else {
|
|
410
|
+
sourceElement.eventsOnce.push(prefix);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// --- Resolve Targets ---
|
|
415
|
+
const querySelector = sourceElement.getAttribute(`${prefix}-query`);
|
|
416
|
+
let targetElements = [sourceElement];
|
|
417
|
+
|
|
418
|
+
if (querySelector || params) {
|
|
419
|
+
targetElements = queryElements({
|
|
420
|
+
element: sourceElement,
|
|
421
|
+
prefix: prefix,
|
|
422
|
+
selector: querySelector || params
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// --- Execute Action on Targets ---
|
|
427
|
+
for (const targetElement of targetElements) {
|
|
428
|
+
await this.setValue(
|
|
429
|
+
rootElement,
|
|
430
|
+
sourceElement,
|
|
431
|
+
targetElement,
|
|
432
|
+
prefix,
|
|
433
|
+
events
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
rootElement.dispatchEvent(
|
|
439
|
+
new CustomEvent(`${prefix}End`, { detail: {} })
|
|
440
|
+
);
|
|
441
|
+
},
|
|
442
|
+
|
|
443
|
+
setValue: async function (rootElement, sourceElement, targetElement, prefix, events) {
|
|
444
|
+
// Handle Mouseout toggle logic
|
|
445
|
+
if (events && events.includes("mouseout")) {
|
|
446
|
+
if (targetElement.matches(":hover")) {
|
|
447
|
+
targetElement.addEventListener(
|
|
448
|
+
"mouseout",
|
|
449
|
+
() => {
|
|
450
|
+
this.setValue(rootElement, sourceElement, targetElement, prefix, events);
|
|
451
|
+
},
|
|
452
|
+
{ once: true }
|
|
453
|
+
);
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const deactivate = (rootElement !== sourceElement);
|
|
459
|
+
|
|
460
|
+
// Iterate Attributes
|
|
461
|
+
for (const attribute of sourceElement.attributes) {
|
|
462
|
+
const name = attribute.name;
|
|
463
|
+
|
|
464
|
+
// Optimization: Skip if shorter than prefix + dot + min char
|
|
465
|
+
if (name.length <= prefix.length + 1) continue;
|
|
466
|
+
|
|
467
|
+
// STRICT RULE: Only process attributes with DOT separator.
|
|
468
|
+
// All options use Dash, so they will be ignored here.
|
|
469
|
+
if (!name.startsWith(prefix) || name[prefix.length] !== '.') continue;
|
|
470
|
+
|
|
471
|
+
// Extract target name (e.g., 'value', 'style', 'src', 'data-something')
|
|
472
|
+
const targetName = name.substring(prefix.length + 1);
|
|
473
|
+
|
|
474
|
+
// Get Value
|
|
475
|
+
let value = sourceElement.getAttribute(name);
|
|
476
|
+
if (!value && sourceElement.getValue) {
|
|
477
|
+
value = await sourceElement.getValue();
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Parse CSV for rotation
|
|
481
|
+
let values = [];
|
|
482
|
+
if (typeof value === 'string') {
|
|
483
|
+
values = value.split(',').map(v => v.trim());
|
|
484
|
+
} else if (Array.isArray(value)) {
|
|
485
|
+
values = value;
|
|
486
|
+
} else {
|
|
487
|
+
values = [value];
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
let oldValue = "";
|
|
491
|
+
let newValue = "";
|
|
492
|
+
|
|
493
|
+
switch (targetName) {
|
|
494
|
+
case 'style':
|
|
495
|
+
if (targetElement.getAttribute("style")) {
|
|
496
|
+
// Normalize style string for comparison
|
|
497
|
+
const styles = targetElement.getAttribute("style")
|
|
498
|
+
.split(";")
|
|
499
|
+
.map(x => x.trim().replace(/\s/g, ""));
|
|
500
|
+
|
|
501
|
+
oldValue = values.find(x => styles.includes(x.replace(/\s/g, ""))) || "";
|
|
502
|
+
}
|
|
503
|
+
newValue = this.__getNextValue(values, oldValue, deactivate);
|
|
504
|
+
|
|
505
|
+
if (oldValue) {
|
|
506
|
+
const [prop] = oldValue.split(":");
|
|
507
|
+
if (prop) targetElement.style[prop.trim()] = "";
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (newValue) {
|
|
511
|
+
const styles = newValue.split(";");
|
|
512
|
+
for (const s of styles) {
|
|
513
|
+
const [prop, val] = s.split(":");
|
|
514
|
+
if (prop && val) targetElement.style[prop.trim()] = val.trim();
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
break;
|
|
518
|
+
|
|
519
|
+
case 'class':
|
|
520
|
+
const currentClasses = Array.from(targetElement.classList);
|
|
521
|
+
oldValue = values.find(val => {
|
|
522
|
+
const requiredClasses = val.split(" ");
|
|
523
|
+
return requiredClasses.every(c => currentClasses.includes(c));
|
|
524
|
+
}) || "";
|
|
525
|
+
|
|
526
|
+
newValue = this.__getNextValue(values, oldValue, deactivate);
|
|
527
|
+
|
|
528
|
+
if (oldValue) {
|
|
529
|
+
oldValue.split(" ").forEach(c => targetElement.classList.remove(c));
|
|
530
|
+
if (values.length === 1 && !newValue) return;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if (newValue) {
|
|
534
|
+
newValue.split(" ").forEach(c => targetElement.classList.add(c));
|
|
535
|
+
}
|
|
536
|
+
break;
|
|
537
|
+
|
|
538
|
+
case 'insertadjacenthtml':
|
|
539
|
+
case 'insertadjacenttext':
|
|
540
|
+
case 'insertAdjacentHTML':
|
|
541
|
+
case 'insertAdjacentText':
|
|
542
|
+
const position = sourceElement.getAttribute(name);;
|
|
543
|
+
if (!position) continue;
|
|
544
|
+
|
|
545
|
+
let content = "";
|
|
546
|
+
if (sourceElement.getValue) {
|
|
547
|
+
content = await sourceElement.getValue();
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// Check for CoCreate Rich Text integration
|
|
551
|
+
let domTextEditor = targetElement.parentElement?.closest("[contenteditable]");
|
|
552
|
+
|
|
553
|
+
if (domTextEditor && typeof CoCreate !== 'undefined' && CoCreate.text) {
|
|
554
|
+
CoCreate.text.insertAdjacentElement({
|
|
555
|
+
domTextEditor,
|
|
556
|
+
position,
|
|
557
|
+
target: targetElement,
|
|
558
|
+
elementValue: content
|
|
559
|
+
});
|
|
560
|
+
} else {
|
|
561
|
+
const isHtml = targetName.toLowerCase().includes('html');
|
|
562
|
+
if (isHtml) {
|
|
563
|
+
targetElement.insertAdjacentHTML(position, content);
|
|
564
|
+
} else {
|
|
565
|
+
targetElement.insertAdjacentText(position, content);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
break;
|
|
569
|
+
|
|
570
|
+
case 'value':
|
|
571
|
+
if (targetElement.getValue) {
|
|
572
|
+
oldValue = await targetElement.getValue();
|
|
573
|
+
} else {
|
|
574
|
+
oldValue = targetElement.value || "";
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (!values.includes(oldValue)) oldValue = "";
|
|
578
|
+
newValue = this.__getNextValue(values, oldValue, deactivate);
|
|
579
|
+
|
|
580
|
+
if (targetElement.setValue) {
|
|
581
|
+
targetElement.setValue(newValue);
|
|
582
|
+
} else {
|
|
583
|
+
targetElement.value = newValue;
|
|
584
|
+
}
|
|
585
|
+
break;
|
|
586
|
+
|
|
587
|
+
default:
|
|
588
|
+
// Generic handling: Try Property first, then Attribute
|
|
589
|
+
|
|
590
|
+
// 1. Try Property
|
|
591
|
+
if (targetElement[targetName] !== undefined) {
|
|
592
|
+
oldValue = targetElement[targetName];
|
|
593
|
+
if (!values.includes(oldValue) && typeof oldValue !== 'function') oldValue = "";
|
|
594
|
+
|
|
595
|
+
newValue = this.__getNextValue(values, oldValue, deactivate);
|
|
596
|
+
|
|
597
|
+
if (typeof targetElement[targetName] === 'function') {
|
|
598
|
+
targetElement[targetName](newValue);
|
|
599
|
+
} else {
|
|
600
|
+
targetElement[targetName] = newValue;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
// 2. Fallback to Attribute
|
|
604
|
+
else {
|
|
605
|
+
oldValue = targetElement.getAttribute(targetName);
|
|
606
|
+
if (!values.includes(oldValue)) oldValue = "";
|
|
607
|
+
|
|
608
|
+
newValue = this.__getNextValue(values, oldValue, deactivate);
|
|
609
|
+
|
|
610
|
+
// Handle Namespaced Attributes (e.g. svg:href) if encoded in name
|
|
611
|
+
if (targetName.includes('.')) {
|
|
612
|
+
const realName = targetName.replace('.', ':');
|
|
613
|
+
targetElement.setAttribute(realName, newValue);
|
|
614
|
+
} else {
|
|
615
|
+
targetElement.setAttribute(targetName, newValue);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
break;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
},
|
|
622
|
+
|
|
623
|
+
__getNextValue: function (values, val, deactivate) {
|
|
624
|
+
if (deactivate) return "";
|
|
625
|
+
const size = values.length;
|
|
626
|
+
const nn = values.indexOf(val);
|
|
627
|
+
if (nn === -1) {
|
|
628
|
+
return values[0];
|
|
629
|
+
} else {
|
|
630
|
+
return values[(nn + 1) % size];
|
|
631
|
+
}
|
|
632
|
+
}
|
|
798
633
|
};
|
|
799
634
|
|
|
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
635
|
CoCreateEvents.init();
|
|
1152
636
|
|
|
1153
|
-
export default CoCreateEvents;
|
|
637
|
+
export default CoCreateEvents;
|