@cocreate/events 1.26.0 → 1.27.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 +29 -0
- package/demo/click.html +83 -83
- package/demo/hover.html +65 -65
- package/demo/index.html +124 -124
- package/demo/observer.html +36 -33
- package/demo/selected.html +78 -78
- package/demo/set-value.html +21 -25
- package/docs/index.html +350 -346
- package/package.json +8 -12
- package/src/index.js +355 -83
- package/webpack.config.js +65 -90
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cocreate/events",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.27.1",
|
|
4
4
|
"description": "A simple events component in vanilla javascript. Easily configured using HTML5 attributes and/or JavaScript API.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"events",
|
|
@@ -45,22 +45,18 @@
|
|
|
45
45
|
},
|
|
46
46
|
"main": "./src/index.js",
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"clean-webpack-plugin": "^3.0.0",
|
|
52
|
-
"file-loader": "^6.2.0",
|
|
48
|
+
"css-loader": "^5.1.3",
|
|
49
|
+
"esbuild": "^0.25.2",
|
|
50
|
+
"esbuild-loader": "^4.3.0",
|
|
53
51
|
"mini-css-extract-plugin": "^1.5.0",
|
|
54
|
-
"style-loader": "^3.3.1",
|
|
55
|
-
"terser-webpack-plugin": "^5.1.1",
|
|
56
52
|
"webpack": "^5.24.4",
|
|
57
53
|
"webpack-cli": "^4.5.0",
|
|
58
54
|
"webpack-log": "^3.0.1"
|
|
59
55
|
},
|
|
60
56
|
"dependencies": {
|
|
61
|
-
"@cocreate/actions": "^1.
|
|
62
|
-
"@cocreate/element-prototype": "^1.
|
|
63
|
-
"@cocreate/observer": "^1.
|
|
64
|
-
"@cocreate/utils": "^1.
|
|
57
|
+
"@cocreate/actions": "^1.21.0",
|
|
58
|
+
"@cocreate/element-prototype": "^1.29.0",
|
|
59
|
+
"@cocreate/observer": "^1.18.0",
|
|
60
|
+
"@cocreate/utils": "^1.38.0"
|
|
65
61
|
}
|
|
66
62
|
}
|
package/src/index.js
CHANGED
|
@@ -63,7 +63,7 @@ const CoCreateEvents = {
|
|
|
63
63
|
const self = this;
|
|
64
64
|
observer.init({
|
|
65
65
|
name: "CoCreateEventName",
|
|
66
|
-
|
|
66
|
+
types: ["addedNodes"],
|
|
67
67
|
selector: `[event-name]`,
|
|
68
68
|
callback: function (mutation) {
|
|
69
69
|
let name = mutation.target.getAttribute("event-name");
|
|
@@ -89,20 +89,22 @@ const CoCreateEvents = {
|
|
|
89
89
|
}
|
|
90
90
|
});
|
|
91
91
|
|
|
92
|
-
let selector = `[${prefix}], [${prefix}-key], [${prefix}-attribute], [${prefix}-value], [${prefix}-action], [${prefix}-
|
|
92
|
+
let selector = `[${prefix}], [${prefix}-key], [${prefix}-attribute], [${prefix}-value], [${prefix}-action], [${prefix}-query], [${prefix}-preventDefault], [${prefix}-stopPropagation], [${prefix}-stopImmediatePropagation]`;
|
|
93
93
|
|
|
94
94
|
observer.init({
|
|
95
95
|
name: "CoCreateEventattributes",
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
types: ["attributes", "addedNodes"],
|
|
97
|
+
attributeFilter: [
|
|
98
98
|
`${prefix}-key`,
|
|
99
99
|
`${prefix}-value`,
|
|
100
|
-
`${prefix}-
|
|
101
|
-
`${prefix}-
|
|
102
|
-
`${prefix}-
|
|
103
|
-
`${prefix}-
|
|
104
|
-
`${prefix}-
|
|
105
|
-
`${prefix}-
|
|
100
|
+
`${prefix}-query`,
|
|
101
|
+
`${prefix}-events`,
|
|
102
|
+
`${prefix}-bubbles`,
|
|
103
|
+
`${prefix}-cancelable`,
|
|
104
|
+
`${prefix}-composed`,
|
|
105
|
+
`${prefix}-preventDefault`,
|
|
106
|
+
`${prefix}-stopPropagation`,
|
|
107
|
+
`${prefix}-stopImmediatePropagation`
|
|
106
108
|
],
|
|
107
109
|
selector,
|
|
108
110
|
callback: function (mutation) {
|
|
@@ -125,7 +127,9 @@ const CoCreateEvents = {
|
|
|
125
127
|
this.elements.set(el, prefixes);
|
|
126
128
|
} else if (!prefixes[prefix]) {
|
|
127
129
|
prefixes[prefix] = { events };
|
|
128
|
-
} else
|
|
130
|
+
} else {
|
|
131
|
+
isEventable = false;
|
|
132
|
+
}
|
|
129
133
|
|
|
130
134
|
let customEvents = el.getAttribute(`${prefix}-events`);
|
|
131
135
|
if (customEvents) {
|
|
@@ -140,8 +144,25 @@ const CoCreateEvents = {
|
|
|
140
144
|
prefixes[prefix].events = events;
|
|
141
145
|
isEventable = true;
|
|
142
146
|
}
|
|
147
|
+
|
|
143
148
|
if (!events || !isEventable) continue;
|
|
144
|
-
|
|
149
|
+
|
|
150
|
+
// let originalSelector = ""; // i think we need to extract the selector and remove the operators
|
|
151
|
+
// if (originalSelector) {
|
|
152
|
+
// observer.init({
|
|
153
|
+
// types: ["addedNodes"],
|
|
154
|
+
// callback: function (mutation) {
|
|
155
|
+
// const els = el.queryElements();
|
|
156
|
+
// if (els && els.includes(mutation.target)) {
|
|
157
|
+
// self.__updateElements();
|
|
158
|
+
// }
|
|
159
|
+
// }
|
|
160
|
+
// });
|
|
161
|
+
// }
|
|
162
|
+
|
|
163
|
+
if (events.includes("onload")) {
|
|
164
|
+
this.__updateElements(el, prefix);
|
|
165
|
+
}
|
|
145
166
|
|
|
146
167
|
if (events.includes("observer")) {
|
|
147
168
|
let target;
|
|
@@ -149,21 +170,13 @@ const CoCreateEvents = {
|
|
|
149
170
|
if (attribute.name === "observe-target") {
|
|
150
171
|
target = attribute.value;
|
|
151
172
|
break;
|
|
152
|
-
} else if (
|
|
153
|
-
[
|
|
154
|
-
`${prefix}-selector`,
|
|
155
|
-
`${prefix}-closest`,
|
|
156
|
-
`${prefix}-parent`,
|
|
157
|
-
`${prefix}-next`,
|
|
158
|
-
`${prefix}-previous`
|
|
159
|
-
].includes(attribute.name)
|
|
160
|
-
) {
|
|
173
|
+
} else if ([`${prefix}-query`].includes(attribute.name)) {
|
|
161
174
|
target = attribute.value;
|
|
162
175
|
break;
|
|
163
176
|
}
|
|
164
177
|
}
|
|
165
178
|
|
|
166
|
-
let
|
|
179
|
+
let attributeFilter = (
|
|
167
180
|
el.getAttribute(`${prefix}-attributes`) || ""
|
|
168
181
|
)
|
|
169
182
|
.split(/\s*,\s*/)
|
|
@@ -176,7 +189,7 @@ const CoCreateEvents = {
|
|
|
176
189
|
.filter((item) => item);
|
|
177
190
|
|
|
178
191
|
let observerConfig = {
|
|
179
|
-
|
|
192
|
+
types: observeAttribute,
|
|
180
193
|
callback: function (mutation) {
|
|
181
194
|
self.__updateElements(el, prefix, mutation.target);
|
|
182
195
|
}
|
|
@@ -186,20 +199,19 @@ const CoCreateEvents = {
|
|
|
186
199
|
if (
|
|
187
200
|
target &&
|
|
188
201
|
!observeAttribute.length &&
|
|
189
|
-
!
|
|
202
|
+
!attributeFilter.length
|
|
190
203
|
) {
|
|
191
|
-
observerConfig.
|
|
204
|
+
observerConfig.types.push("addedNodes");
|
|
192
205
|
}
|
|
193
206
|
observerConfig.target = target;
|
|
194
207
|
}
|
|
195
208
|
|
|
196
|
-
if (
|
|
197
|
-
observerConfig.
|
|
198
|
-
observerConfig.
|
|
209
|
+
if (attributeFilter.length) {
|
|
210
|
+
observerConfig.types.push("attributes");
|
|
211
|
+
observerConfig.attributeFilter = attributeFilter;
|
|
199
212
|
}
|
|
200
213
|
|
|
201
|
-
if (observerConfig.
|
|
202
|
-
observer.init(observerConfig);
|
|
214
|
+
if (observerConfig.types.length) observer.init(observerConfig);
|
|
203
215
|
}
|
|
204
216
|
|
|
205
217
|
if (events.includes("resize")) {
|
|
@@ -270,19 +282,40 @@ const CoCreateEvents = {
|
|
|
270
282
|
}
|
|
271
283
|
|
|
272
284
|
for (let i = 0; i < events.length; i++) {
|
|
285
|
+
// Skip specific lifecycle events
|
|
273
286
|
if (
|
|
274
|
-
events[i]
|
|
275
|
-
events[i]
|
|
276
|
-
events[i]
|
|
277
|
-
events[i]
|
|
287
|
+
events[i] === "onload" ||
|
|
288
|
+
events[i] === "observer" ||
|
|
289
|
+
events[i] === "intersection" ||
|
|
290
|
+
events[i] === "resize"
|
|
278
291
|
) {
|
|
279
|
-
|
|
280
|
-
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Remove any existing event listener
|
|
296
|
+
el.removeEventListener(events[i], eventFunction);
|
|
297
|
+
|
|
298
|
+
// Initialize an empty options object
|
|
299
|
+
const options = {};
|
|
300
|
+
|
|
301
|
+
// Attributes to check dynamically
|
|
302
|
+
const attributes = ["bubbles", "cancelable", "composed"];
|
|
303
|
+
|
|
304
|
+
for (let j = 0; j < attributes.length; j++) {
|
|
305
|
+
const attrValue = el.getAttribute(
|
|
306
|
+
`${events[i]}-${attributes[j]}`
|
|
307
|
+
);
|
|
308
|
+
if (attrValue !== null) {
|
|
309
|
+
options[attributes[j]] = attrValue !== "false";
|
|
310
|
+
}
|
|
281
311
|
}
|
|
312
|
+
|
|
313
|
+
// Add the new event listener with the dynamically constructed options
|
|
314
|
+
el.addEventListener(events[i], eventFunction, options);
|
|
282
315
|
}
|
|
283
316
|
|
|
284
317
|
function eventFunction(event) {
|
|
285
|
-
// TODO: apply debounce
|
|
318
|
+
// TODO: apply debounce.
|
|
286
319
|
// let debounce;
|
|
287
320
|
// clearTimeout(debounce);
|
|
288
321
|
// debounce = setTimeout(function() {
|
|
@@ -300,6 +333,25 @@ const CoCreateEvents = {
|
|
|
300
333
|
if (attribute.includes(prefix)) return;
|
|
301
334
|
// if (target.closest(`[actions*="${prefix}"]`))
|
|
302
335
|
// return;
|
|
336
|
+
|
|
337
|
+
// Define the lifecycle attributes (matching JavaScript event methods)
|
|
338
|
+
const lifecycleAttributes = [
|
|
339
|
+
"preventDefault",
|
|
340
|
+
"stopPropagation",
|
|
341
|
+
"stopImmediatePropagation"
|
|
342
|
+
];
|
|
343
|
+
|
|
344
|
+
// Handle lifecycle attributes dynamically
|
|
345
|
+
for (let i = 0; i < lifecycleAttributes.length; i++) {
|
|
346
|
+
const attr = `${prefix}-${lifecycleAttributes[i]}`; // Add prefix dynamically
|
|
347
|
+
const attrValue = target.getAttribute(attr); // Directly get the attribute value
|
|
348
|
+
|
|
349
|
+
// If attribute exists and is not explicitly "false", apply the corresponding event method
|
|
350
|
+
if (attrValue !== null && attrValue !== "false") {
|
|
351
|
+
event[lifecycleAttributes[i]]();
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
303
355
|
self.__updateElements(
|
|
304
356
|
target,
|
|
305
357
|
prefix,
|
|
@@ -307,7 +359,7 @@ const CoCreateEvents = {
|
|
|
307
359
|
prefixes[prefix].events
|
|
308
360
|
);
|
|
309
361
|
|
|
310
|
-
// let selector = `[${prefix}], [${prefix}-key], [${prefix}-value], [${prefix}-
|
|
362
|
+
// let selector = `[${prefix}], [${prefix}-key], [${prefix}-value], [${prefix}-query]`
|
|
311
363
|
|
|
312
364
|
// let parentElement = target.parentElement;
|
|
313
365
|
// if (parentElement) {
|
|
@@ -341,7 +393,26 @@ const CoCreateEvents = {
|
|
|
341
393
|
}
|
|
342
394
|
|
|
343
395
|
for (let element of elements) {
|
|
344
|
-
|
|
396
|
+
// const requestAnimationFrameAttr = element.getAttribute(
|
|
397
|
+
// `${prefix}-requestAnimationFrame`
|
|
398
|
+
// );
|
|
399
|
+
|
|
400
|
+
// if (
|
|
401
|
+
// requestAnimationFrameAttr !== null &&
|
|
402
|
+
// requestAnimationFrameAttr !== "false"
|
|
403
|
+
// ) {
|
|
404
|
+
// // Double requestAnimationFrame
|
|
405
|
+
// await new Promise((resolve) => {
|
|
406
|
+
// requestAnimationFrame(() => {
|
|
407
|
+
// requestAnimationFrame(() => {
|
|
408
|
+
// resolve();
|
|
409
|
+
// });
|
|
410
|
+
// });
|
|
411
|
+
// });
|
|
412
|
+
// }
|
|
413
|
+
|
|
414
|
+
// TODO: Handle setInterval logic
|
|
415
|
+
let delay = element.getAttribute(`${prefix}-setTimeout`);
|
|
345
416
|
|
|
346
417
|
if (delay) {
|
|
347
418
|
delay = parseInt(delay, 10) || 0;
|
|
@@ -390,7 +461,9 @@ const CoCreateEvents = {
|
|
|
390
461
|
values = localStorage.getItem(key);
|
|
391
462
|
} else if ((key = element.getAttribute("localstorage-set"))) {
|
|
392
463
|
values = await element.getValue();
|
|
393
|
-
if (values)
|
|
464
|
+
if (values) {
|
|
465
|
+
localStorage.setItem(key, values);
|
|
466
|
+
}
|
|
394
467
|
}
|
|
395
468
|
|
|
396
469
|
// if (!key || !values)
|
|
@@ -403,59 +476,95 @@ const CoCreateEvents = {
|
|
|
403
476
|
element,
|
|
404
477
|
prefix: `${prefix}-value`
|
|
405
478
|
});
|
|
406
|
-
if (valueElements) {
|
|
479
|
+
if (valueElements.length) {
|
|
407
480
|
let elementValues = [];
|
|
408
|
-
for (let i = 0; i < valueElements.length; i++)
|
|
481
|
+
for (let i = 0; i < valueElements.length; i++) {
|
|
409
482
|
elementValues.push(valueElements[i].getValue());
|
|
410
|
-
|
|
411
|
-
if (elementValues.length)
|
|
483
|
+
}
|
|
484
|
+
if (elementValues.length) {
|
|
485
|
+
values = elementValues;
|
|
486
|
+
}
|
|
412
487
|
}
|
|
413
488
|
}
|
|
414
489
|
|
|
415
|
-
if (values === null)
|
|
490
|
+
if (values === null) {
|
|
416
491
|
values = element.getAttribute(`${prefix}-if-value`);
|
|
417
|
-
|
|
492
|
+
}
|
|
493
|
+
if (values === null) {
|
|
494
|
+
values = element.getAttribute(prefix);
|
|
495
|
+
}
|
|
418
496
|
}
|
|
419
497
|
|
|
420
498
|
if (values || values === "") {
|
|
421
|
-
if (typeof values === "string")
|
|
422
|
-
|
|
499
|
+
if (typeof values === "string") {
|
|
500
|
+
values = values.split(",");
|
|
501
|
+
} else if (!Array.isArray(values)) {
|
|
502
|
+
values = [values];
|
|
503
|
+
}
|
|
423
504
|
} else {
|
|
424
505
|
values = await element.getValue();
|
|
425
|
-
if (!Array.isArray(values))
|
|
506
|
+
if (!Array.isArray(values)) {
|
|
507
|
+
values = [values];
|
|
508
|
+
}
|
|
426
509
|
}
|
|
427
510
|
|
|
428
|
-
if ((targetAttribute && !values) || values.length === 0)
|
|
511
|
+
if ((targetAttribute && !values) || values.length === 0) {
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
429
514
|
|
|
430
515
|
let ifCondition = element.getAttribute(`${prefix}-if`);
|
|
431
516
|
let elseCondition = element.getAttribute(`${prefix}-else`);
|
|
432
517
|
|
|
433
518
|
let ifValue = element.getAttribute(`${prefix}-if-value`);
|
|
434
|
-
if (
|
|
435
|
-
ifValue
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
519
|
+
if (ifCondition) {
|
|
520
|
+
if (!ifValue && ifValue !== "")
|
|
521
|
+
ifValue = (await element.getValue()) || values;
|
|
522
|
+
//values // await element.getValue()
|
|
523
|
+
else if (ifValue || ifValue === "") {
|
|
524
|
+
ifValue = [ifValue];
|
|
525
|
+
} else {
|
|
526
|
+
ifValue = values;
|
|
527
|
+
}
|
|
439
528
|
|
|
440
|
-
|
|
529
|
+
if (!Array.isArray(ifValue)) {
|
|
530
|
+
ifValue = [ifValue];
|
|
531
|
+
}
|
|
532
|
+
}
|
|
441
533
|
|
|
442
534
|
//TODO: improved resize toggling of values
|
|
443
535
|
// let hasCondition = this.elements2.get(element)
|
|
444
|
-
if (ifCondition
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
536
|
+
if (ifCondition) {
|
|
537
|
+
if (evaluateCondition(ifCondition, ifValue)) {
|
|
538
|
+
// Action 1: Condition exists and evaluates true
|
|
539
|
+
// console.log("Executing Action 1 for ifCondition:", ifCondition); // Optional debug log
|
|
540
|
+
// Replace this comment with your actual code for Path 1
|
|
541
|
+
// e.g., this.elements2.set(element, { condition: ifCondition });
|
|
542
|
+
|
|
543
|
+
// Assuming we stop processing for this element once a condition is met and actioned
|
|
544
|
+
return;
|
|
545
|
+
} else {
|
|
546
|
+
// Condition existed but evaluated false. Stop processing.
|
|
547
|
+
// Corresponds to the original `else if (ifCondition || elseCondition) { return; }` for the ifCondition case
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// If we reach here, ifCondition was falsy (didn't exist)
|
|
553
|
+
// Now check the 'else' condition
|
|
554
|
+
if (elseCondition) {
|
|
555
|
+
if (evaluateCondition(elseCondition, ifValue)) {
|
|
556
|
+
// Action 2: Condition exists and evaluates true
|
|
557
|
+
// console.log("Executing Action 2 for elseCondition:", elseCondition); // Optional debug log
|
|
558
|
+
// Replace this comment with your actual code for Path 2
|
|
559
|
+
// e.g., this.elements2.set(element, { condition: elseCondition });
|
|
560
|
+
|
|
561
|
+
// Assuming we stop processing for this element once a condition is met and actioned
|
|
562
|
+
return;
|
|
563
|
+
} else {
|
|
564
|
+
// Condition existed but evaluated false. Stop processing.
|
|
565
|
+
// Corresponds to the original `else if (ifCondition || elseCondition) { return; }` for the elseCondition case
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
459
568
|
}
|
|
460
569
|
|
|
461
570
|
let targetText = element.getAttribute(`${prefix}-text`);
|
|
@@ -463,12 +572,17 @@ const CoCreateEvents = {
|
|
|
463
572
|
let targetKey = element.getAttribute(`${prefix}-key`);
|
|
464
573
|
|
|
465
574
|
if (prefix === "selected") {
|
|
466
|
-
if (el !== element)
|
|
467
|
-
|
|
575
|
+
if (el !== element) {
|
|
576
|
+
element.removeAttribute("selected");
|
|
577
|
+
} else {
|
|
578
|
+
element.setAttribute("selected", "");
|
|
579
|
+
}
|
|
468
580
|
}
|
|
469
581
|
|
|
470
582
|
let deactivate = false;
|
|
471
|
-
if (el !== element)
|
|
583
|
+
if (el !== element) {
|
|
584
|
+
deactivate = true;
|
|
585
|
+
}
|
|
472
586
|
// values = values.map(x => x.trim());
|
|
473
587
|
|
|
474
588
|
values = values.map((x) => {
|
|
@@ -479,13 +593,17 @@ const CoCreateEvents = {
|
|
|
479
593
|
}
|
|
480
594
|
return x; // Return the updated x
|
|
481
595
|
});
|
|
596
|
+
let targetElements;
|
|
597
|
+
if (element.hasAttribute(`${prefix}-query`)) {
|
|
598
|
+
targetElements = queryElements({
|
|
599
|
+
element,
|
|
600
|
+
prefix,
|
|
601
|
+
selector: params
|
|
602
|
+
});
|
|
603
|
+
} else {
|
|
604
|
+
targetElements = [element];
|
|
605
|
+
}
|
|
482
606
|
|
|
483
|
-
let targetElements = queryElements({
|
|
484
|
-
element,
|
|
485
|
-
prefix,
|
|
486
|
-
selector: params
|
|
487
|
-
});
|
|
488
|
-
if (targetElements === false) targetElements = [element];
|
|
489
607
|
let action = element.getAttribute(`${prefix}-action`);
|
|
490
608
|
for (let i = 0; i < targetElements.length; i++) {
|
|
491
609
|
if (action) {
|
|
@@ -520,7 +638,7 @@ const CoCreateEvents = {
|
|
|
520
638
|
}
|
|
521
639
|
}
|
|
522
640
|
|
|
523
|
-
|
|
641
|
+
el.dispatchEvent(
|
|
524
642
|
new CustomEvent(`${prefix}End`, {
|
|
525
643
|
detail: {}
|
|
526
644
|
})
|
|
@@ -565,7 +683,7 @@ const CoCreateEvents = {
|
|
|
565
683
|
// let value = values[0]
|
|
566
684
|
replaceKey(prefix, element, key, values);
|
|
567
685
|
} else {
|
|
568
|
-
//
|
|
686
|
+
// TODO: if html in iframe
|
|
569
687
|
let domTextEditor;
|
|
570
688
|
if (element.parentElement) {
|
|
571
689
|
domTextEditor =
|
|
@@ -628,7 +746,7 @@ const CoCreateEvents = {
|
|
|
628
746
|
element.classList.add(newValue);
|
|
629
747
|
}
|
|
630
748
|
} else if (attrName === "value") {
|
|
631
|
-
element.setValue(newValue);
|
|
749
|
+
element.setValue(newValue, false);
|
|
632
750
|
} else if (
|
|
633
751
|
["$click", "$focus", "$blur", "$save", "$read"].includes(
|
|
634
752
|
attrName
|
|
@@ -871,6 +989,160 @@ function parseNumberCondition(condition) {
|
|
|
871
989
|
: condition;
|
|
872
990
|
}
|
|
873
991
|
|
|
992
|
+
// Helper function for delay using Promises
|
|
993
|
+
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
994
|
+
|
|
995
|
+
/**
|
|
996
|
+
* Waits for a specific element property to meet a readiness condition,
|
|
997
|
+
* based on retry attributes defined on the element.
|
|
998
|
+
* Incorporates optional exponential backoff for retry delays.
|
|
999
|
+
* Returns a Promise that resolves to true if the condition is met within
|
|
1000
|
+
* the allowed attempts, and false otherwise.
|
|
1001
|
+
*
|
|
1002
|
+
* @param {HTMLElement} element The element with the onload-retry-* attributes.
|
|
1003
|
+
* @returns {Promise<boolean>} A Promise resolving to true if ready, false if timed out.
|
|
1004
|
+
*/
|
|
1005
|
+
async function checkElementReadinessWithBackoff(element) {
|
|
1006
|
+
const propertyName = element.getAttribute("onload-retry-property");
|
|
1007
|
+
|
|
1008
|
+
if (!propertyName) {
|
|
1009
|
+
return true; // No retry needed
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
// --- Retry Parameters ---
|
|
1013
|
+
const maxAttempts =
|
|
1014
|
+
parseInt(element.getAttribute("onload-retry-attempts") || "3", 10) || 3;
|
|
1015
|
+
// Initial delay (used for the first wait and as the base for backoff)
|
|
1016
|
+
const initialDelayMs =
|
|
1017
|
+
parseInt(element.getAttribute("onload-retry-delay") || "100", 10) ||
|
|
1018
|
+
100;
|
|
1019
|
+
// Exponential backoff factor (e.g., 2 for doubling). <= 1 means fixed delay.
|
|
1020
|
+
// Default to 1 (no backoff) if missing or invalid.
|
|
1021
|
+
const backoffFactor =
|
|
1022
|
+
parseFloat(
|
|
1023
|
+
element.getAttribute("onload-retry-backoff-factor") || "1"
|
|
1024
|
+
) || 1;
|
|
1025
|
+
// Optional maximum delay cap. Use Infinity if missing, invalid, or <= 0.
|
|
1026
|
+
const maxDelayMs = parseInt(
|
|
1027
|
+
element.getAttribute("onload-retry-max-delay") || "0",
|
|
1028
|
+
10
|
|
1029
|
+
);
|
|
1030
|
+
const effectiveMaxDelay =
|
|
1031
|
+
maxDelayMs && maxDelayMs > 0 ? maxDelayMs : Infinity;
|
|
1032
|
+
|
|
1033
|
+
console.log(
|
|
1034
|
+
`Checking readiness for property "${propertyName}" on element ${
|
|
1035
|
+
element.id || ""
|
|
1036
|
+
}. Max attempts: ${maxAttempts}, Initial Delay: ${initialDelayMs}ms, Backoff Factor: ${backoffFactor}, Max Delay: ${
|
|
1037
|
+
effectiveMaxDelay === Infinity ? "None" : effectiveMaxDelay + "ms"
|
|
1038
|
+
}.`
|
|
1039
|
+
);
|
|
1040
|
+
|
|
1041
|
+
// Initialize the delay to be used for the *first* wait.
|
|
1042
|
+
let currentDelay = initialDelayMs;
|
|
1043
|
+
|
|
1044
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
1045
|
+
console.log(
|
|
1046
|
+
`Attempt ${attempt}/${maxAttempts}: Checking "${propertyName}"...`
|
|
1047
|
+
);
|
|
1048
|
+
try {
|
|
1049
|
+
const propertyValue = element[propertyName];
|
|
1050
|
+
// Example check: Adjust condition as needed
|
|
1051
|
+
if (propertyValue !== undefined && propertyValue > 0) {
|
|
1052
|
+
console.log(
|
|
1053
|
+
`Property "${propertyName}" ready on attempt ${attempt}. Value: ${propertyValue}`
|
|
1054
|
+
);
|
|
1055
|
+
return true; // Condition met
|
|
1056
|
+
}
|
|
1057
|
+
} catch (error) {
|
|
1058
|
+
console.error(
|
|
1059
|
+
`Error accessing property "${propertyName}" on attempt ${attempt}:`,
|
|
1060
|
+
error
|
|
1061
|
+
);
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
// If condition not met and more attempts remain:
|
|
1065
|
+
if (attempt < maxAttempts) {
|
|
1066
|
+
// Determine the actual delay for this wait, applying the cap
|
|
1067
|
+
const delayForThisWait = Math.min(currentDelay, effectiveMaxDelay);
|
|
1068
|
+
|
|
1069
|
+
console.log(
|
|
1070
|
+
`Waiting ${Math.round(
|
|
1071
|
+
delayForThisWait
|
|
1072
|
+
)}ms before next attempt...`
|
|
1073
|
+
);
|
|
1074
|
+
await delay(delayForThisWait);
|
|
1075
|
+
|
|
1076
|
+
// Calculate the delay for the *next* potential wait, only if backoff is active
|
|
1077
|
+
if (backoffFactor > 1) {
|
|
1078
|
+
currentDelay = currentDelay * backoffFactor;
|
|
1079
|
+
// Note: We apply the cap just before the actual delay,
|
|
1080
|
+
// but the base 'currentDelay' keeps growing exponentially.
|
|
1081
|
+
// Alternatively, you could cap 'currentDelay' itself here:
|
|
1082
|
+
// currentDelay = Math.min(currentDelay * backoffFactor, effectiveMaxDelay);
|
|
1083
|
+
// Let's stick to capping just before delay() for now.
|
|
1084
|
+
}
|
|
1085
|
+
// If backoffFactor <= 1, currentDelay remains initialDelayMs (implicitly handled by initialization)
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
// If loop finishes without condition being met
|
|
1090
|
+
console.warn(
|
|
1091
|
+
`Readiness check failed for property "${propertyName}" on element ${
|
|
1092
|
+
element.id || ""
|
|
1093
|
+
} after ${maxAttempts} attempts.`
|
|
1094
|
+
);
|
|
1095
|
+
return false; // Timed out
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
// --- Example Usage (calling code doesn't change) ---
|
|
1099
|
+
|
|
1100
|
+
async function handleElementProcessingWithBackoff(element) {
|
|
1101
|
+
// Call the function that now supports backoff
|
|
1102
|
+
const isReady = await checkElementReadinessWithBackoff(element);
|
|
1103
|
+
|
|
1104
|
+
if (isReady) {
|
|
1105
|
+
console.log(
|
|
1106
|
+
`Element ${element.id || ""} is ready. Proceeding with actions.`
|
|
1107
|
+
);
|
|
1108
|
+
// --- Execute subsequent actions ---
|
|
1109
|
+
if (element.hasAttribute("onload-attribute")) {
|
|
1110
|
+
/* ... */
|
|
1111
|
+
}
|
|
1112
|
+
if (element.hasAttribute("onload-query")) {
|
|
1113
|
+
/* ... */
|
|
1114
|
+
}
|
|
1115
|
+
} else {
|
|
1116
|
+
console.warn(
|
|
1117
|
+
`Skipping actions for element ${
|
|
1118
|
+
element.id || ""
|
|
1119
|
+
} because readiness check failed.`
|
|
1120
|
+
);
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
// --- Example Elements ---
|
|
1125
|
+
|
|
1126
|
+
// With Backoff (Delays: 100, 200, 400, 800 - capped)
|
|
1127
|
+
// <span id="elem1" onload-retry-property="scrollWidth"
|
|
1128
|
+
// onload-retry-attempts="5" onload-retry-delay="100"
|
|
1129
|
+
// onload-retry-backoff-factor="2" onload-retry-max-delay="500">
|
|
1130
|
+
// Content 1
|
|
1131
|
+
// </span>
|
|
1132
|
+
// Waits would be: 100ms, 200ms, 400ms, 500ms (capped)
|
|
1133
|
+
|
|
1134
|
+
// No Backoff (factor missing/invalid/<=1)
|
|
1135
|
+
// <span id="elem2" onload-retry-property="clientHeight"
|
|
1136
|
+
// onload-retry-attempts="4" onload-retry-delay="300">
|
|
1137
|
+
// Content 2
|
|
1138
|
+
// </span>
|
|
1139
|
+
// Waits would be: 300ms, 300ms, 300ms
|
|
1140
|
+
|
|
1141
|
+
// const element1 = document.getElementById('elem1');
|
|
1142
|
+
// if (element1) handleElementProcessingWithBackoff(element1);
|
|
1143
|
+
// const element2 = document.getElementById('elem2');
|
|
1144
|
+
// if (element2) handleElementProcessingWithBackoff(element2);
|
|
1145
|
+
|
|
874
1146
|
CoCreateEvents.init();
|
|
875
1147
|
|
|
876
1148
|
export default CoCreateEvents;
|