powerpagestoolkit 2.7.135 → 2.7.211

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