@4site/engrid-scripts 0.22.16 → 0.22.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.js +3 -1
- package/dist/checkbox-label.js +5 -5
- package/dist/custom-premium.d.ts +30 -0
- package/dist/custom-premium.js +264 -0
- package/dist/data-layer.js +26 -93
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/interfaces/options.d.ts +10 -0
- package/dist/interfaces/options.js +1 -0
- package/dist/swap-amounts.d.ts +2 -0
- package/dist/swap-amounts.js +6 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
package/dist/app.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DonationAmount, DonationFrequency, EnForm, ProcessingFees, Country, } from "./events";
|
|
2
|
-
import { AmountLabel, Loader, ProgressBar, UpsellLightbox, ENGrid, OptionsDefaults, setRecurrFreq, PageBackground, MediaAttribution, ApplePay, A11y, CapitalizeFields, Ecard, ClickToExpand, Advocacy, DataAttributes, LiveVariables, iFrame, InputPlaceholders, InputHasValueAndFocus, ShowHideRadioCheckboxes, AutoCountrySelect, SkipToMainContentLink, SrcDefer, NeverBounce, AutoYear, Autocomplete, RememberMe, TranslateFields, ShowIfAmount, EngridLogger, OtherAmount, MinMaxAmount, Ticker, DataReplace, DataHide, AddNameToMessage, ExpandRegionName, AppVersion, UrlToForm, RequiredIfVisible, TidyContact, DataLayer, LiveCurrency, Autosubmit, EventTickets, SwapAmounts, DebugPanel, DebugHiddenFields, FreshAddress, BrandingHtml, CountryDisable, PremiumGift, DigitalWallets, MobileCTA, LiveFrequency, UniversalOptIn, Plaid, GiveBySelect, UrlParamsToBodyAttrs, ExitIntentLightbox, SupporterHub, FastFormFill, SetAttr, ShowIfPresent, ENValidators, CustomCurrency, VGS, PostalCodeValidator, CountryRedirect, WelcomeBack, EcardToTarget, UsOnlyForm, ThankYouPageConditionalContent, EmbeddedEcard, CheckboxLabel, UpsellCheckbox, PostDonationEmbed, FrequencyUpsell, } from ".";
|
|
2
|
+
import { AmountLabel, Loader, ProgressBar, UpsellLightbox, ENGrid, OptionsDefaults, setRecurrFreq, PageBackground, MediaAttribution, ApplePay, A11y, CapitalizeFields, Ecard, ClickToExpand, Advocacy, DataAttributes, LiveVariables, iFrame, InputPlaceholders, InputHasValueAndFocus, ShowHideRadioCheckboxes, AutoCountrySelect, SkipToMainContentLink, SrcDefer, NeverBounce, AutoYear, Autocomplete, RememberMe, TranslateFields, ShowIfAmount, EngridLogger, OtherAmount, MinMaxAmount, Ticker, DataReplace, DataHide, AddNameToMessage, ExpandRegionName, AppVersion, UrlToForm, RequiredIfVisible, TidyContact, DataLayer, LiveCurrency, Autosubmit, EventTickets, SwapAmounts, DebugPanel, DebugHiddenFields, FreshAddress, BrandingHtml, CountryDisable, PremiumGift, DigitalWallets, MobileCTA, LiveFrequency, UniversalOptIn, Plaid, GiveBySelect, UrlParamsToBodyAttrs, ExitIntentLightbox, SupporterHub, FastFormFill, SetAttr, ShowIfPresent, ENValidators, CustomCurrency, VGS, PostalCodeValidator, CountryRedirect, WelcomeBack, EcardToTarget, UsOnlyForm, ThankYouPageConditionalContent, EmbeddedEcard, CheckboxLabel, UpsellCheckbox, PostDonationEmbed, FrequencyUpsell, CustomPremium, } from ".";
|
|
3
3
|
export class App extends ENGrid {
|
|
4
4
|
constructor(options) {
|
|
5
5
|
super();
|
|
@@ -246,6 +246,8 @@ export class App extends ENGrid {
|
|
|
246
246
|
new CountryDisable();
|
|
247
247
|
// Premium Gift Features
|
|
248
248
|
new PremiumGift();
|
|
249
|
+
// Custom Premium filtering (frequency/amount-based visibility)
|
|
250
|
+
new CustomPremium();
|
|
249
251
|
// Supporter Hub Features
|
|
250
252
|
new SupporterHub();
|
|
251
253
|
// Digital Wallets Features
|
package/dist/checkbox-label.js
CHANGED
|
@@ -16,15 +16,15 @@ export class CheckboxLabel {
|
|
|
16
16
|
}
|
|
17
17
|
run() {
|
|
18
18
|
this.checkBoxesLabels.forEach((checkboxLabel) => {
|
|
19
|
-
|
|
20
|
-
const labelText = (_a = checkboxLabel.textContent) === null || _a === void 0 ? void 0 : _a.trim();
|
|
19
|
+
const labelHTML = checkboxLabel.innerHTML.trim();
|
|
21
20
|
const checkboxContainer = checkboxLabel.nextElementSibling;
|
|
22
21
|
const checkboxLabelElement = checkboxContainer.querySelector("label:last-child");
|
|
23
|
-
if (!checkboxLabelElement || !
|
|
22
|
+
if (!checkboxLabelElement || !labelHTML)
|
|
24
23
|
return;
|
|
25
|
-
checkboxLabelElement.
|
|
24
|
+
checkboxLabelElement.innerHTML = `<div class="engrid-custom-checkbox-label">${labelHTML}</div>`;
|
|
25
|
+
// Remove the original label element
|
|
26
26
|
checkboxLabel.remove();
|
|
27
|
-
this.logger.log(`Set checkbox label to "${
|
|
27
|
+
this.logger.log(`Set checkbox label to "${labelHTML}"`);
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export declare class CustomPremium {
|
|
2
|
+
private logger;
|
|
3
|
+
private _amount;
|
|
4
|
+
private _frequency;
|
|
5
|
+
private _enForm;
|
|
6
|
+
private debounceTimer;
|
|
7
|
+
private stylesInjected;
|
|
8
|
+
private pendingFrequencyChange;
|
|
9
|
+
constructor();
|
|
10
|
+
private shouldRun;
|
|
11
|
+
private get config();
|
|
12
|
+
private get premiumContainer();
|
|
13
|
+
private get giftItems();
|
|
14
|
+
private getFrequencyConfig;
|
|
15
|
+
private getProductsMap;
|
|
16
|
+
private getConfiguredDefaultPid;
|
|
17
|
+
private injectStyles;
|
|
18
|
+
private startProcessingVisual;
|
|
19
|
+
private endProcessingVisual;
|
|
20
|
+
private scheduleRun;
|
|
21
|
+
private getCurrentFreq;
|
|
22
|
+
private getCurrentAmount;
|
|
23
|
+
private getAllowedProductIds;
|
|
24
|
+
private getProductId;
|
|
25
|
+
private showItem;
|
|
26
|
+
private selectByProductId;
|
|
27
|
+
private clearVariantField;
|
|
28
|
+
private hasVisiblePremiumItems;
|
|
29
|
+
private run;
|
|
30
|
+
}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
// ENgrid component: CustomPremium
|
|
2
|
+
// Filters premium gifts based on window.EngridPageOptions.CustomPremium configuration
|
|
3
|
+
// Rules:
|
|
4
|
+
// - Config shape: window.EngridPageOptions.CustomPremium[frequency][productId] = minimumAmount
|
|
5
|
+
// - On frequency or amount change, wait 500ms (allow EN to re-render), then:
|
|
6
|
+
// - Show only gifts whose minimumAmount <= current amount; hide others
|
|
7
|
+
// - If none visible, hide entire .en__component--premiumgiftblock
|
|
8
|
+
// - If current selection becomes invalid, select default; if default not visible, select "No Premium" and clear transaction.selprodvariantid
|
|
9
|
+
// - Run once 500ms after page load
|
|
10
|
+
// - Add EnForm onSubmit hook to clear transaction.selprodvariantid when no visible premium items
|
|
11
|
+
import { ENGrid, DonationAmount, DonationFrequency, EnForm, EngridLogger, } from ".";
|
|
12
|
+
export class CustomPremium {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.logger = new EngridLogger("CustomPremium", "teal", "white", "🧩");
|
|
15
|
+
this._amount = DonationAmount.getInstance();
|
|
16
|
+
this._frequency = DonationFrequency.getInstance();
|
|
17
|
+
this._enForm = EnForm.getInstance();
|
|
18
|
+
this.stylesInjected = false;
|
|
19
|
+
this.pendingFrequencyChange = false;
|
|
20
|
+
if (!this.shouldRun())
|
|
21
|
+
return;
|
|
22
|
+
this.injectStyles();
|
|
23
|
+
// Initial run: execute once after 500ms
|
|
24
|
+
window.setTimeout(() => this.run(), 500);
|
|
25
|
+
// On changes, schedule processing and fade out immediately
|
|
26
|
+
this._amount.onAmountChange.subscribe(() => this.scheduleRun());
|
|
27
|
+
this._frequency.onFrequencyChange.subscribe(() => {
|
|
28
|
+
this.pendingFrequencyChange = true;
|
|
29
|
+
this.scheduleRun();
|
|
30
|
+
});
|
|
31
|
+
// Clear hidden variant field on submit if there are no visible premium items
|
|
32
|
+
this._enForm.onSubmit.subscribe(() => {
|
|
33
|
+
if (!this.hasVisiblePremiumItems()) {
|
|
34
|
+
this.clearVariantField();
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
shouldRun() {
|
|
39
|
+
const isPremiumPage = "pageJson" in window &&
|
|
40
|
+
"pageType" in window.pageJson &&
|
|
41
|
+
window.pageJson.pageType === "premiumgift";
|
|
42
|
+
const hasConfig = !!ENGrid.getOption("CustomPremium");
|
|
43
|
+
return isPremiumPage && hasConfig;
|
|
44
|
+
}
|
|
45
|
+
get config() {
|
|
46
|
+
const cfg = ENGrid.getOption("CustomPremium");
|
|
47
|
+
return cfg || null;
|
|
48
|
+
}
|
|
49
|
+
get premiumContainer() {
|
|
50
|
+
return document.querySelector(".en__component--premiumgiftblock");
|
|
51
|
+
}
|
|
52
|
+
get giftItems() {
|
|
53
|
+
return Array.from(document.querySelectorAll(".en__pg"));
|
|
54
|
+
}
|
|
55
|
+
getFrequencyConfig(frequency) {
|
|
56
|
+
const customPremiumConfig = this.config;
|
|
57
|
+
if (!customPremiumConfig)
|
|
58
|
+
return null;
|
|
59
|
+
const frequencyConfig = customPremiumConfig[frequency];
|
|
60
|
+
if (frequencyConfig && typeof frequencyConfig === "object")
|
|
61
|
+
return frequencyConfig;
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
getProductsMap(frequency) {
|
|
65
|
+
const frequencyConfig = this.getFrequencyConfig(frequency);
|
|
66
|
+
const productsMap = {};
|
|
67
|
+
if (!frequencyConfig)
|
|
68
|
+
return productsMap;
|
|
69
|
+
// If explicit products object exists, use it
|
|
70
|
+
if (frequencyConfig.products &&
|
|
71
|
+
typeof frequencyConfig.products === "object") {
|
|
72
|
+
Object.entries(frequencyConfig.products).forEach(([productId, min]) => {
|
|
73
|
+
const id = String(productId);
|
|
74
|
+
const minAmount = Number(min);
|
|
75
|
+
if (!isNaN(minAmount))
|
|
76
|
+
productsMap[id] = minAmount;
|
|
77
|
+
});
|
|
78
|
+
return productsMap;
|
|
79
|
+
}
|
|
80
|
+
// Otherwise, treat own numeric-value keys as products, ignore 'default'
|
|
81
|
+
Object.entries(frequencyConfig).forEach(([key, value]) => {
|
|
82
|
+
if (key === "default")
|
|
83
|
+
return;
|
|
84
|
+
const minAmount = Number(value);
|
|
85
|
+
if (!isNaN(minAmount))
|
|
86
|
+
productsMap[String(key)] = minAmount;
|
|
87
|
+
});
|
|
88
|
+
return productsMap;
|
|
89
|
+
}
|
|
90
|
+
getConfiguredDefaultPid(frequency) {
|
|
91
|
+
const frequencyConfig = this.getFrequencyConfig(frequency);
|
|
92
|
+
if (!frequencyConfig)
|
|
93
|
+
return null;
|
|
94
|
+
const defaultValue = frequencyConfig.default;
|
|
95
|
+
if (defaultValue === undefined || defaultValue === null)
|
|
96
|
+
return "0"; // not set => No Premium by spec
|
|
97
|
+
const id = String(defaultValue);
|
|
98
|
+
return id;
|
|
99
|
+
}
|
|
100
|
+
injectStyles() {
|
|
101
|
+
if (this.stylesInjected)
|
|
102
|
+
return;
|
|
103
|
+
const id = "engrid-custom-premium-style";
|
|
104
|
+
if (document.getElementById(id)) {
|
|
105
|
+
this.stylesInjected = true;
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const style = document.createElement("style");
|
|
109
|
+
style.id = id;
|
|
110
|
+
style.innerHTML = `
|
|
111
|
+
.en__component--premiumgiftblock { transition: opacity 200ms ease-in-out; }
|
|
112
|
+
.en__component--premiumgiftblock.engrid-premium-processing { opacity: 0; pointer-events: none; }
|
|
113
|
+
.en__component--premiumgiftblock.engrid-premium-hidden { display: none !important; }
|
|
114
|
+
.en__component--premiumgiftblock.engrid-premium-ready { opacity: 1; }
|
|
115
|
+
`;
|
|
116
|
+
document.head.appendChild(style);
|
|
117
|
+
this.stylesInjected = true;
|
|
118
|
+
}
|
|
119
|
+
startProcessingVisual() {
|
|
120
|
+
const container = this.premiumContainer;
|
|
121
|
+
if (container) {
|
|
122
|
+
container.classList.add("engrid-premium-processing");
|
|
123
|
+
container.classList.remove("engrid-premium-ready");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
endProcessingVisual(hasVisible) {
|
|
127
|
+
const container = this.premiumContainer;
|
|
128
|
+
if (!container)
|
|
129
|
+
return;
|
|
130
|
+
container.classList.remove("engrid-premium-processing");
|
|
131
|
+
if (hasVisible) {
|
|
132
|
+
container.classList.remove("engrid-premium-hidden");
|
|
133
|
+
container.classList.add("engrid-premium-ready");
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
container.classList.add("engrid-premium-hidden");
|
|
137
|
+
container.classList.remove("engrid-premium-ready");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
scheduleRun() {
|
|
141
|
+
// Immediately fade out while we wait for EN to re-render
|
|
142
|
+
this.startProcessingVisual();
|
|
143
|
+
if (this.debounceTimer)
|
|
144
|
+
window.clearTimeout(this.debounceTimer);
|
|
145
|
+
this.debounceTimer = window.setTimeout(() => this.run(), 500);
|
|
146
|
+
}
|
|
147
|
+
getCurrentFreq() {
|
|
148
|
+
return (this._frequency.frequency || "onetime").toLowerCase();
|
|
149
|
+
}
|
|
150
|
+
getCurrentAmount() {
|
|
151
|
+
return this._amount.amount || 0;
|
|
152
|
+
}
|
|
153
|
+
getAllowedProductIds(freq, amount) {
|
|
154
|
+
const cfg = this.config;
|
|
155
|
+
const allowed = new Set();
|
|
156
|
+
if (!cfg)
|
|
157
|
+
return allowed;
|
|
158
|
+
const products = this.getProductsMap(freq);
|
|
159
|
+
Object.keys(products).forEach((pid) => {
|
|
160
|
+
const min = Number(products[pid]);
|
|
161
|
+
if (!isNaN(min) && amount >= min)
|
|
162
|
+
allowed.add(String(pid));
|
|
163
|
+
});
|
|
164
|
+
return allowed;
|
|
165
|
+
}
|
|
166
|
+
getProductId(item) {
|
|
167
|
+
const input = item.querySelector('input[name="en__pg"]');
|
|
168
|
+
return input ? input.value : null;
|
|
169
|
+
}
|
|
170
|
+
showItem(item, show) {
|
|
171
|
+
item.style.display = show ? "" : "none";
|
|
172
|
+
}
|
|
173
|
+
selectByProductId(productId) {
|
|
174
|
+
const radio = document.querySelector('input[name="en__pg"][value="' + productId + '"]');
|
|
175
|
+
if (radio) {
|
|
176
|
+
radio.checked = true;
|
|
177
|
+
radio.dispatchEvent(new Event("change", { bubbles: true, cancelable: true }));
|
|
178
|
+
// Update EN's selected class if necessary
|
|
179
|
+
const prev = document.querySelector(".en__pg--selected");
|
|
180
|
+
const pg = radio.closest(".en__pg");
|
|
181
|
+
if (prev && prev !== pg)
|
|
182
|
+
prev.classList.remove("en__pg--selected");
|
|
183
|
+
if (pg)
|
|
184
|
+
pg.classList.add("en__pg--selected");
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
clearVariantField() {
|
|
188
|
+
ENGrid.setFieldValue("transaction.selprodvariantid", "");
|
|
189
|
+
}
|
|
190
|
+
hasVisiblePremiumItems() {
|
|
191
|
+
// Exclude the "No Premium" (value 0) from count
|
|
192
|
+
return this.giftItems.some((item) => {
|
|
193
|
+
const pid = this.getProductId(item);
|
|
194
|
+
const visible = ENGrid.isVisible(item);
|
|
195
|
+
return visible && pid !== "0";
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
run() {
|
|
199
|
+
const container = this.premiumContainer;
|
|
200
|
+
if (!container)
|
|
201
|
+
return this.logger.log("No premium container found.");
|
|
202
|
+
const frequency = this.getCurrentFreq();
|
|
203
|
+
const amount = this.getCurrentAmount();
|
|
204
|
+
const allowedProductIds = this.getAllowedProductIds(frequency, amount);
|
|
205
|
+
// Iterate items and toggle visibility
|
|
206
|
+
let anyVisible = false;
|
|
207
|
+
const items = this.giftItems;
|
|
208
|
+
const noPremiumItems = [];
|
|
209
|
+
items.forEach((item) => {
|
|
210
|
+
const productId = this.getProductId(item);
|
|
211
|
+
if (!productId)
|
|
212
|
+
return;
|
|
213
|
+
if (productId === "0") {
|
|
214
|
+
// track no-premium items but don't decide visibility here — it's always available
|
|
215
|
+
noPremiumItems.push(item);
|
|
216
|
+
this.showItem(item, true);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const visible = allowedProductIds.has(productId);
|
|
220
|
+
this.showItem(item, visible);
|
|
221
|
+
if (visible)
|
|
222
|
+
anyVisible = true;
|
|
223
|
+
});
|
|
224
|
+
// If nothing visible (besides no-premium), hide whole container
|
|
225
|
+
const hasVisibleGifts = anyVisible;
|
|
226
|
+
this.endProcessingVisual(hasVisibleGifts);
|
|
227
|
+
// Selection handling
|
|
228
|
+
const current = document.querySelector('input[name="en__pg"]:checked');
|
|
229
|
+
const currentProductId = (current === null || current === void 0 ? void 0 : current.value) || null;
|
|
230
|
+
const defaultProductId = this.getConfiguredDefaultPid(frequency); // may be "0"
|
|
231
|
+
// If current selection is invalid after filtering, apply default logic
|
|
232
|
+
const currentIsValid = currentProductId === "0" ||
|
|
233
|
+
(currentProductId ? allowedProductIds.has(currentProductId) : false);
|
|
234
|
+
if (!currentIsValid) {
|
|
235
|
+
if (defaultProductId &&
|
|
236
|
+
defaultProductId !== "0" &&
|
|
237
|
+
allowedProductIds.has(defaultProductId)) {
|
|
238
|
+
this.selectByProductId(defaultProductId);
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
this.selectByProductId("0");
|
|
242
|
+
this.clearVariantField();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
// Current selection is valid; only force No Premium if frequency changed and default is 0/missing
|
|
247
|
+
if (this.pendingFrequencyChange &&
|
|
248
|
+
(!defaultProductId || defaultProductId === "0")) {
|
|
249
|
+
if (currentProductId !== "0") {
|
|
250
|
+
this.selectByProductId("0");
|
|
251
|
+
this.clearVariantField();
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// If container hidden (no visible gifts), select No Premium and clear hidden
|
|
256
|
+
if (!hasVisibleGifts) {
|
|
257
|
+
this.selectByProductId("0");
|
|
258
|
+
this.clearVariantField();
|
|
259
|
+
}
|
|
260
|
+
this.logger.log(`Processed gifts for freq=${frequency}, amount=${amount}. Visible gifts: ${hasVisibleGifts ? "yes" : "no"}`);
|
|
261
|
+
// Reset frequency-change flag after processing
|
|
262
|
+
this.pendingFrequencyChange = false;
|
|
263
|
+
}
|
|
264
|
+
}
|
package/dist/data-layer.js
CHANGED
|
@@ -1,25 +1,13 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
// - EN_FASTFORMFILL_PERSONALINFO_SUCCESS
|
|
12
|
-
// - EN_FASTFORMFILL_PERSONALINFO_PARTIALSUCCESS
|
|
13
|
-
// - EN_FASTFORMFILL_PERSONALINFO_FAILURE
|
|
14
|
-
// - EN_FASTFORMFILL_ADDRESS_SUCCESS
|
|
15
|
-
// - EN_FASTFORMFILL_ADDRESS_PARTIALSUCCESS
|
|
16
|
-
// - EN_FASTFORMFILL_ADDRESS_FAILURE
|
|
17
|
-
// - EN_FASTFORMFILL_ALL_SUCCESS
|
|
18
|
-
// - EN_FASTFORMFILL_ALL_FAILURE
|
|
19
|
-
// - EN_SUBMISSION_WITH_EMAIL_OPTIN
|
|
20
|
-
// - EN_SUBMISSION_WITHOUT_EMAIL_OPTIN
|
|
21
|
-
// - EN_FORM_VALUE_UPDATED
|
|
22
|
-
import { EngridLogger, ENGrid, EnForm, FastFormFill, RememberMeEvents, } from ".";
|
|
1
|
+
// DataLayer: singleton helper for pushing structured analytics events/vars to window.dataLayer.
|
|
2
|
+
// On load it emits one aggregated event `pageJsonVariablesReady` with:
|
|
3
|
+
// EN_PAGEJSON_* (normalized pageJson), EN_URLPARAM_*, EN_RECURRING_FREQUENCIES (donation pages),
|
|
4
|
+
// and EN_SUBMISSION_SUCCESS_{PAGETYPE} when on the final page.
|
|
5
|
+
// User actions emit: EN_FORM_VALUE_UPDATED (field changes) and submission opt‑in/out events.
|
|
6
|
+
// Queued end‑of‑gift events/variables (via addEndOfGiftProcessEvent / addEndOfGiftProcessVariable)
|
|
7
|
+
// are replayed after a successful gift process load.
|
|
8
|
+
// Sensitive payment/bank fields are excluded; selected PII fields are Base64 “hashed” (btoa — not cryptographic).
|
|
9
|
+
// Replace with a real hash (e.g., SHA‑256) if required.
|
|
10
|
+
import { EngridLogger, ENGrid, EnForm, RememberMeEvents } from ".";
|
|
23
11
|
export class DataLayer {
|
|
24
12
|
constructor() {
|
|
25
13
|
this.logger = new EngridLogger("DataLayer", "#f1e5bc", "#009cdc", "📊");
|
|
@@ -77,108 +65,53 @@ export class DataLayer {
|
|
|
77
65
|
}
|
|
78
66
|
transformJSON(value) {
|
|
79
67
|
if (typeof value === "string") {
|
|
80
|
-
return value
|
|
68
|
+
return value
|
|
69
|
+
.toUpperCase()
|
|
70
|
+
.trim()
|
|
71
|
+
.replace(/\s+/g, "-")
|
|
72
|
+
.replace(/:-/g, "-");
|
|
81
73
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
74
|
+
if (typeof value === "boolean") {
|
|
75
|
+
return value ? "TRUE" : "FALSE";
|
|
76
|
+
}
|
|
77
|
+
if (typeof value === "number") {
|
|
78
|
+
return value; // Preserve numeric type for analytics platforms that infer number vs string
|
|
85
79
|
}
|
|
86
80
|
return "";
|
|
87
81
|
}
|
|
88
82
|
onLoad() {
|
|
89
|
-
// Collect all data layer
|
|
83
|
+
// Collect all data layer variables to push at once
|
|
90
84
|
const dataLayerData = {};
|
|
91
|
-
const dataLayerEvents = [];
|
|
92
85
|
if (ENGrid.getGiftProcess()) {
|
|
93
86
|
this.logger.log("EN_SUCCESSFUL_DONATION");
|
|
94
|
-
dataLayerEvents.push("EN_SUCCESSFUL_DONATION");
|
|
95
87
|
this.addEndOfGiftProcessEventsToDataLayer();
|
|
96
88
|
}
|
|
97
|
-
else {
|
|
98
|
-
this.logger.log("EN_PAGE_VIEW");
|
|
99
|
-
dataLayerEvents.push("EN_PAGE_VIEW");
|
|
100
|
-
}
|
|
101
89
|
if (window.pageJson) {
|
|
102
90
|
const pageJson = window.pageJson;
|
|
103
91
|
for (const property in pageJson) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
pageJson[property];
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
dataLayerEvents.push(`EN_PAGEJSON_${property.toUpperCase()}-${this.transformJSON(pageJson[property])}`);
|
|
111
|
-
dataLayerData[`EN_PAGEJSON_${property.toUpperCase()}`] =
|
|
112
|
-
this.transformJSON(pageJson[property]);
|
|
113
|
-
}
|
|
114
|
-
dataLayerEvents.push("EN_PAGEJSON_" + property.toUpperCase());
|
|
115
|
-
dataLayerData.eventValue = pageJson[property];
|
|
92
|
+
const key = `EN_PAGEJSON_${property.toUpperCase()}`;
|
|
93
|
+
const value = pageJson[property];
|
|
94
|
+
dataLayerData[key] = this.transformJSON(value);
|
|
116
95
|
}
|
|
117
96
|
if (ENGrid.getPageCount() === ENGrid.getPageNumber()) {
|
|
118
|
-
dataLayerEvents.push("EN_SUBMISSION_SUCCESS_" + pageJson.pageType.toUpperCase());
|
|
119
97
|
dataLayerData[`EN_SUBMISSION_SUCCESS_${pageJson.pageType.toUpperCase()}`] = "TRUE";
|
|
120
98
|
}
|
|
121
99
|
}
|
|
122
100
|
const urlParams = new URLSearchParams(window.location.search);
|
|
123
101
|
urlParams.forEach((value, key) => {
|
|
124
|
-
dataLayerEvents.push(`EN_URLPARAM_${key.toUpperCase()}-${this.transformJSON(value)}`);
|
|
125
102
|
dataLayerData[`EN_URLPARAM_${key.toUpperCase()}`] =
|
|
126
103
|
this.transformJSON(value);
|
|
127
104
|
});
|
|
128
105
|
if (ENGrid.getPageType() === "DONATION") {
|
|
129
106
|
const recurrFreqEls = document.querySelectorAll('[name="transaction.recurrfreq"]');
|
|
130
107
|
const recurrValues = [...recurrFreqEls].map((el) => el.value);
|
|
131
|
-
|
|
132
|
-
dataLayerData[`'EN_RECURRING_FREQEUENCIES'`] = recurrValues;
|
|
133
|
-
}
|
|
134
|
-
let fastFormFill = false;
|
|
135
|
-
// Fast Form Fill - Personal Details
|
|
136
|
-
const fastPersonalDetailsFormBlock = document.querySelector(".en__component--formblock.fast-personal-details");
|
|
137
|
-
if (fastPersonalDetailsFormBlock) {
|
|
138
|
-
const allPersonalMandatoryInputsAreFilled = FastFormFill.allMandatoryInputsAreFilled(fastPersonalDetailsFormBlock);
|
|
139
|
-
const somePersonalMandatoryInputsAreFilled = FastFormFill.someMandatoryInputsAreFilled(fastPersonalDetailsFormBlock);
|
|
140
|
-
if (allPersonalMandatoryInputsAreFilled) {
|
|
141
|
-
dataLayerEvents.push("EN_FASTFORMFILL_PERSONALINFO_SUCCESS");
|
|
142
|
-
fastFormFill = true;
|
|
143
|
-
}
|
|
144
|
-
else if (somePersonalMandatoryInputsAreFilled) {
|
|
145
|
-
dataLayerEvents.push("EN_FASTFORMFILL_PERSONALINFO_PARTIALSUCCESS");
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
dataLayerEvents.push("EN_FASTFORMFILL_PERSONALINFO_FAILURE");
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
// Fast Form Fill - Address Details
|
|
152
|
-
const fastAddressDetailsFormBlock = document.querySelector(".en__component--formblock.fast-address-details");
|
|
153
|
-
if (fastAddressDetailsFormBlock) {
|
|
154
|
-
const allAddressMandatoryInputsAreFilled = FastFormFill.allMandatoryInputsAreFilled(fastAddressDetailsFormBlock);
|
|
155
|
-
const someAddressMandatoryInputsAreFilled = FastFormFill.someMandatoryInputsAreFilled(fastAddressDetailsFormBlock);
|
|
156
|
-
if (allAddressMandatoryInputsAreFilled) {
|
|
157
|
-
dataLayerEvents.push("EN_FASTFORMFILL_ADDRESS_SUCCESS");
|
|
158
|
-
fastFormFill = fastFormFill ? true : false; // Only set to true if it was true before
|
|
159
|
-
}
|
|
160
|
-
else if (someAddressMandatoryInputsAreFilled) {
|
|
161
|
-
dataLayerEvents.push("EN_FASTFORMFILL_ADDRESS_PARTIALSUCCESS");
|
|
162
|
-
}
|
|
163
|
-
else {
|
|
164
|
-
dataLayerEvents.push("EN_FASTFORMFILL_ADDRESS_FAILURE");
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
if (fastFormFill) {
|
|
168
|
-
dataLayerEvents.push("EN_FASTFORMFILL_ALL_SUCCESS");
|
|
169
|
-
}
|
|
170
|
-
else {
|
|
171
|
-
dataLayerEvents.push("EN_FASTFORMFILL_ALL_FAILURE");
|
|
108
|
+
dataLayerData[`EN_RECURRING_FREQUENCIES`] = recurrValues;
|
|
172
109
|
}
|
|
173
110
|
// Push all collected variables at once
|
|
174
111
|
if (Object.keys(dataLayerData).length > 0) {
|
|
175
112
|
dataLayerData.event = "pageJsonVariablesReady";
|
|
176
113
|
this.dataLayer.push(dataLayerData);
|
|
177
114
|
}
|
|
178
|
-
// Push all collected events individually (GTM requirement)
|
|
179
|
-
dataLayerEvents.forEach((event) => {
|
|
180
|
-
this.dataLayer.push({ event });
|
|
181
|
-
});
|
|
182
115
|
this.attachEventListeners();
|
|
183
116
|
}
|
|
184
117
|
onSubmit() {
|
|
@@ -266,7 +199,7 @@ export class DataLayer {
|
|
|
266
199
|
}
|
|
267
200
|
addEndOfGiftProcessVariable(variableName, variableValue = "") {
|
|
268
201
|
this.storeEndOfGiftProcessData({
|
|
269
|
-
[
|
|
202
|
+
[variableName.toUpperCase()]: variableValue,
|
|
270
203
|
});
|
|
271
204
|
}
|
|
272
205
|
storeEndOfGiftProcessData(data) {
|
package/dist/index.d.ts
CHANGED
|
@@ -58,6 +58,7 @@ export * from "./debug-hidden-fields";
|
|
|
58
58
|
export * from "./branding-html";
|
|
59
59
|
export * from "./country-disable";
|
|
60
60
|
export * from "./premium-gift";
|
|
61
|
+
export * from "./custom-premium";
|
|
61
62
|
export * from "./digital-wallets";
|
|
62
63
|
export * from "./mobile-cta";
|
|
63
64
|
export * from "./live-frequency";
|
package/dist/index.js
CHANGED
|
@@ -58,6 +58,7 @@ export * from "./debug-hidden-fields";
|
|
|
58
58
|
export * from "./branding-html";
|
|
59
59
|
export * from "./country-disable";
|
|
60
60
|
export * from "./premium-gift";
|
|
61
|
+
export * from "./custom-premium";
|
|
61
62
|
export * from "./digital-wallets";
|
|
62
63
|
export * from "./mobile-cta";
|
|
63
64
|
export * from "./live-frequency";
|
|
@@ -151,6 +151,16 @@ export interface Options {
|
|
|
151
151
|
placementQuerySelector?: string | null;
|
|
152
152
|
excludePageIDs?: string[];
|
|
153
153
|
};
|
|
154
|
+
CustomPremium?: false | {
|
|
155
|
+
[frequency: string]: ({
|
|
156
|
+
products?: {
|
|
157
|
+
[productId: string]: number;
|
|
158
|
+
};
|
|
159
|
+
default?: number | string;
|
|
160
|
+
} & {
|
|
161
|
+
[productId: string]: number;
|
|
162
|
+
});
|
|
163
|
+
};
|
|
154
164
|
onLoad?: () => void;
|
|
155
165
|
onResize?: () => void;
|
|
156
166
|
onSubmit?: () => void;
|
package/dist/swap-amounts.d.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* "Other": "other",
|
|
11
11
|
* },
|
|
12
12
|
* default: 30,
|
|
13
|
+
* stickyDefault: false, // Optional. When true, every swap forces the default amount to be (re)selected
|
|
13
14
|
* },
|
|
14
15
|
* "monthly": {
|
|
15
16
|
* amounts: {
|
|
@@ -20,6 +21,7 @@
|
|
|
20
21
|
* "Other": "other",
|
|
21
22
|
* },
|
|
22
23
|
* default: 15,
|
|
24
|
+
* stickyDefault: true, // Example forcing default on each frequency swap
|
|
23
25
|
* },
|
|
24
26
|
* };
|
|
25
27
|
*/
|
package/dist/swap-amounts.js
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* "Other": "other",
|
|
16
16
|
* },
|
|
17
17
|
* default: 30,
|
|
18
|
+
* stickyDefault: false, // Optional. When true, every swap forces the default amount to be (re)selected
|
|
18
19
|
* },
|
|
19
20
|
* "monthly": {
|
|
20
21
|
* amounts: {
|
|
@@ -25,6 +26,7 @@
|
|
|
25
26
|
* "Other": "other",
|
|
26
27
|
* },
|
|
27
28
|
* default: 15,
|
|
29
|
+
* stickyDefault: true, // Example forcing default on each frequency swap
|
|
28
30
|
* },
|
|
29
31
|
* };
|
|
30
32
|
*/
|
|
@@ -80,6 +82,7 @@ export class SwapAmounts {
|
|
|
80
82
|
const config = {
|
|
81
83
|
amounts: amountsObj,
|
|
82
84
|
default: defaultAmount,
|
|
85
|
+
// stickyDefault omitted so it defaults to false behavior
|
|
83
86
|
};
|
|
84
87
|
window.EngridAmounts = {
|
|
85
88
|
onetime: config,
|
|
@@ -95,7 +98,9 @@ export class SwapAmounts {
|
|
|
95
98
|
const config = configs[freq];
|
|
96
99
|
if (!config)
|
|
97
100
|
return;
|
|
98
|
-
const
|
|
101
|
+
const stickyDefault = !!config.stickyDefault;
|
|
102
|
+
// If stickyDefault, always ignore current value so selected flag in list enforces default
|
|
103
|
+
const ignoreCurrentValue = stickyDefault ? true : this.ignoreCurrentValue();
|
|
99
104
|
window.EngagingNetworks.require._defined.enjs.swapList("donationAmt", this.toEnAmountList(config), { ignoreCurrentValue });
|
|
100
105
|
this._amount.load();
|
|
101
106
|
this.logger.log("Amounts Swapped To", config, { ignoreCurrentValue });
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const AppVersion = "0.22.
|
|
1
|
+
export declare const AppVersion = "0.22.18";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const AppVersion = "0.22.
|
|
1
|
+
export const AppVersion = "0.22.18";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@4site/engrid-scripts",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.18",
|
|
4
4
|
"description": "Scripts for the ENGRID Project",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"strongly-typed-events": "^2.0.9"
|
|
26
26
|
},
|
|
27
27
|
"homepage": "https://github.com/4site-interactive-studios/engrid#readme",
|
|
28
|
-
"gitHead": "
|
|
28
|
+
"gitHead": "ae02b46d2b8cf00df293b08101b70c349c21c19c",
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"nodemon": "^2.0.20"
|
|
31
31
|
}
|