@4site/engrid-scripts 0.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/.prettierrc.js +226 -0
  2. package/LICENSE +24 -0
  3. package/dist/a11y.d.ts +6 -0
  4. package/dist/a11y.js +46 -0
  5. package/dist/add-name-to-message.d.ts +5 -0
  6. package/dist/add-name-to-message.js +49 -0
  7. package/dist/advocacy.d.ts +7 -0
  8. package/dist/advocacy.js +34 -0
  9. package/dist/amount-label.d.ts +6 -0
  10. package/dist/amount-label.js +29 -0
  11. package/dist/app.d.ts +19 -0
  12. package/dist/app.js +340 -0
  13. package/dist/apple-pay.d.ts +13 -0
  14. package/dist/apple-pay.js +169 -0
  15. package/dist/auto-country-select.d.ts +10 -0
  16. package/dist/auto-country-select.js +63 -0
  17. package/dist/auto-year.d.ts +7 -0
  18. package/dist/auto-year.js +35 -0
  19. package/dist/autocomplete.d.ts +5 -0
  20. package/dist/autocomplete.js +38 -0
  21. package/dist/autosubmit.d.ts +5 -0
  22. package/dist/autosubmit.js +14 -0
  23. package/dist/branding-html.d.ts +14 -0
  24. package/dist/branding-html.js +70 -0
  25. package/dist/capitalize-fields.d.ts +7 -0
  26. package/dist/capitalize-fields.js +22 -0
  27. package/dist/click-to-expand.d.ts +4 -0
  28. package/dist/click-to-expand.js +39 -0
  29. package/dist/cookie.d.ts +53 -0
  30. package/dist/cookie.js +76 -0
  31. package/dist/country-disable.d.ts +4 -0
  32. package/dist/country-disable.js +22 -0
  33. package/dist/country-redirect.d.ts +7 -0
  34. package/dist/country-redirect.js +61 -0
  35. package/dist/custom-currency.d.ts +9 -0
  36. package/dist/custom-currency.js +112 -0
  37. package/dist/data-attributes.d.ts +5 -0
  38. package/dist/data-attributes.js +122 -0
  39. package/dist/data-hide.d.ts +15 -0
  40. package/dist/data-hide.js +66 -0
  41. package/dist/data-layer.d.ts +23 -0
  42. package/dist/data-layer.js +312 -0
  43. package/dist/data-replace.d.ts +26 -0
  44. package/dist/data-replace.js +76 -0
  45. package/dist/debug-hidden-fields.d.ts +4 -0
  46. package/dist/debug-hidden-fields.js +42 -0
  47. package/dist/debug-panel.d.ts +27 -0
  48. package/dist/debug-panel.js +508 -0
  49. package/dist/deprecated.d.ts +5 -0
  50. package/dist/deprecated.js +40 -0
  51. package/dist/digital-wallets.d.ts +8 -0
  52. package/dist/digital-wallets.js +143 -0
  53. package/dist/ecard-to-target.d.ts +11 -0
  54. package/dist/ecard-to-target.js +73 -0
  55. package/dist/ecard.d.ts +8 -0
  56. package/dist/ecard.js +61 -0
  57. package/dist/embedded-ecard.d.ts +18 -0
  58. package/dist/embedded-ecard.js +267 -0
  59. package/dist/en-validators.d.ts +10 -0
  60. package/dist/en-validators.js +89 -0
  61. package/dist/engrid.d.ts +49 -0
  62. package/dist/engrid.js +547 -0
  63. package/dist/event-tickets.d.ts +3 -0
  64. package/dist/event-tickets.js +24 -0
  65. package/dist/events/country.d.ts +12 -0
  66. package/dist/events/country.js +42 -0
  67. package/dist/events/donation-amount.d.ts +16 -0
  68. package/dist/events/donation-amount.js +119 -0
  69. package/dist/events/donation-frequency.d.ts +17 -0
  70. package/dist/events/donation-frequency.js +115 -0
  71. package/dist/events/en-form.d.ts +20 -0
  72. package/dist/events/en-form.js +52 -0
  73. package/dist/events/index.d.ts +6 -0
  74. package/dist/events/index.js +6 -0
  75. package/dist/events/processing-fees.d.ts +20 -0
  76. package/dist/events/processing-fees.js +92 -0
  77. package/dist/events/remember-me-events.d.ts +18 -0
  78. package/dist/events/remember-me-events.js +36 -0
  79. package/dist/exit-intent-lightbox.d.ts +14 -0
  80. package/dist/exit-intent-lightbox.js +145 -0
  81. package/dist/expand-region-name.d.ts +7 -0
  82. package/dist/expand-region-name.js +44 -0
  83. package/dist/fast-form-fill.d.ts +8 -0
  84. package/dist/fast-form-fill.js +75 -0
  85. package/dist/freshaddress.d.ts +18 -0
  86. package/dist/freshaddress.js +204 -0
  87. package/dist/give-by-select.d.ts +9 -0
  88. package/dist/give-by-select.js +82 -0
  89. package/dist/iframe.d.ts +16 -0
  90. package/dist/iframe.js +217 -0
  91. package/dist/index.d.ts +82 -0
  92. package/dist/index.js +84 -0
  93. package/dist/input-has-value-and-focus.d.ts +9 -0
  94. package/dist/input-has-value-and-focus.js +50 -0
  95. package/dist/input-placeholders.d.ts +7 -0
  96. package/dist/input-placeholders.js +82 -0
  97. package/dist/interfaces/ecard-to-target-options.d.ts +9 -0
  98. package/dist/interfaces/ecard-to-target-options.js +8 -0
  99. package/dist/interfaces/embedded-ecard-options.d.ts +8 -0
  100. package/dist/interfaces/embedded-ecard-options.js +7 -0
  101. package/dist/interfaces/exit-intent-options.d.ts +13 -0
  102. package/dist/interfaces/exit-intent-options.js +13 -0
  103. package/dist/interfaces/options.d.ts +147 -0
  104. package/dist/interfaces/options.js +50 -0
  105. package/dist/interfaces/translate-options.d.ts +7 -0
  106. package/dist/interfaces/translate-options.js +42 -0
  107. package/dist/interfaces/upsell-options.d.ts +24 -0
  108. package/dist/interfaces/upsell-options.js +33 -0
  109. package/dist/live-currency.d.ts +15 -0
  110. package/dist/live-currency.js +140 -0
  111. package/dist/live-frequency.d.ts +13 -0
  112. package/dist/live-frequency.js +109 -0
  113. package/dist/live-variables.d.ts +21 -0
  114. package/dist/live-variables.js +147 -0
  115. package/dist/loader.d.ts +10 -0
  116. package/dist/loader.js +175 -0
  117. package/dist/logger.d.ts +16 -0
  118. package/dist/logger.js +84 -0
  119. package/dist/media-attribution.d.ts +4 -0
  120. package/dist/media-attribution.js +66 -0
  121. package/dist/menu.d.ts +0 -0
  122. package/dist/menu.js +1 -0
  123. package/dist/min-max-amount.d.ts +13 -0
  124. package/dist/min-max-amount.js +71 -0
  125. package/dist/mobile-cta.d.ts +9 -0
  126. package/dist/mobile-cta.js +71 -0
  127. package/dist/modal.d.ts +19 -0
  128. package/dist/modal.js +129 -0
  129. package/dist/neverbounce.d.ts +23 -0
  130. package/dist/neverbounce.js +244 -0
  131. package/dist/other-amount.d.ts +6 -0
  132. package/dist/other-amount.js +70 -0
  133. package/dist/page-background.d.ts +7 -0
  134. package/dist/page-background.js +55 -0
  135. package/dist/plaid.d.ts +6 -0
  136. package/dist/plaid.js +49 -0
  137. package/dist/postal-code-validator.d.ts +20 -0
  138. package/dist/postal-code-validator.js +118 -0
  139. package/dist/premium-gift.d.ts +10 -0
  140. package/dist/premium-gift.js +115 -0
  141. package/dist/progress-bar.d.ts +3 -0
  142. package/dist/progress-bar.js +40 -0
  143. package/dist/remember-me.d.ts +61 -0
  144. package/dist/remember-me.js +358 -0
  145. package/dist/required-if-visible.d.ts +8 -0
  146. package/dist/required-if-visible.js +60 -0
  147. package/dist/set-attr.d.ts +4 -0
  148. package/dist/set-attr.js +31 -0
  149. package/dist/set-recurr-freq.d.ts +7 -0
  150. package/dist/set-recurr-freq.js +70 -0
  151. package/dist/show-hide-radio-checkboxes.d.ts +13 -0
  152. package/dist/show-hide-radio-checkboxes.js +161 -0
  153. package/dist/show-if-amount.d.ts +15 -0
  154. package/dist/show-if-amount.js +117 -0
  155. package/dist/show-if-present.d.ts +7 -0
  156. package/dist/show-if-present.js +79 -0
  157. package/dist/skip-link.d.ts +4 -0
  158. package/dist/skip-link.js +36 -0
  159. package/dist/src-defer.d.ts +6 -0
  160. package/dist/src-defer.js +70 -0
  161. package/dist/supporter-hub.d.ts +9 -0
  162. package/dist/supporter-hub.js +75 -0
  163. package/dist/swap-amounts.d.ts +45 -0
  164. package/dist/swap-amounts.js +81 -0
  165. package/dist/thank-you-page-conditional-content.d.ts +8 -0
  166. package/dist/thank-you-page-conditional-content.js +46 -0
  167. package/dist/ticker.d.ts +11 -0
  168. package/dist/ticker.js +66 -0
  169. package/dist/tidycontact.d.ts +46 -0
  170. package/dist/tidycontact.js +1202 -0
  171. package/dist/translate-fields.d.ts +12 -0
  172. package/dist/translate-fields.js +548 -0
  173. package/dist/universal-opt-in.d.ts +7 -0
  174. package/dist/universal-opt-in.js +91 -0
  175. package/dist/upsell-lightbox.d.ts +31 -0
  176. package/dist/upsell-lightbox.js +363 -0
  177. package/dist/url-params-to-body-attrs.d.ts +5 -0
  178. package/dist/url-params-to-body-attrs.js +14 -0
  179. package/dist/url-to-form.d.ts +7 -0
  180. package/dist/url-to-form.js +32 -0
  181. package/dist/us-only-form.d.ts +4 -0
  182. package/dist/us-only-form.js +30 -0
  183. package/dist/version.d.ts +1 -0
  184. package/dist/version.js +1 -0
  185. package/dist/vgs.d.ts +16 -0
  186. package/dist/vgs.js +250 -0
  187. package/dist/welcome-back.d.ts +10 -0
  188. package/dist/welcome-back.js +112 -0
  189. package/package.json +32 -0
@@ -0,0 +1,161 @@
1
+ import { ENGrid, EngridLogger } from ".";
2
+ export class ShowHideRadioCheckboxes {
3
+ // Create default data attributes on all fields
4
+ createDataAttributes() {
5
+ this.elements.forEach((item) => {
6
+ if (item instanceof HTMLInputElement) {
7
+ let inputValue = item.value.replace(/\W/g, "");
8
+ document
9
+ .querySelectorAll("." + this.classes + inputValue)
10
+ .forEach((el) => {
11
+ // Consider toggling "hide" class so these fields can be displayed when in a debug state
12
+ if (el instanceof HTMLElement) {
13
+ const fields = el.querySelectorAll("input[type='text'], input[type='number'], input[type='email'], select, textarea");
14
+ if (fields.length > 0) {
15
+ fields.forEach((field) => {
16
+ if (field instanceof HTMLInputElement ||
17
+ field instanceof HTMLSelectElement) {
18
+ if (!field.hasAttribute("data-original-value")) {
19
+ field.setAttribute("data-original-value", field.value);
20
+ }
21
+ if (!field.hasAttribute("data-value")) {
22
+ field.setAttribute("data-value", field.value);
23
+ }
24
+ }
25
+ });
26
+ }
27
+ }
28
+ });
29
+ }
30
+ });
31
+ }
32
+ // Hide All Divs
33
+ hideAll() {
34
+ this.elements.forEach((item, index) => {
35
+ if (item instanceof HTMLInputElement)
36
+ this.hide(item);
37
+ });
38
+ }
39
+ // Hide Single Element Div
40
+ hide(item) {
41
+ let inputValue = item.value.replace(/\W/g, "");
42
+ document.querySelectorAll("." + this.classes + inputValue).forEach((el) => {
43
+ // Consider toggling "hide" class so these fields can be displayed when in a debug state
44
+ if (el instanceof HTMLElement) {
45
+ this.toggleValue(el, "hide");
46
+ el.style.display = "none";
47
+ this.logger.log("Hiding", el);
48
+ }
49
+ });
50
+ }
51
+ // Show Single Element Div
52
+ show(item) {
53
+ let inputValue = item.value.replace(/\W/g, "");
54
+ document.querySelectorAll("." + this.classes + inputValue).forEach((el) => {
55
+ // Consider toggling "hide" class so these fields can be displayed when in a debug state
56
+ if (el instanceof HTMLElement) {
57
+ this.toggleValue(el, "show");
58
+ el.style.display = "";
59
+ this.logger.log("Showing", el);
60
+ }
61
+ });
62
+ if (item.type == "checkbox" && !item.checked) {
63
+ this.hide(item);
64
+ }
65
+ }
66
+ // Take the field values and add to a data attribute on the field
67
+ toggleValue(item, type) {
68
+ if (type == "hide" && !ENGrid.isVisible(item))
69
+ return;
70
+ this.logger.log(`toggleValue: ${type}`);
71
+ const fields = item.querySelectorAll("input[type='text'], input[type='number'], input[type='email'], select, textarea");
72
+ if (fields.length > 0) {
73
+ fields.forEach((field) => {
74
+ var _a;
75
+ if (field instanceof HTMLInputElement ||
76
+ field instanceof HTMLSelectElement) {
77
+ if (field.name) {
78
+ const fieldValue = ENGrid.getFieldValue(field.name);
79
+ const originalValue = field.getAttribute("data-original-value");
80
+ const dataValue = (_a = field.getAttribute("data-value")) !== null && _a !== void 0 ? _a : "";
81
+ if (type === "hide") {
82
+ field.setAttribute("data-value", fieldValue);
83
+ ENGrid.setFieldValue(field.name, originalValue);
84
+ }
85
+ else {
86
+ ENGrid.setFieldValue(field.name, dataValue);
87
+ }
88
+ }
89
+ }
90
+ });
91
+ }
92
+ }
93
+ getSessionState() {
94
+ var _a;
95
+ try {
96
+ const plainState = (_a = window.sessionStorage.getItem(`engrid_ShowHideRadioCheckboxesState`)) !== null && _a !== void 0 ? _a : "";
97
+ return JSON.parse(plainState);
98
+ }
99
+ catch (err) {
100
+ return [];
101
+ }
102
+ }
103
+ storeSessionState() {
104
+ const state = this.getSessionState();
105
+ [...this.elements].forEach((element) => {
106
+ var _a, _b;
107
+ if (!(element instanceof HTMLInputElement))
108
+ return;
109
+ if (element.type == "radio" && element.checked) {
110
+ //remove other items that have the same "class" property
111
+ state.forEach((item, index) => {
112
+ if (item.class == this.classes) {
113
+ state.splice(index, 1);
114
+ }
115
+ });
116
+ //add the current item, with the currently active value
117
+ state.push({
118
+ page: ENGrid.getPageID(),
119
+ class: this.classes,
120
+ value: element.value,
121
+ });
122
+ this.logger.log("storing radio state", state[state.length - 1]);
123
+ }
124
+ if (element.type == "checkbox") {
125
+ //remove other items that have the same "class" property
126
+ state.forEach((item, index) => {
127
+ if (item.class == this.classes) {
128
+ state.splice(index, 1);
129
+ }
130
+ });
131
+ //add the current item, with the first checked value or "N" if none are checked
132
+ state.push({
133
+ page: ENGrid.getPageID(),
134
+ class: this.classes,
135
+ value: (_b = (_a = [...this.elements].find((el) => el.checked)) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : "N", // First checked value or "N" if none
136
+ });
137
+ this.logger.log("storing checkbox state", state[state.length - 1]);
138
+ }
139
+ });
140
+ window.sessionStorage.setItem(`engrid_ShowHideRadioCheckboxesState`, JSON.stringify(state));
141
+ }
142
+ constructor(elements, classes) {
143
+ this.logger = new EngridLogger("ShowHideRadioCheckboxes", "black", "lightblue", "👁");
144
+ this.elements = document.getElementsByName(elements);
145
+ this.classes = classes;
146
+ this.createDataAttributes();
147
+ this.hideAll();
148
+ this.storeSessionState();
149
+ for (let i = 0; i < this.elements.length; i++) {
150
+ let element = this.elements[i];
151
+ if (element.checked) {
152
+ this.show(element);
153
+ }
154
+ element.addEventListener("change", (e) => {
155
+ this.hideAll();
156
+ this.show(element);
157
+ this.storeSessionState();
158
+ });
159
+ }
160
+ }
161
+ }
@@ -0,0 +1,15 @@
1
+ import { DonationAmount } from "./events";
2
+ export declare class ShowIfAmount {
3
+ _amount: DonationAmount;
4
+ private _elements;
5
+ private logger;
6
+ constructor();
7
+ private init;
8
+ private getClassNameByOperand;
9
+ private lessthan;
10
+ private lessthanorequalto;
11
+ private equalto;
12
+ private greaterthanorequalto;
13
+ private greaterthan;
14
+ private between;
15
+ }
@@ -0,0 +1,117 @@
1
+ import { ENGrid, EngridLogger } from ".";
2
+ import { DonationAmount } from "./events";
3
+ export class ShowIfAmount {
4
+ constructor() {
5
+ this._amount = DonationAmount.getInstance();
6
+ this.logger = new EngridLogger("ShowIfAmount", "yellow", "black", "👀");
7
+ this._elements = document.querySelectorAll('[class*="showifamount"]');
8
+ if (this._elements.length > 0) {
9
+ this._amount.onAmountChange.subscribe(() => this.init());
10
+ this.init();
11
+ return;
12
+ }
13
+ this.logger.log("Show If Amount: NO ELEMENTS FOUND");
14
+ }
15
+ init() {
16
+ //If we are on a thank you page, use the window.pageJson.amount
17
+ const amount = ENGrid.getGiftProcess()
18
+ ? window.pageJson.amount
19
+ : this._amount.amount;
20
+ this._elements.forEach((element) => {
21
+ this.lessthan(amount, element);
22
+ this.lessthanorequalto(amount, element);
23
+ this.equalto(amount, element);
24
+ this.greaterthanorequalto(amount, element);
25
+ this.greaterthan(amount, element);
26
+ this.between(amount, element);
27
+ });
28
+ }
29
+ getClassNameByOperand(classList, operand) {
30
+ let myClass = null;
31
+ classList.forEach((className) => {
32
+ if (className.includes(`showifamount-${operand}-`)) {
33
+ myClass = className;
34
+ }
35
+ });
36
+ return myClass;
37
+ }
38
+ lessthan(amount, element) {
39
+ const showifamountClass = this.getClassNameByOperand(element.classList, "lessthan");
40
+ if (showifamountClass) {
41
+ let amountCheck = showifamountClass.split("-").slice(-1)[0];
42
+ if (amount < Number(amountCheck)) {
43
+ this.logger.log("(lessthan):", element);
44
+ element.classList.add("engrid-open");
45
+ }
46
+ else {
47
+ element.classList.remove("engrid-open");
48
+ }
49
+ }
50
+ }
51
+ lessthanorequalto(amount, element) {
52
+ const showifamountClass = this.getClassNameByOperand(element.classList, "lessthanorequalto");
53
+ if (showifamountClass) {
54
+ let amountCheck = showifamountClass.split("-").slice(-1)[0];
55
+ if (amount <= Number(amountCheck)) {
56
+ this.logger.log("(lessthanorequalto):", element);
57
+ element.classList.add("engrid-open");
58
+ }
59
+ else {
60
+ element.classList.remove("engrid-open");
61
+ }
62
+ }
63
+ }
64
+ equalto(amount, element) {
65
+ const showifamountClass = this.getClassNameByOperand(element.classList, "equalto");
66
+ if (showifamountClass) {
67
+ let amountCheck = showifamountClass.split("-").slice(-1)[0];
68
+ if (amount == Number(amountCheck)) {
69
+ this.logger.log("(equalto):", element);
70
+ element.classList.add("engrid-open");
71
+ }
72
+ else {
73
+ element.classList.remove("engrid-open");
74
+ }
75
+ }
76
+ }
77
+ greaterthanorequalto(amount, element) {
78
+ const showifamountClass = this.getClassNameByOperand(element.classList, "greaterthanorequalto");
79
+ if (showifamountClass) {
80
+ let amountCheck = showifamountClass.split("-").slice(-1)[0];
81
+ if (amount >= Number(amountCheck)) {
82
+ this.logger.log("(greaterthanorequalto):", element);
83
+ element.classList.add("engrid-open");
84
+ }
85
+ else {
86
+ element.classList.remove("engrid-open");
87
+ }
88
+ }
89
+ }
90
+ greaterthan(amount, element) {
91
+ const showifamountClass = this.getClassNameByOperand(element.classList, "greaterthan");
92
+ if (showifamountClass) {
93
+ let amountCheck = showifamountClass.split("-").slice(-1)[0];
94
+ if (amount > Number(amountCheck)) {
95
+ this.logger.log("(greaterthan):", element);
96
+ element.classList.add("engrid-open");
97
+ }
98
+ else {
99
+ element.classList.remove("engrid-open");
100
+ }
101
+ }
102
+ }
103
+ between(amount, element) {
104
+ const showifamountClass = this.getClassNameByOperand(element.classList, "between");
105
+ if (showifamountClass) {
106
+ let amountCheckMin = showifamountClass.split("-").slice(-2, -1)[0];
107
+ let amountCheckMax = showifamountClass.split("-").slice(-1)[0];
108
+ if (amount > Number(amountCheckMin) && amount < Number(amountCheckMax)) {
109
+ this.logger.log("(between):", element);
110
+ element.classList.add("engrid-open");
111
+ }
112
+ else {
113
+ element.classList.remove("engrid-open");
114
+ }
115
+ }
116
+ }
117
+ }
@@ -0,0 +1,7 @@
1
+ export declare class ShowIfPresent {
2
+ private logger;
3
+ private elements;
4
+ constructor();
5
+ private shouldRun;
6
+ private run;
7
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * This class contains the logic for special classes that can be used to hide elements if
3
+ * certain supporter questions are present or absent.
4
+ * Typically, this can be used to hide elements when an opt in question is not rendered on the page
5
+ * because the supporter came from a campaign link and is already opted in, so EN doesn't render
6
+ * the question on the page.
7
+ *
8
+ * The class names are of the format:
9
+ * engrid__supporterquestions{id}-present -- show this element when the supporter question is present
10
+ * engrid__supporterquestions{id}-absent -- show this element when the supporter question is absent
11
+ *
12
+ * The {id} is the id of the supporter question. This can be found by inspecting the element on the page.
13
+ *
14
+ * It's also possible to combine multiple questions using the following format. These examples show 2 questions,
15
+ * but you can use as many as you like:
16
+ * engrid__supporterquestions{id1}__supporterquestions{id2}-present -- show this element when EITHER question is present
17
+ * engrid__supporterquestions{id1}__supporterquestions{id2}-absent -- show this element when EITHER question is absent
18
+ */
19
+ import { EngridLogger } from "./logger";
20
+ export class ShowIfPresent {
21
+ constructor() {
22
+ this.logger = new EngridLogger("ShowIfPresent", "yellow", "black", "👀");
23
+ this.elements = [];
24
+ if (this.shouldRun()) {
25
+ this.run();
26
+ }
27
+ }
28
+ shouldRun() {
29
+ // Check if we have any elements on the page that match the pattern for this functionality
30
+ // e.g. engrid__supporterquestions{id}__supporterquestions{id}-present, etc.
31
+ this.elements = [
32
+ ...document.querySelectorAll('[class*="engrid__supporterquestions"]'),
33
+ ].filter((el) => {
34
+ const classNames = el.className.split(" ");
35
+ return classNames.some((className) => /^engrid__supporterquestions\d+(__supporterquestions\d+)*-(present|absent)$/.test(className));
36
+ });
37
+ return this.elements.length > 0;
38
+ }
39
+ run() {
40
+ const actions = [];
41
+ // Create an array of actions for each element we have
42
+ this.elements.forEach((el) => {
43
+ // Mapping to an object with the class name, field name(s), and type
44
+ const classNames = el.className.split(" ");
45
+ const matchingClass = classNames.find((className) => /^engrid__supporterquestions\d+(__supporterquestions\d+)*-(present|absent)$/.test(className));
46
+ if (!matchingClass)
47
+ return null;
48
+ const typeIndex = matchingClass.lastIndexOf("-");
49
+ const type = matchingClass.substring(typeIndex + 1);
50
+ // Getting an array of the matching input names
51
+ // e.g. engrid__supporterquestions12345-present => ['supporter.questions.12345']
52
+ // e.g. engrid__supporterquestions12345__supporterquestions67890-present => ['supporter.questions.12345', 'supporter.questions.67890']
53
+ const inputIds = matchingClass
54
+ .substring(8, typeIndex)
55
+ .split("__")
56
+ .map((id) => `supporter.questions.${id.substring(18)}`);
57
+ actions.push({
58
+ class: matchingClass,
59
+ fieldNames: inputIds,
60
+ type: type,
61
+ });
62
+ });
63
+ //Process the actions
64
+ actions.forEach((action) => {
65
+ const inputElements = action.fieldNames.map((fieldName) => document.getElementsByName(fieldName)[0]);
66
+ const elements = document.querySelectorAll(`.${action.class}`);
67
+ const areAllInputsPresent = inputElements.every((input) => !!input);
68
+ const areAllInputsAbsent = inputElements.every((input) => !input);
69
+ // Hide the elements based on AND conditions
70
+ if ((action.type === "present" && areAllInputsAbsent) ||
71
+ (action.type === "absent" && areAllInputsPresent)) {
72
+ this.logger.log(`Conditions not met, hiding elements with class ${action.class}`);
73
+ elements.forEach((el) => {
74
+ el.style.display = "none";
75
+ });
76
+ }
77
+ });
78
+ }
79
+ }
@@ -0,0 +1,4 @@
1
+ export declare class SkipToMainContentLink {
2
+ constructor();
3
+ private insertSkipLinkSpan;
4
+ }
@@ -0,0 +1,36 @@
1
+ // Javascript that adds an accessible "Skip Link" button after the <body> opening that jumps to
2
+ // the first <title> or <h1> field in a "body-" section, or the first <h1> if none are found
3
+ // in those sections
4
+ // Depends on _engrid-skip-link.scss
5
+ import { ENGrid } from ".";
6
+ export class SkipToMainContentLink {
7
+ constructor() {
8
+ const firstTitleInEngridBody = document.querySelector("div[class*='body-'] title");
9
+ const firstH1InEngridBody = document.querySelector("div[class*='body-'] h1");
10
+ const firstTitle = document.querySelector("title");
11
+ const firstH1 = document.querySelector("h1");
12
+ if (firstTitleInEngridBody && firstTitleInEngridBody.parentElement) {
13
+ firstTitleInEngridBody.parentElement.insertAdjacentHTML("beforebegin", '<span id="skip-link"></span>');
14
+ this.insertSkipLinkSpan();
15
+ }
16
+ else if (firstH1InEngridBody && firstH1InEngridBody.parentElement) {
17
+ firstH1InEngridBody.parentElement.insertAdjacentHTML("beforebegin", '<span id="skip-link"></span>');
18
+ this.insertSkipLinkSpan();
19
+ }
20
+ else if (firstTitle && firstTitle.parentElement) {
21
+ firstTitle.parentElement.insertAdjacentHTML("beforebegin", '<span id="skip-link"></span>');
22
+ this.insertSkipLinkSpan();
23
+ }
24
+ else if (firstH1 && firstH1.parentElement) {
25
+ firstH1.parentElement.insertAdjacentHTML("beforebegin", '<span id="skip-link"></span>');
26
+ this.insertSkipLinkSpan();
27
+ }
28
+ else {
29
+ if (ENGrid.debug)
30
+ console.log("This page contains no <title> or <h1> and a 'Skip to main content' link was not added");
31
+ }
32
+ }
33
+ insertSkipLinkSpan() {
34
+ document.body.insertAdjacentHTML("afterbegin", '<a class="skip-link" href="#skip-link">Skip to main content</a>');
35
+ }
36
+ }
@@ -0,0 +1,6 @@
1
+ export declare class SrcDefer {
2
+ imgSrcDefer: NodeListOf<Element>;
3
+ videoBackground: NodeListOf<Element>;
4
+ videoBackgroundSource: NodeListOf<Element>;
5
+ constructor();
6
+ }
@@ -0,0 +1,70 @@
1
+ // Build Notes: Add the vanilla Javascript version inline inside the page template right before </body>
2
+ // In the event the vanilla javascript is not inlined we should still process any assets with a data-src still defined on it. Plus we only process background video via this JS file as to not block the page with a large video file downloading.
3
+ // // 4Site's simplified image lazy loader
4
+ // var srcDefer = document.querySelectorAll("img[data-src]");
5
+ // window.addEventListener('DOMContentLoaded', (event) => {
6
+ // for (var i = 0; i < srcDefer.length; i++) {
7
+ // let dataSrc = srcDefer[i].getAttribute("data-src");
8
+ // if (dataSrc) {
9
+ // srcDefer[i].setAttribute("decoding", "async"); // Gets image processing off the main working thread
10
+ // srcDefer[i].setAttribute("loading", "lazy"); // Lets the browser determine when the asset should be downloaded
11
+ // srcDefer[i].setAttribute("src", dataSrc); // Sets the src which will cause the browser to retrieve the asset
12
+ // srcDefer[i].setAttribute("data-engrid-data-src-processed", "true"); // Sets an attribute to mark that it has been processed by ENGrid
13
+ // srcDefer[i].removeAttribute("data-src"); // Removes the data-source
14
+ // }
15
+ // }
16
+ // });
17
+ export class SrcDefer {
18
+ constructor() {
19
+ // Find all images and videos with a data-src defined
20
+ this.imgSrcDefer = document.querySelectorAll("img[data-src]");
21
+ this.videoBackground = document.querySelectorAll("video");
22
+ this.videoBackgroundSource = document.querySelectorAll("video source");
23
+ // Process images
24
+ for (let i = 0; i < this.imgSrcDefer.length; i++) {
25
+ let img = this.imgSrcDefer[i];
26
+ if (img) {
27
+ img.setAttribute("decoding", "async"); // Gets image processing off the main working thread, and decodes the image asynchronously to reduce delay in presenting other content
28
+ img.setAttribute("loading", "lazy"); // Lets the browser determine when the asset should be downloaded using it's native lazy loading
29
+ let imgDataSrc = img.getAttribute("data-src");
30
+ if (imgDataSrc) {
31
+ img.setAttribute("src", imgDataSrc); // Sets the src which will cause the browser to retrieve the asset
32
+ }
33
+ img.setAttribute("data-engrid-data-src-processed", "true"); // Sets an attribute to mark that it has been processed by ENGrid
34
+ img.removeAttribute("data-src"); // Removes the data-source
35
+ }
36
+ }
37
+ // Process video
38
+ for (let i = 0; i < this.videoBackground.length; i++) {
39
+ let video = this.videoBackground[i];
40
+ // Process one or more defined sources in the <video> tag
41
+ this.videoBackgroundSource = video.querySelectorAll("source");
42
+ if (this.videoBackgroundSource) {
43
+ // loop through all the sources
44
+ for (let j = 0; j < this.videoBackgroundSource.length; j++) {
45
+ let videoSource = this.videoBackgroundSource[j];
46
+ if (videoSource) {
47
+ let videoBackgroundSourcedDataSrc = videoSource.getAttribute("data-src");
48
+ if (videoBackgroundSourcedDataSrc) {
49
+ videoSource.setAttribute("src", videoBackgroundSourcedDataSrc);
50
+ videoSource.setAttribute("data-engrid-data-src-processed", "true"); // Sets an attribute to mark that it has been processed by ENGrid
51
+ videoSource.removeAttribute("data-src"); // Removes the data-source
52
+ }
53
+ }
54
+ }
55
+ // To get the browser to request the video asset defined we need to remove the <video> tag and re-add it
56
+ let videoBackgroundParent = video.parentNode; // Determine the parent of the <video> tag
57
+ let copyOfVideoBackground = video; // Copy the <video> tag
58
+ if (videoBackgroundParent && copyOfVideoBackground) {
59
+ videoBackgroundParent.replaceChild(copyOfVideoBackground, video); // Replace the <video> with the copy of itself
60
+ // Update the video to auto play, mute, loop
61
+ video.muted = true; // Mute the video by default
62
+ video.controls = false; // Hide the browser controls
63
+ video.loop = true; // Loop the video
64
+ video.playsInline = true; // Encourage the user agent to display video content within the element's playback area
65
+ video.play(); // Plays the video
66
+ }
67
+ }
68
+ }
69
+ }
70
+ }
@@ -0,0 +1,9 @@
1
+ export declare class SupporterHub {
2
+ private logger;
3
+ private _form;
4
+ constructor();
5
+ shoudRun(): boolean;
6
+ watch(): void;
7
+ creditCardUpdate(overlay: HTMLDivElement): void;
8
+ amountLabelUpdate(overlay: HTMLDivElement): void;
9
+ }
@@ -0,0 +1,75 @@
1
+ // Component that adds 4Site Special Features to the Supporter Hub Page
2
+ import { ENGrid, EngridLogger, EnForm } from ".";
3
+ export class SupporterHub {
4
+ constructor() {
5
+ this.logger = new EngridLogger("SupporterHub", "black", "pink", "🛖");
6
+ this._form = EnForm.getInstance();
7
+ if (!this.shoudRun())
8
+ return;
9
+ this.logger.log("Enabled");
10
+ this.watch();
11
+ }
12
+ shoudRun() {
13
+ return ("pageJson" in window &&
14
+ "pageType" in window.pageJson &&
15
+ window.pageJson.pageType === "supporterhub");
16
+ }
17
+ watch() {
18
+ const form = ENGrid.enForm;
19
+ // Create a observer to watch the Form for overlays
20
+ const observer = new MutationObserver((mutations) => {
21
+ mutations.forEach((mutation) => {
22
+ if (mutation.type === "childList") {
23
+ mutation.addedNodes.forEach((node) => {
24
+ if (node.nodeName === "DIV") {
25
+ const overlay = node;
26
+ if (overlay.classList.contains("en__hubOverlay") ||
27
+ overlay.classList.contains("en__hubPledge__panels")) {
28
+ this.logger.log("Overlay found");
29
+ this.creditCardUpdate(node);
30
+ this.amountLabelUpdate(node);
31
+ }
32
+ }
33
+ });
34
+ }
35
+ });
36
+ });
37
+ // Start observing the Link ID #plaid-link-button
38
+ observer.observe(form, {
39
+ childList: true,
40
+ subtree: true,
41
+ });
42
+ // Run the Credit Card Update function in case the overlay is already present on page load
43
+ const hubOverlay = document.querySelector(".en__hubOverlay");
44
+ if (hubOverlay) {
45
+ this.creditCardUpdate(hubOverlay);
46
+ this.amountLabelUpdate(hubOverlay);
47
+ }
48
+ }
49
+ creditCardUpdate(overlay) {
50
+ window.setTimeout(() => {
51
+ // Check if the overlay has Credit Card field and Update Button
52
+ const ccField = overlay.querySelector("#en__hubPledge__field--ccnumber"), updateButton = overlay.querySelector(".en__hubUpdateCC__toggle");
53
+ if (ccField && updateButton) {
54
+ // When field gets focus, click the update button
55
+ ccField.addEventListener("focus", () => {
56
+ this.logger.log("Credit Card field focused");
57
+ updateButton.click();
58
+ });
59
+ }
60
+ }, 300);
61
+ }
62
+ amountLabelUpdate(overlay) {
63
+ window.setTimeout(() => {
64
+ // Check if the overlay has Amounts, and set the currency symbol updated attribute
65
+ const amountContainer = overlay.querySelector(".en__field--donationAmt");
66
+ if (amountContainer) {
67
+ amountContainer
68
+ .querySelectorAll(".en__field__element--radio .en__field__item")
69
+ .forEach((node) => {
70
+ node.setAttribute("data-engrid-currency-symbol-updated", "true");
71
+ });
72
+ }
73
+ }, 300);
74
+ }
75
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Example:
3
+ * window.EngridAmounts = {
4
+ * "onetime": {
5
+ * amounts: {
6
+ * "10": 10,
7
+ * "30": 30,
8
+ * "50": 50,
9
+ * "100": 100,
10
+ * "Other": "other",
11
+ * },
12
+ * default: 30,
13
+ * },
14
+ * "monthly": {
15
+ * amounts: {
16
+ * "5": 5,
17
+ * "15": 15,
18
+ * "25": 25,
19
+ * "30": 30,
20
+ * "Other": "other",
21
+ * },
22
+ * default: 15,
23
+ * },
24
+ * };
25
+ */
26
+ import { DonationAmount } from ".";
27
+ export declare class SwapAmounts {
28
+ private logger;
29
+ _amount: DonationAmount;
30
+ private _frequency;
31
+ private defaultChange;
32
+ private swapped;
33
+ constructor();
34
+ swapAmounts(): void;
35
+ loadEnAmounts(amountArray: {
36
+ amounts: [string, number];
37
+ default: number;
38
+ }): {
39
+ selected: boolean;
40
+ label: string;
41
+ value: string;
42
+ }[];
43
+ shouldRun(): boolean;
44
+ ignoreCurrentValue(): boolean;
45
+ }