powerpagestoolkit 2.7.121 → 2.7.131

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.
@@ -0,0 +1,1368 @@
1
+ "use strict";
2
+ var powerpagestoolkit = (() => {
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var src_exports = {};
23
+ __export(src_exports, {
24
+ API: () => API_default,
25
+ bindForm: () => bindForm,
26
+ createRef: () => createDOMNodeReference,
27
+ waitFor: () => waitFor
28
+ });
29
+
30
+ // src/utils/safeAjax.ts
31
+ function safeAjax(ajaxOptions) {
32
+ const deferredAjax = $.Deferred();
33
+ shell.getTokenDeferred().done(function(token) {
34
+ if (!ajaxOptions.headers) {
35
+ $.extend(ajaxOptions, {
36
+ headers: {
37
+ __RequestVerificationToken: token
38
+ }
39
+ });
40
+ } else {
41
+ ajaxOptions.headers["__RequestVerificationToken"] = token;
42
+ }
43
+ $.ajax(ajaxOptions).done(function(data, textStatus, jqXHR) {
44
+ validateLoginSession(data, textStatus, jqXHR, deferredAjax.resolve);
45
+ }).fail(deferredAjax.reject);
46
+ }).fail(function() {
47
+ deferredAjax.rejectWith(this, arguments);
48
+ });
49
+ return deferredAjax.promise();
50
+ }
51
+
52
+ // src/core/API.ts
53
+ var API = {
54
+ /**
55
+ * @param tableSetName The dataverse set name for the table that you are updating a record in
56
+ * @param data The JSON of the fields and data that are to be updated on the targeted record
57
+ * @returns a Promise resolving the successful results *[record id]* of the POST request, or rejecting the failed results *[error]* of the POST request.
58
+ */
59
+ createRecord(tableSetName, data) {
60
+ return new Promise((resolve, reject) => {
61
+ safeAjax({
62
+ type: "POST",
63
+ url: `/_api/${tableSetName}`,
64
+ data: JSON.stringify(data),
65
+ contentType: "application/json",
66
+ success: function(response, status, xhr) {
67
+ resolve(xhr.getResponseHeader("entityid"));
68
+ },
69
+ error: (error) => {
70
+ reject(error);
71
+ }
72
+ });
73
+ });
74
+ },
75
+ /**
76
+ *
77
+ * @param tableSetName The DataVerse SET name of the table being queried
78
+ * @param recordID the GUID of the records to be retrieved
79
+ * @param selectColumns *OPTIONAL* if desired, enter your own custom OData query for advanced GET results. Format = select=column1,column2,column3...
80
+ * @returns a Promise resolving the successful results of the GET request, or rejecting the failed results of the GET request
81
+ */
82
+ getRecord(tableSetName, recordID, selectColumns) {
83
+ return new Promise((resolve, reject) => {
84
+ const url = `/_api/${tableSetName}(${recordID})${selectColumns ? `?$${selectColumns}` : ""}`;
85
+ safeAjax({
86
+ type: "GET",
87
+ url,
88
+ success: resolve,
89
+ error: reject
90
+ });
91
+ });
92
+ },
93
+ /**
94
+ *
95
+ * @param tableSetName The dataverse set name of the table being queried
96
+ * @param queryParameters *OPTIONAL* the OData query parameters for refining search results: *format = $filter=filters&$select=columns*
97
+ * @returns a Promise resolving the successful results of the GET request, or rejecting the failed results of the GET request
98
+ */
99
+ getMultiple(tableSetName, queryParameters) {
100
+ return new Promise((resolve, reject) => {
101
+ const url = `/_api/${tableSetName}${queryParameters ? `?${queryParameters}` : ""}`;
102
+ safeAjax({
103
+ type: "GET",
104
+ url,
105
+ success: function(response) {
106
+ resolve(response.value);
107
+ },
108
+ error: reject
109
+ });
110
+ });
111
+ },
112
+ /**
113
+ *
114
+ * @param tableSetName The dataverse set name for the table that you are updating a record in
115
+ * @param recordId The GUID of the record that is being updated
116
+ * @param data The JSON of the fields and data that are to be updated on the targeted record
117
+ * @returns A Promise with the results of the API execution
118
+ */
119
+ updateRecord(tableSetName, recordId, data) {
120
+ return new Promise((resolve, reject) => {
121
+ const url = `/_api/${tableSetName}(${recordId})`;
122
+ safeAjax({
123
+ type: "PATCH",
124
+ url,
125
+ data: JSON.stringify(data),
126
+ success: resolve,
127
+ error: reject
128
+ });
129
+ });
130
+ }
131
+ };
132
+ var API_default = API;
133
+
134
+ // src/core/waitFor.ts
135
+ function waitFor(target, root = document, multiple = false, debounceTime2) {
136
+ return new Promise((resolve, reject) => {
137
+ if (multiple) {
138
+ let timeoutMs;
139
+ const observedElements = [];
140
+ const observedSet = /* @__PURE__ */ new Set();
141
+ if (debounceTime2 < 1) {
142
+ return resolve(
143
+ Array.from(root.querySelectorAll(target))
144
+ );
145
+ }
146
+ const observer = new MutationObserver(() => {
147
+ const found = Array.from(root.querySelectorAll(target));
148
+ found.forEach((element) => {
149
+ if (!observedSet.has(element)) {
150
+ observedSet.add(element);
151
+ observedElements.push(element);
152
+ }
153
+ });
154
+ clearTimeout(timeoutMs);
155
+ timeoutMs = setTimeout(() => {
156
+ if (observedElements.length > 0) {
157
+ observer.disconnect();
158
+ resolve(observedElements);
159
+ } else {
160
+ reject(
161
+ new Error(
162
+ `No elements found with target: "${target}" within ${debounceTime2 / 1e3} seconds. If the element you are expecting has not loaded yet, consider raising your timeout.`
163
+ )
164
+ );
165
+ }
166
+ }, debounceTime2);
167
+ });
168
+ observer.observe(root, {
169
+ childList: true,
170
+ subtree: true,
171
+ attributes: false
172
+ });
173
+ } else {
174
+ const observer = new MutationObserver(() => {
175
+ const observedElement = root.querySelector(target);
176
+ if (observedElement) {
177
+ clearTimeout(timeout);
178
+ observer.disconnect();
179
+ resolve(observedElement);
180
+ }
181
+ });
182
+ const timeout = setTimeout(() => {
183
+ observer.disconnect();
184
+ reject(
185
+ new Error(
186
+ `Element not found by target: "${target}" within ${debounceTime2 / 1e3} second. If the element you are expecting has not loaded yet, consider raising your timeout.`
187
+ )
188
+ );
189
+ }, debounceTime2);
190
+ if (target instanceof HTMLElement) {
191
+ clearTimeout(timeout);
192
+ return resolve(target);
193
+ }
194
+ const element = root.querySelector(target);
195
+ if (element) {
196
+ clearTimeout(timeout);
197
+ return resolve(element);
198
+ }
199
+ observer.observe(root, {
200
+ subtree: true,
201
+ attributes: true,
202
+ childList: true
203
+ // Detects added/removed child elements
204
+ });
205
+ }
206
+ });
207
+ }
208
+
209
+ // src/utils/createInfoElement.ts
210
+ function CreateInfoEl(titleString, iconStyle) {
211
+ if (typeof titleString !== "string") {
212
+ throw new Error(
213
+ `argument "titleString" must be of type "string". Received: "${typeof titleString}"`
214
+ );
215
+ }
216
+ if (iconStyle && typeof iconStyle !== "object") {
217
+ throw new Error(
218
+ `argument "iconStyle" must be of type "object". Received: "${typeof iconStyle}"`
219
+ );
220
+ }
221
+ const span = document.createElement("span");
222
+ span.classList.add("info-icon");
223
+ const icon = document.createElement("i");
224
+ icon.classList.add("fa", "fa-solid", "fa-info-circle");
225
+ icon.setAttribute("aria-label", "Info");
226
+ icon.style.cursor = "pointer";
227
+ const flyoutContent = document.createElement("div");
228
+ flyoutContent.innerHTML = titleString;
229
+ flyoutContent.classList.add("flyout-content");
230
+ span.appendChild(icon);
231
+ span.appendChild(flyoutContent);
232
+ if (iconStyle) {
233
+ Object.assign(icon.style, iconStyle);
234
+ }
235
+ const positionFlyout = () => {
236
+ flyoutContent.style.display = "block";
237
+ const flyoutRect = flyoutContent.getBoundingClientRect();
238
+ const viewportWidth = window.innerWidth;
239
+ if (flyoutRect.right > viewportWidth) {
240
+ const overflowAmount = flyoutRect.right - viewportWidth;
241
+ flyoutContent.style.left = `calc(50% - ${overflowAmount}px)`;
242
+ }
243
+ if (flyoutRect.left < 0) {
244
+ const overflowAmount = Math.abs(flyoutRect.left);
245
+ flyoutContent.style.left = `calc(50% + ${overflowAmount}px)`;
246
+ }
247
+ };
248
+ span.addEventListener("mouseenter", () => {
249
+ positionFlyout();
250
+ });
251
+ span.addEventListener("mouseleave", (event) => {
252
+ const relatedTarget = event.relatedTarget;
253
+ if (!span.contains(relatedTarget)) {
254
+ flyoutContent.style.display = "none";
255
+ }
256
+ });
257
+ icon.addEventListener("touchstart", (event) => {
258
+ event.preventDefault();
259
+ flyoutContent.style.display = flyoutContent.style.display === "block" ? "none" : "block";
260
+ if (flyoutContent.style.display === "block") {
261
+ positionFlyout();
262
+ }
263
+ });
264
+ document.body.addEventListener("click", (event) => {
265
+ if (!span.contains(event.target)) {
266
+ flyoutContent.style.display = "none";
267
+ }
268
+ });
269
+ flyoutContent.style.display = "none";
270
+ return span;
271
+ }
272
+
273
+ // src/constants/symbols.ts
274
+ var init = Symbol("__I");
275
+ var destroy = Symbol("__D");
276
+ var valueSync = Symbol("__VS");
277
+ var dateSync = Symbol("__DS");
278
+ var getElementValue = Symbol("__GEV");
279
+ var attachVisibilityController = Symbol("__AVC");
280
+ var attachRadioButtons = Symbol("__ARB");
281
+ var bindMethods = Symbol("__B");
282
+ var debounceTime = Symbol("__DT");
283
+ var observers = Symbol("__O");
284
+ var boundEventListeners = Symbol("__BEV");
285
+ var isValidFormElement = Symbol("__VFE");
286
+ var registerEventListener = Symbol("__REV");
287
+
288
+ // src/errors/errors.ts
289
+ var DOMNodeInitializationError = class extends Error {
290
+ constructor(instance, error) {
291
+ super(
292
+ `There was an error initializing a DOMNodeReference for target: ${instance.target}, :: ${error}`
293
+ );
294
+ this.name = "DOMNodeInitializationError";
295
+ }
296
+ };
297
+ var DOMNodeNotFoundError = class extends Error {
298
+ constructor(instance) {
299
+ super(`The targeted DOM element was not found: ${instance.target}`);
300
+ }
301
+ };
302
+ var ConditionalRenderingError = class extends Error {
303
+ constructor(instance, error) {
304
+ super(
305
+ `There was an error condiguring conditional rendering for target: ${instance.target} :: ${error}`
306
+ );
307
+ }
308
+ };
309
+ var ValidationConfigError = class extends Error {
310
+ constructor(node, message) {
311
+ super(`Validation configuration error for ${node.target}: ${message}`);
312
+ this.name = "ValidationConfigError";
313
+ }
314
+ };
315
+
316
+ // src/core/DOMNodeReference.ts
317
+ var EventTypes = {
318
+ CHECKBOX: "click",
319
+ RADIO: "click",
320
+ SELECT: "change",
321
+ TEXTAREA: "keyup",
322
+ DEFAULT: "input"
323
+ };
324
+ var DOMNodeReference = class _DOMNodeReference {
325
+ // properties initialized in the constructor
326
+ target;
327
+ logicalName;
328
+ root;
329
+ [debounceTime];
330
+ isLoaded;
331
+ defaultDisplay;
332
+ [observers] = [];
333
+ [boundEventListeners] = [];
334
+ isRadio = false;
335
+ radioType = null;
336
+ /**
337
+ * The value of the element that this node represents
338
+ * stays in syncs with the live DOM elements?.,m via event handler
339
+ */
340
+ value;
341
+ /**
342
+ * Creates an instance of DOMNodeReference.
343
+ * @param target - The CSS selector to find the desired DOM element.
344
+ * @param root - Optionally specify the element within to search for the element targeted by 'target'
345
+ * Defaults to 'document.body'
346
+ */
347
+ /******/
348
+ /******/
349
+ constructor(target, root = document.body, debounceTime2) {
350
+ this.target = target;
351
+ this.logicalName = this.extractLogicalName(target);
352
+ this.root = root;
353
+ this[debounceTime] = debounceTime2;
354
+ this.isLoaded = false;
355
+ this.defaultDisplay = "";
356
+ this.value = null;
357
+ this[bindMethods]();
358
+ }
359
+ extractLogicalName(target) {
360
+ if (typeof target !== "string") return "";
361
+ const bracketMatch = target.match(/\[([^\]]+)\]/);
362
+ if (!bracketMatch) return target.replace(/[#\[\]]/g, "");
363
+ const content = bracketMatch[1];
364
+ const quoteMatch = content.match(/["']([^"']+)["']/);
365
+ return (quoteMatch?.[1] || content).replace(/[#\[\]]/g, "");
366
+ }
367
+ async [init]() {
368
+ try {
369
+ if (this.target instanceof HTMLElement) {
370
+ this.element = this.target;
371
+ } else {
372
+ this.element = await waitFor(
373
+ this.target,
374
+ this.root,
375
+ false,
376
+ this[debounceTime]
377
+ );
378
+ }
379
+ if (!this.element) {
380
+ throw new DOMNodeNotFoundError(this);
381
+ }
382
+ if (this.element.id && this.element.querySelectorAll(
383
+ `#${this.element.id} > input[type="radio"]`
384
+ ).length > 0) {
385
+ await this[attachRadioButtons]();
386
+ }
387
+ this[valueSync]();
388
+ this[attachVisibilityController]();
389
+ this.defaultDisplay = this.visibilityController.style.display;
390
+ const observer = new MutationObserver((mutations) => {
391
+ for (const mutation of mutations) {
392
+ if (Array.from(mutation.removedNodes).includes(this.element)) {
393
+ this[destroy]();
394
+ observer.disconnect();
395
+ break;
396
+ }
397
+ }
398
+ });
399
+ observer.observe(document.body, {
400
+ childList: true,
401
+ subtree: true
402
+ });
403
+ this.isLoaded = true;
404
+ } catch (error) {
405
+ const errorMessage = error instanceof Error ? error.message : String(error);
406
+ throw new DOMNodeInitializationError(this, errorMessage);
407
+ }
408
+ }
409
+ /**
410
+ * Initializes value synchronization with appropriate event listeners
411
+ * based on element type.
412
+ */
413
+ [valueSync]() {
414
+ if (!this[isValidFormElement](this.element)) return;
415
+ this.updateValue();
416
+ const eventType = this.determineEventType();
417
+ this[registerEventListener](this.element, eventType, this.updateValue);
418
+ if (this.isDateInput()) {
419
+ this[dateSync](this.element);
420
+ }
421
+ }
422
+ determineEventType() {
423
+ if (this.element instanceof HTMLSelectElement) return "change";
424
+ if (!(this.element instanceof HTMLInputElement)) return EventTypes.DEFAULT;
425
+ return EventTypes[this.element.type.toUpperCase()] || EventTypes.DEFAULT;
426
+ }
427
+ isDateInput() {
428
+ return this.element instanceof HTMLInputElement && this.element.dataset.type === "date";
429
+ }
430
+ [isValidFormElement](element) {
431
+ return element instanceof HTMLInputElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSpanElement || element instanceof HTMLButtonElement || element instanceof HTMLFieldSetElement;
432
+ }
433
+ [registerEventListener](element, eventType, handler) {
434
+ element.addEventListener(eventType, handler);
435
+ this[boundEventListeners].push({
436
+ element,
437
+ handler,
438
+ event: eventType
439
+ });
440
+ }
441
+ async [dateSync](element) {
442
+ const parentElement = element.parentElement;
443
+ if (!parentElement) {
444
+ throw new Error("Date input must have a parent element");
445
+ }
446
+ const dateNode = await waitFor(
447
+ "[data-date-format]",
448
+ parentElement,
449
+ false,
450
+ 1500
451
+ );
452
+ this[registerEventListener](dateNode, "select", this.updateValue);
453
+ }
454
+ /**
455
+ * Gets the current value of the element based on its type
456
+ * @protected
457
+ * @returns Object containing value and optional checked state
458
+ */
459
+ [getElementValue]() {
460
+ const input = this.element;
461
+ const select = this.element;
462
+ if (this.yesRadio instanceof _DOMNodeReference && this.noRadio instanceof _DOMNodeReference) {
463
+ return {
464
+ value: this.yesRadio.checked,
465
+ checked: this.yesRadio.checked
466
+ };
467
+ }
468
+ switch (input.type) {
469
+ case "checkbox":
470
+ case "radio":
471
+ return {
472
+ value: input.checked,
473
+ checked: input.checked
474
+ };
475
+ case "select-multiple":
476
+ return {
477
+ value: Array.from(select.selectedOptions).map(
478
+ (option) => option.value
479
+ )
480
+ };
481
+ case "select-one":
482
+ return {
483
+ value: select.value
484
+ };
485
+ case "number":
486
+ return {
487
+ value: input.value !== "" ? Number(input.value) : null
488
+ };
489
+ default:
490
+ let cleanValue = input.value;
491
+ if (this.element.classList.contains("decimal") || this.element.classList.contains("money"))
492
+ cleanValue = input.value.replace(/[$,]/g, "");
493
+ return {
494
+ value: this.element.classList.contains("decimal") || this.element.classList.contains("money") ? parseFloat(cleanValue) : cleanValue
495
+ };
496
+ }
497
+ }
498
+ [attachVisibilityController]() {
499
+ this.visibilityController = this.element;
500
+ if (this.element.tagName === "TABLE") {
501
+ const fieldset = this.element.closest("fieldset");
502
+ if (fieldset) {
503
+ this.visibilityController = fieldset;
504
+ }
505
+ return;
506
+ }
507
+ const tagsRequiringTdParent = [
508
+ "SPAN",
509
+ "INPUT",
510
+ "TEXTAREA",
511
+ "SELECT",
512
+ "TABLE"
513
+ ];
514
+ if (tagsRequiringTdParent.includes(this.element.tagName)) {
515
+ const tdParent = this.element.closest("td");
516
+ if (tdParent) {
517
+ this.visibilityController = tdParent;
518
+ }
519
+ }
520
+ }
521
+ async [attachRadioButtons]() {
522
+ if (!this.element) {
523
+ console.error(
524
+ "'this.element' not found: cannot attach radio buttons for ",
525
+ this.target
526
+ );
527
+ return;
528
+ }
529
+ this.yesRadio = await createDOMNodeReference('input[type="radio"][value="1"]', {
530
+ root: this.element
531
+ });
532
+ this.yesRadio.isRadio = true;
533
+ this.yesRadio.radioType = "truthy";
534
+ this.noRadio = await createDOMNodeReference('input[type="radio"][value="0"]', {
535
+ root: this.element
536
+ });
537
+ this.noRadio.isRadio = true;
538
+ this.noRadio.radioType = "falsy";
539
+ }
540
+ [bindMethods]() {
541
+ const prototype = Object.getPrototypeOf(this);
542
+ for (const key of Object.getOwnPropertyNames(prototype)) {
543
+ const value = this[key];
544
+ if (key !== "constructor" && typeof value === "function") {
545
+ this[key] = value.bind(this);
546
+ }
547
+ }
548
+ }
549
+ [destroy]() {
550
+ this[boundEventListeners]?.forEach((binding) => {
551
+ binding.element?.removeEventListener(binding.event, binding.handler);
552
+ });
553
+ this[observers]?.forEach((observer) => {
554
+ observer.disconnect();
555
+ });
556
+ this.yesRadio?.[destroy]();
557
+ this.noRadio?.[destroy]();
558
+ this.yesRadio = null;
559
+ this.noRadio = null;
560
+ this.isLoaded = false;
561
+ this.value = null;
562
+ }
563
+ /**
564
+ * Updates the value and checked state based on element type
565
+ * @public
566
+ */
567
+ updateValue(e) {
568
+ if (e) {
569
+ e.stopPropagation();
570
+ }
571
+ if (this.yesRadio && this.noRadio) {
572
+ this.yesRadio.updateValue();
573
+ this.noRadio.updateValue();
574
+ }
575
+ const elementValue = this[getElementValue]();
576
+ this.value = elementValue.value;
577
+ if (elementValue.checked !== void 0) {
578
+ this.checked = elementValue.checked;
579
+ }
580
+ }
581
+ /**
582
+ * Sets up an event listener based on the specified event type, executing the specified
583
+ * event handler
584
+ * @param eventType - The DOM event to watch for
585
+ * @param eventHandler - The callback function that runs when the
586
+ * specified event occurs.
587
+ * @returns - Instance of this [provides option to method chain]
588
+ */
589
+ on(eventType, eventHandler) {
590
+ if (typeof eventHandler !== "function") {
591
+ throw new Error(
592
+ `Argument "eventHandler" must be a Function. Received: ${typeof eventHandler}`
593
+ );
594
+ }
595
+ this[registerEventListener](
596
+ this.element,
597
+ eventType,
598
+ eventHandler.bind(this)
599
+ );
600
+ return this;
601
+ }
602
+ /**
603
+ * Hides the element by setting its display style to "none".
604
+ * @returns - Instance of this [provides option to method chain]
605
+ */
606
+ hide() {
607
+ this.visibilityController.style.display = "none";
608
+ return this;
609
+ }
610
+ /**
611
+ * Shows the element by restoring its default display style.
612
+ * @returns - Instance of this [provides option to method chain]
613
+ */
614
+ show() {
615
+ this.visibilityController.style.display = this.defaultDisplay;
616
+ return this;
617
+ }
618
+ /**
619
+ *
620
+ * @param shouldShow - Either a function that returns true or false,
621
+ * or a natural boolean to determine the visibility of this
622
+ * @returns - Instance of this [provides option to method chain]
623
+ */
624
+ toggleVisibility(shouldShow) {
625
+ if (shouldShow instanceof Function) {
626
+ shouldShow(this) ? this.show() : this.hide();
627
+ } else {
628
+ shouldShow ? this.show() : this.hide();
629
+ }
630
+ return this;
631
+ }
632
+ /**
633
+ * Sets the value of the HTML element.
634
+ * @param value - The value to set for the HTML element.
635
+ * for parents of boolean radios, pass true or false as value, or
636
+ * an expression returning a boolean
637
+ * @returns - Instance of this [provides option to method chain]
638
+ */
639
+ setValue(value) {
640
+ if (value instanceof Function) {
641
+ value = value();
642
+ }
643
+ const eventType = this.determineEventType();
644
+ this.element.dispatchEvent(new Event(eventType, { bubbles: false }));
645
+ if (this.yesRadio instanceof _DOMNodeReference && this.noRadio instanceof _DOMNodeReference) {
646
+ this.yesRadio.element.checked = value;
647
+ this.noRadio.element.checked = !value;
648
+ this.value = value;
649
+ this.checked = value;
650
+ this.element.checked = value;
651
+ } else if (this.isRadio || this.element.type === "radio") {
652
+ this.checked = value;
653
+ this.element.checked = value;
654
+ } else {
655
+ this.element.value = value;
656
+ }
657
+ return this;
658
+ }
659
+ /**
660
+ * Disables the element so that users cannot input any data
661
+ * @returns - Instance of this [provides option to method chain]
662
+ */
663
+ disable() {
664
+ try {
665
+ this.element.disabled = true;
666
+ } catch (error) {
667
+ const errorMessage = error instanceof Error ? error.message : String(error);
668
+ throw new Error(
669
+ `There was an error trying to disable the target: ${this.target}: "${errorMessage}"`
670
+ );
671
+ }
672
+ return this;
673
+ }
674
+ /**
675
+ * Clears all values and states of the element.
676
+ * Handles different input types appropriately, and can be called
677
+ * on an element containing N child inputs to clear all
678
+ *
679
+ * @returns - Instance of this [provides option to method chain]
680
+ * @throws If clearing values fails
681
+ */
682
+ async clearValue() {
683
+ try {
684
+ const element = this.element;
685
+ if (element instanceof HTMLInputElement) {
686
+ switch (element.type.toLowerCase()) {
687
+ case "checkbox":
688
+ case "radio":
689
+ element.checked = false;
690
+ this.checked = false;
691
+ this.value = false;
692
+ break;
693
+ case "number":
694
+ element.value = "";
695
+ this.value = "";
696
+ break;
697
+ default:
698
+ element.value = "";
699
+ this.value = "";
700
+ break;
701
+ }
702
+ } else if (element instanceof HTMLSelectElement) {
703
+ if (element.multiple) {
704
+ Array.from(element.options).forEach(
705
+ (option) => option.selected = false
706
+ );
707
+ this.value = [];
708
+ } else {
709
+ element.selectedIndex = -1;
710
+ this.value = "";
711
+ }
712
+ } else if (element instanceof HTMLTextAreaElement) {
713
+ element.value = "";
714
+ this.value = "";
715
+ } else {
716
+ this.value = "";
717
+ const childInputs = Array.from(
718
+ this.element.querySelectorAll("input, select, textarea")
719
+ );
720
+ if (childInputs.length > 0) {
721
+ const promises = childInputs.map(async (input) => {
722
+ const inputRef = await createDOMNodeReference(
723
+ input
724
+ );
725
+ return inputRef.clearValue();
726
+ });
727
+ await Promise.all(promises);
728
+ }
729
+ }
730
+ if (this.yesRadio instanceof _DOMNodeReference && this.noRadio instanceof _DOMNodeReference) {
731
+ await this.yesRadio.clearValue();
732
+ await this.noRadio.clearValue();
733
+ }
734
+ const events = [
735
+ new Event("input", { bubbles: true }),
736
+ new Event("change", { bubbles: true }),
737
+ new Event("click", { bubbles: true })
738
+ ];
739
+ events.forEach((event) => this.element.dispatchEvent(event));
740
+ return this;
741
+ } catch (error) {
742
+ const errorMessage = `Failed to clear values for element with target "${this.target}": ${error instanceof Error ? error.message : String(error)}`;
743
+ throw new Error(errorMessage);
744
+ }
745
+ }
746
+ /**
747
+ * Enables the element so that users can input data
748
+ * @returns - Instance of this [provides option to method chain]
749
+ */
750
+ enable() {
751
+ try {
752
+ this.element.disabled = false;
753
+ } catch (e) {
754
+ throw new Error(
755
+ `There was an error trying to disable the target: ${this.target}`
756
+ );
757
+ }
758
+ return this;
759
+ }
760
+ /**
761
+ *
762
+ * @param elements - The elements to prepend to the element targeted by this.
763
+ * @returns - Instance of this [provides option to method chain]
764
+ */
765
+ prepend(...elements) {
766
+ this.element.prepend(...elements);
767
+ return this;
768
+ }
769
+ /**
770
+ * Appends child elements to the HTML element.
771
+ * @param elements - The elements to append to the element targeted by this.
772
+ * @returns - Instance of this [provides option to method chain]
773
+ */
774
+ append(...elements) {
775
+ this.element.append(...elements);
776
+ return this;
777
+ }
778
+ /**
779
+ * Inserts elements before the HTML element.
780
+ * @param elements - The elements to insert before the HTML element.
781
+ * @returns - Instance of this [provides option to method chain]
782
+ */
783
+ before(...elements) {
784
+ this.element.before(...elements);
785
+ return this;
786
+ }
787
+ /**
788
+ * Inserts elements after the HTML element.
789
+ * @param elements - The elements to insert after the HTML element.
790
+ * @returns - Instance of this [provides option to method chain]
791
+ */
792
+ after(...elements) {
793
+ this.element.after(...elements);
794
+ return this;
795
+ }
796
+ /**
797
+ * Retrieves the label associated with the HTML element.
798
+ * @returns {HTMLElement} The label element associated with this element.
799
+ */
800
+ getLabel() {
801
+ return document.querySelector(`#${this.element.id}_label`) || null;
802
+ }
803
+ /**
804
+ * Adds a tooltip with specified text to the label associated with the HTML element.
805
+ * @param innerHTML - The innerHTML to append into the tooltip.
806
+ * @param containerStyle - Optional object with CSS Styles to apply to the info element
807
+ * @returns - Instance of this [provides option to method chain]
808
+ */
809
+ addLabelTooltip(innerHTML, containerStyle) {
810
+ this.getLabel()?.append(
811
+ CreateInfoEl(innerHTML, containerStyle || void 0)
812
+ );
813
+ return this;
814
+ }
815
+ /**
816
+ * Adds a tooltip with the specified text to the element
817
+ * @param innerHTML - The innerHTML to append into the tooltip
818
+ * @param containerStyle - Optional object with CSS Styles to apply to the info element
819
+ * @returns - Instance of this [provides option to method chain]
820
+ */
821
+ addTooltip(innerHTML, containerStyle) {
822
+ this.append(CreateInfoEl(innerHTML, containerStyle || void 0));
823
+ return this;
824
+ }
825
+ /**
826
+ * Sets the inner HTML content of the HTML element.
827
+ * @param {string} string - The text to set as the inner HTML of the element.
828
+ * @returns - Instance of this [provides option to method chain]
829
+ */
830
+ setInnerHTML(string) {
831
+ this.element.innerHTML = string;
832
+ return this;
833
+ }
834
+ /**
835
+ * Removes this element from the DOM
836
+ * @returns - Instance of this [provides option to method chain]
837
+ */
838
+ remove() {
839
+ this.element.remove();
840
+ return this;
841
+ }
842
+ /**
843
+ *
844
+ * @param options and object containing the styles you want to set : {key: value} e.g.: {'display': 'block'}
845
+ * @returns - Instance of this [provides option to method chain]
846
+ */
847
+ setStyle(options) {
848
+ if (Object.prototype.toString.call(options) !== "[object Object]") {
849
+ throw new Error(
850
+ `powerpagestoolkit: 'DOMNodeReference.setStyle' required options to be in the form of an object. Argument passed was of type: ${typeof options}`
851
+ );
852
+ }
853
+ for (const _key in options) {
854
+ const key = _key;
855
+ this.element.style[key] = options[key];
856
+ }
857
+ return this;
858
+ }
859
+ /**
860
+ * Unchecks both the yes and no radio buttons if they exist.
861
+ * @returns - Instance of this [provides option to method chain]
862
+ */
863
+ uncheckRadios() {
864
+ if (this.yesRadio instanceof _DOMNodeReference && this.noRadio instanceof _DOMNodeReference) {
865
+ this.yesRadio.element.checked = false;
866
+ this.noRadio.element.checked = false;
867
+ } else {
868
+ console.error(
869
+ "[SYNACT] Attempted to uncheck radios for an element that has no radios"
870
+ );
871
+ }
872
+ return this;
873
+ }
874
+ /**
875
+ * Applies a business rule to manage visibility, required state, value, and disabled state dynamically.
876
+ * @see {@link BusinessRule}
877
+ * @param rule The business rule containing conditions for various actions.
878
+ * @param dependencies For re-evaluation conditions when the state of the dependencies change
879
+ * @returns Instance of this for method chaining.
880
+ */
881
+ applyBusinessRule(rule, dependencies) {
882
+ try {
883
+ if (rule.setVisibility) {
884
+ const [condition, clearValuesOnHide = true] = rule.setVisibility;
885
+ const initialState = condition.bind(this)();
886
+ this.toggleVisibility(initialState);
887
+ if (dependencies.length) {
888
+ this._configDependencyTracking(
889
+ () => this.toggleVisibility(condition.bind(this)()),
890
+ dependencies,
891
+ {
892
+ clearValuesOnHide,
893
+ observeVisibility: true,
894
+ trackInputEvents: false,
895
+ trackRadioButtons: false
896
+ }
897
+ );
898
+ }
899
+ }
900
+ if (rule.setRequired) {
901
+ const [isRequired, isValid] = rule.setRequired;
902
+ const fieldDisplayName = (() => {
903
+ let label = this.getLabel();
904
+ if (!label)
905
+ return new Error(
906
+ `There was an error accessing the label for this element: ${String(
907
+ this.target
908
+ )}`
909
+ );
910
+ label = label.innerHTML;
911
+ if (label.length > 50) {
912
+ label = label.substring(0, 50) + "...";
913
+ }
914
+ return label;
915
+ })();
916
+ if (typeof Page_Validators === "undefined") {
917
+ throw new ValidationConfigError(this, "Page_Validators not found");
918
+ }
919
+ const validatorId = `${this.element.id}Validator`;
920
+ const newValidator = document.createElement("span");
921
+ newValidator.style.display = "none";
922
+ newValidator.id = validatorId;
923
+ Object.assign(newValidator, {
924
+ controltovalidate: this.element.id,
925
+ errormessage: `<a href='#${this.element.id}_label'>${fieldDisplayName} is a required field</a>`,
926
+ evaluationfunction: () => {
927
+ const isFieldRequired = isRequired.bind(this)();
928
+ const isFieldVisible = window.getComputedStyle(this.visibilityController).display !== "none";
929
+ return !isFieldRequired || !isFieldVisible || isValid.bind(this)(isFieldRequired);
930
+ }
931
+ });
932
+ Page_Validators.push(newValidator);
933
+ this.setRequiredLevel(isRequired.bind(this)());
934
+ this._configDependencyTracking(
935
+ () => this.setRequiredLevel(isRequired.bind(this)()),
936
+ dependencies,
937
+ { clearValuesOnHide: false }
938
+ );
939
+ }
940
+ if (rule.setValue) {
941
+ let [condition, value] = rule.setValue;
942
+ if (value instanceof Function) value = value();
943
+ if (condition.bind(this)()) {
944
+ this.setValue.bind(this)(value);
945
+ }
946
+ if (dependencies.length) {
947
+ this._configDependencyTracking(
948
+ () => {
949
+ if (condition.bind(this)()) {
950
+ this.setValue.bind(this)(value);
951
+ }
952
+ },
953
+ dependencies,
954
+ { clearValuesOnHide: false }
955
+ );
956
+ }
957
+ }
958
+ if (rule.setDisabled) {
959
+ const condition = rule.setDisabled;
960
+ condition.bind(this)() ? this.disable() : this.enable();
961
+ if (dependencies.length) {
962
+ this._configDependencyTracking(
963
+ () => {
964
+ condition.bind(this)() ? this.enable() : this.disable();
965
+ },
966
+ dependencies,
967
+ {
968
+ clearValuesOnHide: false,
969
+ observeVisibility: true,
970
+ trackInputEvents: true,
971
+ trackRadioButtons: true
972
+ }
973
+ );
974
+ }
975
+ }
976
+ return this;
977
+ } catch (error) {
978
+ throw new ValidationConfigError(
979
+ this,
980
+ `Failed to apply business rule: ${error}`
981
+ );
982
+ }
983
+ }
984
+ /**
985
+ * Configures conditional rendering for the target element based on a condition
986
+ * and the visibility of one or more trigger elements.
987
+ * @deprecated Use the new 'applyBusinessRule Method
988
+ * @param condition A function that returns a boolean to determine
989
+ * the visibility of the target element. If `condition()` returns true, the element is shown;
990
+ * otherwise, it is hidden.
991
+ * @param dependencies - An array of `DOMNodeReference` instances. Event listeners are
992
+ * registered on each to toggle the visibility of the target element based on the `condition` and the visibility of
993
+ * the target node.
994
+ * @throws When there's an error in setting up conditional rendering
995
+ * @returns Instance of this [provides option to method chain]
996
+ */
997
+ configureConditionalRendering(condition, dependencies, clearValuesOnHide = true) {
998
+ try {
999
+ if (typeof condition !== "function") {
1000
+ throw new TypeError("Condition must be a function");
1001
+ }
1002
+ condition = condition.bind(this);
1003
+ const initialState = condition();
1004
+ this.toggleVisibility(initialState);
1005
+ if (!dependencies?.length) {
1006
+ console.warn(
1007
+ `powerpagestoolkit: No dependencies provided for conditional rendering of ${this}. Include referenced nodes in the dependency array if using them in rendering logic.`
1008
+ );
1009
+ return this;
1010
+ }
1011
+ this._configDependencyTracking(
1012
+ () => this.toggleVisibility(condition()),
1013
+ dependencies,
1014
+ {
1015
+ clearValuesOnHide,
1016
+ observeVisibility: true,
1017
+ trackInputEvents: false,
1018
+ trackRadioButtons: false
1019
+ }
1020
+ );
1021
+ return this;
1022
+ } catch (error) {
1023
+ const errorMessage = error instanceof Error ? error.message : String(error);
1024
+ throw new ConditionalRenderingError(this, errorMessage);
1025
+ }
1026
+ }
1027
+ /**
1028
+ * Sets up validation and requirement rules for the field with enhanced error handling and dynamic updates.
1029
+ * @deprecated Use the new 'applyBusinessRule Method
1030
+ * @param isRequired Function determining if field is required
1031
+ * @param isValid Function validating field input
1032
+ * @param fieldDisplayName Display name for error messages
1033
+ * @param dependencies Fields that trigger requirement/validation updates
1034
+ * @returns Instance of this
1035
+ * @throws If validation setup fails
1036
+ */
1037
+ configureValidationAndRequirements(isRequired, isValid, fieldDisplayName, dependencies) {
1038
+ if (!fieldDisplayName?.trim()) {
1039
+ throw new ValidationConfigError(this, "Field display name is required");
1040
+ }
1041
+ if (!Array.isArray(dependencies)) {
1042
+ throw new ValidationConfigError(this, "Dependencies must be an array");
1043
+ }
1044
+ try {
1045
+ isRequired = isRequired.bind(this);
1046
+ isValid = isValid.bind(this);
1047
+ if (typeof Page_Validators === "undefined") {
1048
+ throw new ValidationConfigError(this, "Page_Validators not found");
1049
+ }
1050
+ const validatorId = `${this.element.id}Validator`;
1051
+ const newValidator = document.createElement("span");
1052
+ newValidator.style.display = "none";
1053
+ newValidator.id = validatorId;
1054
+ const validatorConfig = {
1055
+ controltovalidate: this.element.id,
1056
+ errormessage: `<a href='#${this.element.id}_label'>${fieldDisplayName} is a required field</a>`,
1057
+ evaluationfunction: () => {
1058
+ const isFieldRequired = isRequired();
1059
+ const isFieldVisible = window.getComputedStyle(this.visibilityController).display !== "none";
1060
+ if (!isFieldRequired || !isFieldVisible) {
1061
+ return true;
1062
+ }
1063
+ return isValid();
1064
+ }
1065
+ };
1066
+ Object.assign(newValidator, validatorConfig);
1067
+ Page_Validators.push(newValidator);
1068
+ this.setRequiredLevel(isRequired());
1069
+ this._configDependencyTracking(
1070
+ () => this.setRequiredLevel(isRequired()),
1071
+ dependencies
1072
+ );
1073
+ } catch (error) {
1074
+ throw new ValidationConfigError(
1075
+ this,
1076
+ `Failed to configure validation: ${error}`
1077
+ );
1078
+ }
1079
+ return this;
1080
+ }
1081
+ /**
1082
+ * Sets up tracking for dependencies using both event listeners and mutation observers.
1083
+ * @protected
1084
+ * @param handler The function to execute when dependencies change
1085
+ * @param dependencies Array of dependent DOM nodes to track
1086
+ * @param options Additional configuration options. clearValuesOnHide defaults to false.
1087
+ * all other options defaults to true
1088
+ */
1089
+ _configDependencyTracking(handler, dependencies, options = {
1090
+ clearValuesOnHide: false,
1091
+ observeVisibility: true,
1092
+ trackInputEvents: true,
1093
+ trackRadioButtons: true
1094
+ }) {
1095
+ const {
1096
+ clearValuesOnHide = false,
1097
+ observeVisibility = true,
1098
+ trackInputEvents = true,
1099
+ trackRadioButtons = true
1100
+ } = options;
1101
+ if (!dependencies?.length) {
1102
+ console.warn(
1103
+ `powerpagestoolkit: No dependencies specified for ${this.element.id}. Include all referenced nodes in the dependency array for proper tracking.`
1104
+ );
1105
+ return;
1106
+ }
1107
+ dependencies.forEach((dep) => {
1108
+ if (!dep || !(dep instanceof _DOMNodeReference)) {
1109
+ throw new TypeError(
1110
+ "Each dependency must be a valid DOMNodeReference instance"
1111
+ );
1112
+ }
1113
+ const handleChange = () => {
1114
+ handler();
1115
+ if (clearValuesOnHide && window.getComputedStyle(this.visibilityController).display === "none") {
1116
+ this.clearValue();
1117
+ }
1118
+ };
1119
+ this[registerEventListener](dep.element, "change", handleChange);
1120
+ if (trackInputEvents) {
1121
+ this[registerEventListener](dep.element, "input", handleChange);
1122
+ }
1123
+ if (observeVisibility) {
1124
+ const observer = new MutationObserver(() => {
1125
+ const display = window.getComputedStyle(
1126
+ dep.visibilityController
1127
+ ).display;
1128
+ if (display !== "none") {
1129
+ handler();
1130
+ }
1131
+ });
1132
+ observer.observe(dep.visibilityController, {
1133
+ attributes: true,
1134
+ attributeFilter: ["style"],
1135
+ subtree: false
1136
+ });
1137
+ this[observers].push(observer);
1138
+ }
1139
+ if (trackRadioButtons && dep.yesRadio && dep.noRadio) {
1140
+ [dep.yesRadio, dep.noRadio].forEach((radio) => {
1141
+ radio.on("change", handleChange);
1142
+ this[boundEventListeners].push({
1143
+ element: radio.element,
1144
+ event: "change",
1145
+ handler: handleChange
1146
+ });
1147
+ });
1148
+ }
1149
+ });
1150
+ }
1151
+ /**
1152
+ * Sets the required level for the field by adding or removing the "required-field" class on the label.
1153
+ *
1154
+ * @param isRequired Determines whether the field should be marked as required.
1155
+ * If true, the "required-field" class is added to the label; if false, it is removed.
1156
+ * @returns Instance of this [provides option to method chain]
1157
+ */
1158
+ setRequiredLevel(isRequired) {
1159
+ if (isRequired instanceof Function) {
1160
+ isRequired() ? this.getLabel()?.classList.add("required-field") : this.getLabel()?.classList.remove("required-field");
1161
+ return this;
1162
+ } else {
1163
+ isRequired ? this.getLabel()?.classList.add("required-field") : this.getLabel()?.classList.remove("required-field");
1164
+ return this;
1165
+ }
1166
+ }
1167
+ /**
1168
+ * Executes a callback function once the element is fully loaded.
1169
+ * If the element is already loaded, the callback is called immediately.
1170
+ * Otherwise, a MutationObserver is used to detect when the element is added to the DOM.
1171
+ * @param callback A callback function to execute once the element is loaded.
1172
+ * Receives instance of 'this' as an argument
1173
+ */
1174
+ onceLoaded(callback) {
1175
+ if (this.isLoaded) {
1176
+ callback(this);
1177
+ return;
1178
+ }
1179
+ if (this.target instanceof HTMLElement) {
1180
+ callback(this);
1181
+ return;
1182
+ }
1183
+ const observer = new MutationObserver(() => {
1184
+ if (document.querySelector(this.target)) {
1185
+ observer.disconnect();
1186
+ this.isLoaded = true;
1187
+ callback(this);
1188
+ }
1189
+ });
1190
+ observer.observe(document.body, {
1191
+ subtree: true,
1192
+ childList: true
1193
+ });
1194
+ this[observers].push(observer);
1195
+ }
1196
+ };
1197
+
1198
+ // src/core/DOMNodeReferenceArray.ts
1199
+ var DOMNodeReferenceArray = class extends Array {
1200
+ /**
1201
+ * Hides all the containers of the DOMNodeReference instances in the array.
1202
+ */
1203
+ hideAll() {
1204
+ this.forEach((instance) => instance.hide());
1205
+ return this;
1206
+ }
1207
+ /**
1208
+ * Shows all the containers of the DOMNodeReference instances in the array.
1209
+ */
1210
+ showAll() {
1211
+ this.forEach((instance) => instance.show());
1212
+ return this;
1213
+ }
1214
+ };
1215
+
1216
+ // src/utils/enhanceArray.ts
1217
+ function enhanceArray(array) {
1218
+ const enhancedArray = new DOMNodeReferenceArray(...array);
1219
+ return new Proxy(enhancedArray, {
1220
+ get(target, prop, receiver) {
1221
+ if (prop in target) {
1222
+ return Reflect.get(target, prop, receiver);
1223
+ }
1224
+ if (typeof prop === "string") {
1225
+ return target.find(
1226
+ (instance) => instance.target.toString().replace(/[#\[\]]/g, "") === prop || instance.logicalName === prop
1227
+ );
1228
+ }
1229
+ return void 0;
1230
+ }
1231
+ });
1232
+ }
1233
+
1234
+ // src/core/createDOMNodeReferences.ts
1235
+ async function createDOMNodeReference(target, options = {
1236
+ multiple: false,
1237
+ root: document.body,
1238
+ timeoutMs: 0
1239
+ }) {
1240
+ try {
1241
+ if (typeof options !== "object") {
1242
+ throw new Error(
1243
+ `'options' must be of type 'object'. Received type: '${typeof options}'`
1244
+ );
1245
+ }
1246
+ validateOptions(options);
1247
+ const { multiple = false, root = document.body, timeoutMs = 0 } = options;
1248
+ const isMultiple = typeof multiple === "function" ? multiple() : multiple;
1249
+ if (isMultiple) {
1250
+ if (typeof target !== "string") {
1251
+ throw new Error(
1252
+ `'target' must be of type 'string' if 'multiple' is set to 'true'. Received type: '${typeof target}'`
1253
+ );
1254
+ }
1255
+ const elements = await waitFor(target, root, true, timeoutMs);
1256
+ const initializedElements = await Promise.all(
1257
+ elements.map(async (element) => {
1258
+ const instance2 = new DOMNodeReference(element, root, timeoutMs);
1259
+ await instance2[init]();
1260
+ return new Proxy(instance2, createProxyHandler());
1261
+ })
1262
+ );
1263
+ return enhanceArray(initializedElements);
1264
+ }
1265
+ const instance = new DOMNodeReference(target, root, timeoutMs);
1266
+ await instance[init]();
1267
+ return new Proxy(instance, createProxyHandler());
1268
+ } catch (e) {
1269
+ throw new Error(e);
1270
+ }
1271
+ }
1272
+ function validateOptions(options) {
1273
+ const { multiple = false, root = document.body, timeoutMs = 0 } = options;
1274
+ if (typeof multiple !== "boolean" && typeof multiple !== "function") {
1275
+ throw new Error(
1276
+ `'multiple' must be of type 'boolean' or 'function'. Received type: '${typeof multiple}'`
1277
+ );
1278
+ }
1279
+ if (typeof multiple === "function") {
1280
+ const value = multiple();
1281
+ if (typeof value !== "boolean") {
1282
+ throw new Error(
1283
+ `'multiple' function must return a boolean. Received type: '${typeof value}'`
1284
+ );
1285
+ }
1286
+ }
1287
+ if (!(root instanceof HTMLElement)) {
1288
+ throw new Error(
1289
+ `'root' must be of type 'HTMLElement'. Received type: '${typeof root}'`
1290
+ );
1291
+ }
1292
+ if (typeof timeoutMs !== "number") {
1293
+ throw new Error(
1294
+ `'timeout' must be of type 'number'. Received type: '${typeof timeoutMs}'`
1295
+ );
1296
+ }
1297
+ return;
1298
+ }
1299
+ function createProxyHandler() {
1300
+ return {
1301
+ get: (target, prop) => {
1302
+ if (prop.toString().startsWith("_")) return void 0;
1303
+ const value = target[prop];
1304
+ if (typeof value === "function" && prop !== "onceLoaded") {
1305
+ return (...args) => {
1306
+ target.onceLoaded(() => value.apply(target, args));
1307
+ return target;
1308
+ };
1309
+ }
1310
+ return value;
1311
+ }
1312
+ };
1313
+ }
1314
+
1315
+ // src/core/bindForm.ts
1316
+ async function bindForm(formId) {
1317
+ try {
1318
+ const form = await API_default.getRecord("systemforms", formId);
1319
+ const { formxml } = form;
1320
+ const parser = new DOMParser();
1321
+ const xmlDoc = parser.parseFromString(formxml, "application/xml");
1322
+ const controls = processElements(xmlDoc.getElementsByTagName("control"));
1323
+ const sections = processElements(xmlDoc.getElementsByTagName("section"));
1324
+ const tabs = processElements(xmlDoc.getElementsByTagName("tab"));
1325
+ const resolvedRefs = await Promise.all([...controls, ...sections, ...tabs]);
1326
+ return enhanceArray(
1327
+ resolvedRefs.filter((ref) => ref !== null)
1328
+ );
1329
+ } catch (error) {
1330
+ if (error instanceof Error) {
1331
+ console.error(error.message);
1332
+ throw error;
1333
+ } else {
1334
+ console.error(error);
1335
+ throw new Error(String(error));
1336
+ }
1337
+ }
1338
+ }
1339
+ function processElements(element) {
1340
+ return Array.from(element).map((element2) => {
1341
+ const identifyingAttribute = getIdentifyingAttribute(element2.tagName);
1342
+ const datafieldname = element2.getAttribute(identifyingAttribute);
1343
+ if (!datafieldname) return null;
1344
+ const referenceString = createReferenceString(
1345
+ element2.tagName,
1346
+ datafieldname
1347
+ );
1348
+ if (!referenceString) return null;
1349
+ return createDOMNodeReference(referenceString).catch((error) => {
1350
+ console.warn(
1351
+ `Failed to create a reference to the form field: ${datafieldname}`,
1352
+ error
1353
+ );
1354
+ return null;
1355
+ });
1356
+ }).filter(Boolean);
1357
+ }
1358
+ function getIdentifyingAttribute(tagName) {
1359
+ return tagName === "control" ? "id" : tagName === "tab" || tagName === "section" ? "name" : "id";
1360
+ }
1361
+ function createReferenceString(tagName, datafieldname) {
1362
+ if (tagName === "control") return `#${datafieldname}`;
1363
+ if (tagName === "tab" || tagName === "section")
1364
+ return `[data-name="${datafieldname}"]`;
1365
+ return null;
1366
+ }
1367
+ return __toCommonJS(src_exports);
1368
+ })();