@cocreate/events 1.25.0 → 1.27.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 CHANGED
@@ -1,3 +1,34 @@
1
+ # [1.27.0](https://github.com/CoCreate-app/CoCreate-events/compare/v1.26.0...v1.27.0) (2025-04-11)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * console.log() ([2174c1e](https://github.com/CoCreate-app/CoCreate-events/commit/2174c1e0a9fa0b777e073de30aed4f29ec59f8f6))
7
+ * formating ([58b8550](https://github.com/CoCreate-app/CoCreate-events/commit/58b8550c96c30153a94e24ce93b39914eed2fba3))
8
+ * removed console.log() ([f44cb8f](https://github.com/CoCreate-app/CoCreate-events/commit/f44cb8fdfec3b3b1787e830cd1b3bab3a1ef3fa5))
9
+ * renamed prefix-delay to prefix-setTimout ([84e511a](https://github.com/CoCreate-app/CoCreate-events/commit/84e511af8bcb521e5e8e7bc481a6f202fca04930))
10
+ * Selctor === "" returning null ([c49bc35](https://github.com/CoCreate-app/CoCreate-events/commit/c49bc35cd8a8ff9232229ee64d71bc4e32d3d6fc))
11
+ * selectors ([ccb2dee](https://github.com/CoCreate-app/CoCreate-events/commit/ccb2dee2a682bcdf601047f0783c5c44ebcfe5ed))
12
+ * update observer obseve param to type and and attributeName to attributeFilter ([44372d4](https://github.com/CoCreate-app/CoCreate-events/commit/44372d4ceba70360507cd0c94df43f4ee877be99))
13
+
14
+
15
+ ### Features
16
+
17
+ * support "*-preventDefault", "*-stopPropagation", "*-stopImmediatePropagation" ([5b10663](https://github.com/CoCreate-app/CoCreate-events/commit/5b106633f425001ca6ec7d65dc0bd00ad6368b64))
18
+
19
+ # [1.26.0](https://github.com/CoCreate-app/CoCreate-events/compare/v1.25.0...v1.26.0) (2025-01-18)
20
+
21
+
22
+ ### Bug Fixes
23
+
24
+ * removed ovserver from local storage ([b07e5f7](https://github.com/CoCreate-app/CoCreate-events/commit/b07e5f7ef2d6f8a39dc77a56a63f078d0576f85a))
25
+
26
+
27
+ ### Features
28
+
29
+ * added focus ([0b81f3e](https://github.com/CoCreate-app/CoCreate-events/commit/0b81f3edff2c254281cb5a1028d2396b3b86e9e8))
30
+ * intersection handling ([dea8872](https://github.com/CoCreate-app/CoCreate-events/commit/dea887246eb0489022450fd135418dc42767fd59))
31
+
1
32
  # [1.25.0](https://github.com/CoCreate-app/CoCreate-events/compare/v1.24.0...v1.25.0) (2024-12-14)
2
33
 
3
34
 
@@ -1,31 +1,27 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="en">
3
- <head>
4
- <title>Value | CoCreateJS</title>
3
+ <head>
4
+ <title>Value | CoCreateJS</title>
5
5
 
6
- <!-- CoCreate Favicon -->
7
- <link
8
- rel="icon"
9
- type="image/png"
10
- sizes="32x32"
11
- href="../assets/favicon.ico" />
12
- <link rel="manifest" href="/manifest.webmanifest" />
13
- </head>
14
- <body>
15
- <div click-selector="#testing" click-value="hello, bye" click-key="ty">
16
- <span id="testing" class="{{ty}}">other content</span>
17
- </div>
6
+ <!-- CoCreate Favicon -->
7
+ <link
8
+ rel="icon"
9
+ type="image/png"
10
+ sizes="32x32"
11
+ href="../assets/favicon.ico" />
12
+ <link rel="manifest" href="/manifest.webmanifest" />
13
+ </head>
14
+ <body>
15
+ <div click-selector="#testing" click-value="hello, bye" click-key="ty">
16
+ <span id="testing" class="{{ty}}">other content</span>
17
+ </div>
18
18
 
19
- <form realtime="false">
20
- <input
21
- input-selector="#testing2"
22
- input-attribute="value"
23
- value-type="array" />
19
+ <form>
20
+ <input input-selector="#testing2" input-attribute="value" />
21
+ <input id="testing2" />
22
+ </form>
24
23
 
25
- <input id="testing2" class="{{ty}}" />
26
- </form>
27
-
28
- <!-- <script src="../../../CoCreateJS/dist/CoCreate.js"></script> -->
29
- <script src="https://CoCreate.app/dist/CoCreate.js"></script>
30
- </body>
24
+ <!-- <script src="../../../CoCreateJS/dist/CoCreate.js"></script> -->
25
+ <script src="https://CoCreate.app/dist/CoCreate.js"></script>
26
+ </body>
31
27
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocreate/events",
3
- "version": "1.25.0",
3
+ "version": "1.27.0",
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",
package/src/index.js CHANGED
@@ -40,8 +40,10 @@ const CoCreateEvents = {
40
40
  this.initPrefix("selected", ["click"]);
41
41
  this.initPrefix("onload", ["onload"]);
42
42
  this.initPrefix("observe", ["observer"]);
43
+ this.initPrefix("intersection", ["intersection"]);
43
44
  this.initPrefix("resize", ["onload", "resize"]);
44
- this.initPrefix("localstorage", ["onload", "observer"]);
45
+ this.initPrefix("localstorage", ["onload"]);
46
+ this.initPrefix("focus", ["focus"]);
45
47
  }
46
48
 
47
49
  let customEventEls = document.querySelectorAll("[event-name]");
@@ -61,7 +63,7 @@ const CoCreateEvents = {
61
63
  const self = this;
62
64
  observer.init({
63
65
  name: "CoCreateEventName",
64
- observe: ["addedNodes"],
66
+ types: ["addedNodes"],
65
67
  selector: `[event-name]`,
66
68
  callback: function (mutation) {
67
69
  let name = mutation.target.getAttribute("event-name");
@@ -87,38 +89,33 @@ const CoCreateEvents = {
87
89
  }
88
90
  });
89
91
 
90
- let selector = `[${prefix}], [${prefix}-key], [${prefix}-attribute], [${prefix}-value], [${prefix}-action], [${prefix}-selector], [${prefix}-closest], [${prefix}-parent], [${prefix}-next], [${prefix}-previous]`;
92
+ let selector = `[${prefix}], [${prefix}-key], [${prefix}-attribute], [${prefix}-value], [${prefix}-action], [${prefix}-selector], [${prefix}-closest], [${prefix}-parent], [${prefix}-next], [${prefix}-previous], [${prefix}-preventDefault], [${prefix}-stopPropagation], [${prefix}-stopImmediatePropagation]`;
91
93
 
92
94
  observer.init({
93
95
  name: "CoCreateEventattributes",
94
- observe: ["attributes", "addedNodes"],
95
- attributeName: [`${prefix}-events`],
96
- selector: selector,
96
+ types: ["attributes", "addedNodes"],
97
+ attributeFilter: [
98
+ `${prefix}-key`,
99
+ `${prefix}-value`,
100
+ `${prefix}-selector`,
101
+ `${prefix}-closest`,
102
+ `${prefix}-parent`,
103
+ `${prefix}-next`,
104
+ `${prefix}-previous`,
105
+ `${prefix}-events`,
106
+ `${prefix}-bubbles`,
107
+ `${prefix}-cancelable`,
108
+ `${prefix}-composed`,
109
+ `${prefix}-preventDefault`,
110
+ `${prefix}-stopPropagation`,
111
+ `${prefix}-stopImmediatePropagation`
112
+ ],
113
+ selector,
97
114
  callback: function (mutation) {
98
115
  self.initElements([mutation.target], prefix, events);
99
116
  }
100
117
  });
101
118
 
102
- if (events && events.includes("observer")) {
103
- observer.init({
104
- name: "observerAttributes",
105
- observe: ["attributes"],
106
- attributeName: [
107
- `${prefix}-key`,
108
- `${prefix}-value`,
109
- `${prefix}-selector`,
110
- `${prefix}-closest`,
111
- `${prefix}-parent`,
112
- `${prefix}-next`,
113
- `${prefix}-previous`
114
- ],
115
- callback: function (mutation) {
116
- // remove previous observer
117
- self.initElements([mutation.target], prefix, events);
118
- }
119
- });
120
- }
121
-
122
119
  let elements = document.querySelectorAll(selector);
123
120
  this.initElements(elements, prefix, events);
124
121
  },
@@ -134,7 +131,9 @@ const CoCreateEvents = {
134
131
  this.elements.set(el, prefixes);
135
132
  } else if (!prefixes[prefix]) {
136
133
  prefixes[prefix] = { events };
137
- } else isEventable = false;
134
+ } else {
135
+ isEventable = false;
136
+ }
138
137
 
139
138
  let customEvents = el.getAttribute(`${prefix}-events`);
140
139
  if (customEvents) {
@@ -149,8 +148,25 @@ const CoCreateEvents = {
149
148
  prefixes[prefix].events = events;
150
149
  isEventable = true;
151
150
  }
151
+
152
152
  if (!events || !isEventable) continue;
153
- if (events.includes("onload")) this.__updateElements(el, prefix);
153
+
154
+ // let originalSelector = ""; // i think we need to extract the selector and remove the operators
155
+ // if (originalSelector) {
156
+ // observer.init({
157
+ // types: ["addedNodes"],
158
+ // callback: function (mutation) {
159
+ // const els = el.queryElements();
160
+ // if (els && els.includes(mutation.target)) {
161
+ // self.__updateElements();
162
+ // }
163
+ // }
164
+ // });
165
+ // }
166
+
167
+ if (events.includes("onload")) {
168
+ this.__updateElements(el, prefix);
169
+ }
154
170
 
155
171
  if (events.includes("observer")) {
156
172
  let target;
@@ -172,7 +188,7 @@ const CoCreateEvents = {
172
188
  }
173
189
  }
174
190
 
175
- let attributeName = (
191
+ let attributeFilter = (
176
192
  el.getAttribute(`${prefix}-attributes`) || ""
177
193
  )
178
194
  .split(/\s*,\s*/)
@@ -185,7 +201,7 @@ const CoCreateEvents = {
185
201
  .filter((item) => item);
186
202
 
187
203
  let observerConfig = {
188
- observe: observeAttribute,
204
+ types: observeAttribute,
189
205
  callback: function (mutation) {
190
206
  self.__updateElements(el, prefix, mutation.target);
191
207
  }
@@ -195,20 +211,19 @@ const CoCreateEvents = {
195
211
  if (
196
212
  target &&
197
213
  !observeAttribute.length &&
198
- !attributeName.length
214
+ !attributeFilter.length
199
215
  ) {
200
- observerConfig.observe.push("addedNodes");
216
+ observerConfig.types.push("addedNodes");
201
217
  }
202
218
  observerConfig.target = target;
203
219
  }
204
220
 
205
- if (attributeName.length) {
206
- observerConfig.observe.push("attributes");
207
- observerConfig.attributeName = attributeName;
221
+ if (attributeFilter.length) {
222
+ observerConfig.types.push("attributes");
223
+ observerConfig.attributeFilter = attributeFilter;
208
224
  }
209
225
 
210
- if (observerConfig.observe.length)
211
- observer.init(observerConfig);
226
+ if (observerConfig.types.length) observer.init(observerConfig);
212
227
  }
213
228
 
214
229
  if (events.includes("resize")) {
@@ -221,19 +236,98 @@ const CoCreateEvents = {
221
236
  resizeObserver.observe(el);
222
237
  }
223
238
 
239
+ if (events.includes("intersection")) {
240
+ const rootSelector = el.getAttribute(`${prefix}-root`);
241
+ const rootMargin =
242
+ el.getAttribute(`${prefix}-root-margin`) || "0px";
243
+ const threshold = el.getAttribute(`${prefix}-threshold`)
244
+ ? JSON.parse(el.getAttribute(`${prefix}-threshold`))
245
+ : [0];
246
+ const trigger = el.getAttribute(`${prefix}-trigger`) || "both"; // Default to "both"
247
+
248
+ // Resolve root from the selector or use null for viewport
249
+ const root = rootSelector
250
+ ? document.querySelector(rootSelector)
251
+ : null;
252
+
253
+ // Validate and filter threshold values
254
+ const validThreshold = Array.isArray(threshold)
255
+ ? threshold.filter((value) => value >= 0 && value <= 1)
256
+ : [0];
257
+
258
+ // Create IntersectionObserver
259
+ const intersectionObserver = new IntersectionObserver(
260
+ (entries) => {
261
+ entries.forEach((entry) => {
262
+ const isVisible = entry.isIntersecting;
263
+
264
+ // Determine if __updateElements should be called based on trigger
265
+ // if (
266
+ // (trigger === "visible" && isVisible) ||
267
+ // (trigger === "not-visible" && !isVisible) ||
268
+ // trigger === "both"
269
+ // ) {
270
+ if (isVisible) {
271
+ // Set an attribute to reflect visibility state
272
+ el.setAttribute(
273
+ `${prefix}-is-visible`,
274
+ isVisible ? "true" : "false"
275
+ );
276
+
277
+ // Call __updateElements
278
+ self.__updateElements(
279
+ el,
280
+ prefix
281
+ // {
282
+ // isVisible,
283
+ // intersectionRatio:
284
+ // entry.intersectionRatio
285
+ // }
286
+ );
287
+ }
288
+ });
289
+ },
290
+ { root, rootMargin, threshold: validThreshold }
291
+ );
292
+
293
+ intersectionObserver.observe(el);
294
+ }
295
+
224
296
  for (let i = 0; i < events.length; i++) {
297
+ // Skip specific lifecycle events
225
298
  if (
226
- events[i] !== "onload" &&
227
- events[i] !== "observer" &&
228
- events[i] !== "resize"
299
+ events[i] === "onload" ||
300
+ events[i] === "observer" ||
301
+ events[i] === "intersection" ||
302
+ events[i] === "resize"
229
303
  ) {
230
- el.removeEventListener(events[i], eventFunction);
231
- el.addEventListener(events[i], eventFunction);
304
+ continue;
305
+ }
306
+
307
+ // Remove any existing event listener
308
+ el.removeEventListener(events[i], eventFunction);
309
+
310
+ // Initialize an empty options object
311
+ const options = {};
312
+
313
+ // Attributes to check dynamically
314
+ const attributes = ["bubbles", "cancelable", "composed"];
315
+
316
+ for (let j = 0; j < attributes.length; j++) {
317
+ const attrValue = el.getAttribute(
318
+ `${events[i]}-${attributes[j]}`
319
+ );
320
+ if (attrValue !== null) {
321
+ options[attributes[j]] = attrValue !== "false";
322
+ }
232
323
  }
324
+
325
+ // Add the new event listener with the dynamically constructed options
326
+ el.addEventListener(events[i], eventFunction, options);
233
327
  }
234
328
 
235
329
  function eventFunction(event) {
236
- // TODO: apply debounce
330
+ // TODO: apply debounce.
237
331
  // let debounce;
238
332
  // clearTimeout(debounce);
239
333
  // debounce = setTimeout(function() {
@@ -251,6 +345,25 @@ const CoCreateEvents = {
251
345
  if (attribute.includes(prefix)) return;
252
346
  // if (target.closest(`[actions*="${prefix}"]`))
253
347
  // return;
348
+
349
+ // Define the lifecycle attributes (matching JavaScript event methods)
350
+ const lifecycleAttributes = [
351
+ "preventDefault",
352
+ "stopPropagation",
353
+ "stopImmediatePropagation"
354
+ ];
355
+
356
+ // Handle lifecycle attributes dynamically
357
+ for (let i = 0; i < lifecycleAttributes.length; i++) {
358
+ const attr = `${prefix}-${lifecycleAttributes[i]}`; // Add prefix dynamically
359
+ const attrValue = target.getAttribute(attr); // Directly get the attribute value
360
+
361
+ // If attribute exists and is not explicitly "false", apply the corresponding event method
362
+ if (attrValue !== null && attrValue !== "false") {
363
+ event[lifecycleAttributes[i]]();
364
+ }
365
+ }
366
+
254
367
  self.__updateElements(
255
368
  target,
256
369
  prefix,
@@ -292,7 +405,26 @@ const CoCreateEvents = {
292
405
  }
293
406
 
294
407
  for (let element of elements) {
295
- let delay = element.getAttribute(`${prefix}-delay`);
408
+ // const requestAnimationFrameAttr = element.getAttribute(
409
+ // `${prefix}-requestAnimationFrame`
410
+ // );
411
+
412
+ // if (
413
+ // requestAnimationFrameAttr !== null &&
414
+ // requestAnimationFrameAttr !== "false"
415
+ // ) {
416
+ // // Double requestAnimationFrame
417
+ // await new Promise((resolve) => {
418
+ // requestAnimationFrame(() => {
419
+ // requestAnimationFrame(() => {
420
+ // resolve();
421
+ // });
422
+ // });
423
+ // });
424
+ // }
425
+
426
+ // TODO: Handle setInterval logic
427
+ let delay = element.getAttribute(`${prefix}-setTimeout`);
296
428
 
297
429
  if (delay) {
298
430
  delay = parseInt(delay, 10) || 0;
@@ -382,13 +514,15 @@ const CoCreateEvents = {
382
514
  let elseCondition = element.getAttribute(`${prefix}-else`);
383
515
 
384
516
  let ifValue = element.getAttribute(`${prefix}-if-value`);
385
- if (!ifValue && ifValue !== "")
386
- ifValue = (await element.getValue()) || values;
387
- //values // await element.getValue()
388
- else if (ifValue || ifValue === "") ifValue = [ifValue];
389
- else ifValue = values;
390
-
391
- if (!Array.isArray(ifValue)) ifValue = [ifValue];
517
+ if (ifCondition) {
518
+ if (!ifValue && ifValue !== "")
519
+ ifValue = (await element.getValue()) || values;
520
+ //values // await element.getValue()
521
+ else if (ifValue || ifValue === "") ifValue = [ifValue];
522
+ else ifValue = values;
523
+
524
+ if (!Array.isArray(ifValue)) ifValue = [ifValue];
525
+ }
392
526
 
393
527
  //TODO: improved resize toggling of values
394
528
  // let hasCondition = this.elements2.get(element)
@@ -436,7 +570,11 @@ const CoCreateEvents = {
436
570
  prefix,
437
571
  selector: params
438
572
  });
439
- if (targetElements === false) targetElements = [element];
573
+
574
+ if (targetElements === false) {
575
+ targetElements = [element];
576
+ }
577
+
440
578
  let action = element.getAttribute(`${prefix}-action`);
441
579
  for (let i = 0; i < targetElements.length; i++) {
442
580
  if (action) {
@@ -471,7 +609,7 @@ const CoCreateEvents = {
471
609
  }
472
610
  }
473
611
 
474
- document.dispatchEvent(
612
+ el.dispatchEvent(
475
613
  new CustomEvent(`${prefix}End`, {
476
614
  detail: {}
477
615
  })
@@ -516,7 +654,7 @@ const CoCreateEvents = {
516
654
  // let value = values[0]
517
655
  replaceKey(prefix, element, key, values);
518
656
  } else {
519
- // TODo: if html in iframe
657
+ // TODO: if html in iframe
520
658
  let domTextEditor;
521
659
  if (element.parentElement) {
522
660
  domTextEditor =
@@ -579,7 +717,7 @@ const CoCreateEvents = {
579
717
  element.classList.add(newValue);
580
718
  }
581
719
  } else if (attrName === "value") {
582
- element.setValue(newValue);
720
+ element.setValue(newValue, false);
583
721
  } else if (
584
722
  ["$click", "$focus", "$blur", "$save", "$read"].includes(
585
723
  attrName
@@ -822,6 +960,160 @@ function parseNumberCondition(condition) {
822
960
  : condition;
823
961
  }
824
962
 
963
+ // Helper function for delay using Promises
964
+ const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
965
+
966
+ /**
967
+ * Waits for a specific element property to meet a readiness condition,
968
+ * based on retry attributes defined on the element.
969
+ * Incorporates optional exponential backoff for retry delays.
970
+ * Returns a Promise that resolves to true if the condition is met within
971
+ * the allowed attempts, and false otherwise.
972
+ *
973
+ * @param {HTMLElement} element The element with the onload-retry-* attributes.
974
+ * @returns {Promise<boolean>} A Promise resolving to true if ready, false if timed out.
975
+ */
976
+ async function checkElementReadinessWithBackoff(element) {
977
+ const propertyName = element.getAttribute("onload-retry-property");
978
+
979
+ if (!propertyName) {
980
+ return true; // No retry needed
981
+ }
982
+
983
+ // --- Retry Parameters ---
984
+ const maxAttempts =
985
+ parseInt(element.getAttribute("onload-retry-attempts") || "3", 10) || 3;
986
+ // Initial delay (used for the first wait and as the base for backoff)
987
+ const initialDelayMs =
988
+ parseInt(element.getAttribute("onload-retry-delay") || "100", 10) ||
989
+ 100;
990
+ // Exponential backoff factor (e.g., 2 for doubling). <= 1 means fixed delay.
991
+ // Default to 1 (no backoff) if missing or invalid.
992
+ const backoffFactor =
993
+ parseFloat(
994
+ element.getAttribute("onload-retry-backoff-factor") || "1"
995
+ ) || 1;
996
+ // Optional maximum delay cap. Use Infinity if missing, invalid, or <= 0.
997
+ const maxDelayMs = parseInt(
998
+ element.getAttribute("onload-retry-max-delay") || "0",
999
+ 10
1000
+ );
1001
+ const effectiveMaxDelay =
1002
+ maxDelayMs && maxDelayMs > 0 ? maxDelayMs : Infinity;
1003
+
1004
+ console.log(
1005
+ `Checking readiness for property "${propertyName}" on element ${
1006
+ element.id || ""
1007
+ }. Max attempts: ${maxAttempts}, Initial Delay: ${initialDelayMs}ms, Backoff Factor: ${backoffFactor}, Max Delay: ${
1008
+ effectiveMaxDelay === Infinity ? "None" : effectiveMaxDelay + "ms"
1009
+ }.`
1010
+ );
1011
+
1012
+ // Initialize the delay to be used for the *first* wait.
1013
+ let currentDelay = initialDelayMs;
1014
+
1015
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
1016
+ console.log(
1017
+ `Attempt ${attempt}/${maxAttempts}: Checking "${propertyName}"...`
1018
+ );
1019
+ try {
1020
+ const propertyValue = element[propertyName];
1021
+ // Example check: Adjust condition as needed
1022
+ if (propertyValue !== undefined && propertyValue > 0) {
1023
+ console.log(
1024
+ `Property "${propertyName}" ready on attempt ${attempt}. Value: ${propertyValue}`
1025
+ );
1026
+ return true; // Condition met
1027
+ }
1028
+ } catch (error) {
1029
+ console.error(
1030
+ `Error accessing property "${propertyName}" on attempt ${attempt}:`,
1031
+ error
1032
+ );
1033
+ }
1034
+
1035
+ // If condition not met and more attempts remain:
1036
+ if (attempt < maxAttempts) {
1037
+ // Determine the actual delay for this wait, applying the cap
1038
+ const delayForThisWait = Math.min(currentDelay, effectiveMaxDelay);
1039
+
1040
+ console.log(
1041
+ `Waiting ${Math.round(
1042
+ delayForThisWait
1043
+ )}ms before next attempt...`
1044
+ );
1045
+ await delay(delayForThisWait);
1046
+
1047
+ // Calculate the delay for the *next* potential wait, only if backoff is active
1048
+ if (backoffFactor > 1) {
1049
+ currentDelay = currentDelay * backoffFactor;
1050
+ // Note: We apply the cap just before the actual delay,
1051
+ // but the base 'currentDelay' keeps growing exponentially.
1052
+ // Alternatively, you could cap 'currentDelay' itself here:
1053
+ // currentDelay = Math.min(currentDelay * backoffFactor, effectiveMaxDelay);
1054
+ // Let's stick to capping just before delay() for now.
1055
+ }
1056
+ // If backoffFactor <= 1, currentDelay remains initialDelayMs (implicitly handled by initialization)
1057
+ }
1058
+ }
1059
+
1060
+ // If loop finishes without condition being met
1061
+ console.warn(
1062
+ `Readiness check failed for property "${propertyName}" on element ${
1063
+ element.id || ""
1064
+ } after ${maxAttempts} attempts.`
1065
+ );
1066
+ return false; // Timed out
1067
+ }
1068
+
1069
+ // --- Example Usage (calling code doesn't change) ---
1070
+
1071
+ async function handleElementProcessingWithBackoff(element) {
1072
+ // Call the function that now supports backoff
1073
+ const isReady = await checkElementReadinessWithBackoff(element);
1074
+
1075
+ if (isReady) {
1076
+ console.log(
1077
+ `Element ${element.id || ""} is ready. Proceeding with actions.`
1078
+ );
1079
+ // --- Execute subsequent actions ---
1080
+ if (element.hasAttribute("onload-attribute")) {
1081
+ /* ... */
1082
+ }
1083
+ if (element.hasAttribute("onload-next")) {
1084
+ /* ... */
1085
+ }
1086
+ } else {
1087
+ console.warn(
1088
+ `Skipping actions for element ${
1089
+ element.id || ""
1090
+ } because readiness check failed.`
1091
+ );
1092
+ }
1093
+ }
1094
+
1095
+ // --- Example Elements ---
1096
+
1097
+ // With Backoff (Delays: 100, 200, 400, 800 - capped)
1098
+ // <span id="elem1" onload-retry-property="scrollWidth"
1099
+ // onload-retry-attempts="5" onload-retry-delay="100"
1100
+ // onload-retry-backoff-factor="2" onload-retry-max-delay="500">
1101
+ // Content 1
1102
+ // </span>
1103
+ // Waits would be: 100ms, 200ms, 400ms, 500ms (capped)
1104
+
1105
+ // No Backoff (factor missing/invalid/<=1)
1106
+ // <span id="elem2" onload-retry-property="clientHeight"
1107
+ // onload-retry-attempts="4" onload-retry-delay="300">
1108
+ // Content 2
1109
+ // </span>
1110
+ // Waits would be: 300ms, 300ms, 300ms
1111
+
1112
+ // const element1 = document.getElementById('elem1');
1113
+ // if (element1) handleElementProcessingWithBackoff(element1);
1114
+ // const element2 = document.getElementById('elem2');
1115
+ // if (element2) handleElementProcessingWithBackoff(element2);
1116
+
825
1117
  CoCreateEvents.init();
826
1118
 
827
1119
  export default CoCreateEvents;