@4site/engrid-scripts 0.24.4 → 0.24.5

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 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, StripeFinancialConnections, GiveBySelect, UrlParamsToBodyAttrs, ExitIntentLightbox, SupporterHub, FastFormFill, SetAttr, ShowIfPresent, ENValidators, CustomCurrency, VGS, PostalCodeValidator, CountryRedirect, WelcomeBack, EcardToTarget, UsOnlyForm, ThankYouPageConditionalContent, EmbeddedEcard, CheckboxLabel, UpsellCheckbox, PostDonationEmbed, FrequencyUpsell, CustomPremium, PreferredPaymentMethod, } 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, StripeFinancialConnections, GiveBySelect, UrlParamsToBodyAttrs, ExitIntentLightbox, SupporterHub, FastFormFill, SetAttr, ShowIfPresent, ENValidators, CustomCurrency, VGS, PostalCodeValidator, CountryRedirect, WelcomeBack, EcardToTarget, UsOnlyForm, ThankYouPageConditionalContent, EmbeddedEcard, CheckboxLabel, UpsellCheckbox, PostDonationEmbed, FrequencyUpsell, CustomPremium, StickyNSG, StickyPrepopulation, PreferredPaymentMethod, } from ".";
3
3
  export class App extends ENGrid {
4
4
  constructor(options) {
5
5
  super();
@@ -160,6 +160,8 @@ export class App extends ENGrid {
160
160
  new Autosubmit();
161
161
  // Adjust display of event tickets.
162
162
  new EventTickets();
163
+ // StickyNSG - Must load before SwapAmounts
164
+ new StickyNSG();
163
165
  // Swap Amounts
164
166
  new SwapAmounts();
165
167
  // On the end of the script, after all subscribers defined, let's load the current frequency
@@ -279,6 +281,7 @@ export class App extends ENGrid {
279
281
  new CheckboxLabel();
280
282
  new PostDonationEmbed();
281
283
  new FrequencyUpsell();
284
+ new StickyPrepopulation();
282
285
  //Debug panel
283
286
  let showDebugPanel = this.options.Debug;
284
287
  try {
@@ -16,10 +16,15 @@ export class AutoCountrySelect {
16
16
  ENGrid.getUrlParameter("supporter.region") ||
17
17
  (ENGrid.getUrlParameter("ea.url.id") &&
18
18
  !ENGrid.getUrlParameter("forwarded"));
19
+ // If fast form is active, then personal details have already been filled somehow and we should not override the country selection
20
+ // The client is also likely using WelcomeBack.
21
+ const fastFormActive = ENGrid.getBodyData("hide-fast-address-details") ||
22
+ ENGrid.getBodyData("hide-fast-personal-details");
19
23
  if (!engridAutofill &&
20
24
  !submissionFailed &&
21
25
  hasIntlSupport &&
22
- !locationDataInUrl) {
26
+ !locationDataInUrl &&
27
+ !fastFormActive) {
23
28
  fetch(`https://${window.location.hostname}/cdn-cgi/trace`)
24
29
  .then((res) => res.text())
25
30
  .then((t) => {
@@ -5,6 +5,7 @@ export declare class DataLayer {
5
5
  private static instance;
6
6
  private encoder;
7
7
  private endOfGiftProcessStorageKey;
8
+ private giftFields;
8
9
  private excludedFields;
9
10
  private hashedFields;
10
11
  private retainedEmailField;
@@ -24,6 +24,19 @@ export class DataLayer {
24
24
  this._form = EnForm.getInstance();
25
25
  this.encoder = new TextEncoder();
26
26
  this.endOfGiftProcessStorageKey = "ENGRID_END_OF_GIFT_PROCESS_EVENTS";
27
+ // pageJson entries related to the gift process
28
+ this.giftFields = [
29
+ "amount",
30
+ "currency",
31
+ "donationLogId",
32
+ "feeCover",
33
+ "giftProcess",
34
+ "paymentType",
35
+ "receiptNumber",
36
+ "recurring",
37
+ "transactionId",
38
+ "transactionType",
39
+ ];
27
40
  this.excludedFields = [
28
41
  // Credit Card
29
42
  "transaction.ccnumber",
@@ -102,13 +115,27 @@ export class DataLayer {
102
115
  onLoad() {
103
116
  // Collect all data layer variables to push at once
104
117
  const dataLayerData = {};
118
+ const suppressEcardData = ENGrid.getPageType() === "ECARD" &&
119
+ ENGrid.getOption("SuppressPurchaseEcard");
105
120
  if (ENGrid.getGiftProcess()) {
106
- this.logger.log("EN_SUCCESSFUL_DONATION");
107
- this.addEndOfGiftProcessEventsToDataLayer();
121
+ // EN will chain together gift process data on the page json when redirecting from a completed donation to an ecard.
122
+ // Since the ecard page can be embedded on the thank you page of a donation, this can cause confusion in the data layer with events
123
+ // firing for both the donation and the ecard on the same page.
124
+ if (suppressEcardData) {
125
+ this.logger.log("⛔ Gift process was detected BUT suppressing EN_SUCCESSFUL_DONATION event due to SuppressPurchaseEcard option enabled");
126
+ window.sessionStorage.removeItem(this.endOfGiftProcessStorageKey);
127
+ }
128
+ else {
129
+ this.logger.log("EN_SUCCESSFUL_DONATION");
130
+ this.addEndOfGiftProcessEventsToDataLayer();
131
+ }
108
132
  }
109
133
  if (window.pageJson) {
110
134
  const pageJson = window.pageJson;
111
135
  for (const property in pageJson) {
136
+ if (suppressEcardData && this.giftFields.includes(property)) {
137
+ continue;
138
+ }
112
139
  const key = `EN_PAGEJSON_${property.toUpperCase()}`;
113
140
  const value = pageJson[property];
114
141
  dataLayerData[key] = this.transformJSON(value);
package/dist/index.d.ts CHANGED
@@ -86,6 +86,8 @@ export * from "./optin-ladder";
86
86
  export * from "./post-donation-embed";
87
87
  export * from "./frequency-upsell-modal";
88
88
  export * from "./frequency-upsell";
89
+ export * from "./sticky-nsg";
90
+ export * from "./sticky-prepopulation";
89
91
  export * from "./preferred-payment-method";
90
92
  export * from "./events";
91
93
  export * from "./version";
package/dist/index.js CHANGED
@@ -86,6 +86,8 @@ export * from "./optin-ladder";
86
86
  export * from "./post-donation-embed";
87
87
  export * from "./frequency-upsell-modal";
88
88
  export * from "./frequency-upsell";
89
+ export * from "./sticky-nsg";
90
+ export * from "./sticky-prepopulation";
89
91
  export * from "./preferred-payment-method";
90
92
  // Events
91
93
  export * from "./events";
@@ -18,6 +18,7 @@ export interface Options {
18
18
  UseAmountValidatorFromEN?: boolean;
19
19
  SkipToMainContentLink?: boolean;
20
20
  SrcDefer?: boolean;
21
+ SuppressPurchaseEcard?: boolean;
21
22
  NeverBounceAPI?: string | null;
22
23
  NeverBounceDateField?: string | null;
23
24
  NeverBounceDateFormat?: string;
@@ -50,6 +51,10 @@ export interface Options {
50
51
  fieldClearSelectorTargetLocation?: string;
51
52
  checked?: boolean;
52
53
  };
54
+ StickyNSG?: boolean;
55
+ StickyPrepopulation?: false | {
56
+ fields: string[];
57
+ };
53
58
  TidyContact?: false | {
54
59
  cid?: string;
55
60
  page_types?: ("DONATION" | "ECARD" | "SURVEY" | "EMAILTOTARGET" | "ADVOCACY" | "SUBSCRIBEFORM" | "EVENT" | "SUPPORTERHUB" | "UNSUBSCRIBE" | "TWEETPAGE" | "UNKNOWN")[];
@@ -17,6 +17,7 @@ export const OptionsDefaults = {
17
17
  UseAmountValidatorFromEN: false,
18
18
  SkipToMainContentLink: true,
19
19
  SrcDefer: true,
20
+ SuppressPurchaseEcard: false,
20
21
  NeverBounceAPI: null,
21
22
  NeverBounceDateField: null,
22
23
  NeverBounceStatusField: null,
@@ -41,6 +42,8 @@ export const OptionsDefaults = {
41
42
  CountryRedirect: false,
42
43
  WelcomeBack: false,
43
44
  OptInLadder: false,
45
+ StickyNSG: false,
46
+ StickyPrepopulation: false,
44
47
  PreferredPaymentMethod: false,
45
48
  PageLayouts: [
46
49
  "leftleft1col",
@@ -7,9 +7,10 @@ export declare class OptInLadder {
7
7
  private runAsChildThankYou;
8
8
  private inIframe;
9
9
  private saveStepToSessionStorage;
10
- private getFirstPageUrl;
11
10
  private saveOptInsToSessionStorage;
12
11
  private isEmbeddedThankYouPage;
12
+ private getPageUrl;
13
+ private getFirstPageUrl;
13
14
  private hidePage;
14
15
  private clearSessionStorage;
15
16
  }
@@ -1,8 +1,11 @@
1
- // This component is responsible for showing a ladder of checkboxes, one at a time, to the user.
2
- // If the page is not embedded in an iframe, and there are EN's Opt-In fields on the page, we will store the values to sessionStorage upon Form Submit.
3
- // If the page is embedded in an iframe and on a Thank You Page, we will look for .optin-ladder elements, compare the values to sessionStorage, and show the next checkbox in the ladder, removing all but the first match.
4
- // If the page is embedded in an iframe and on a Thank You Page, and the child iFrame is also a Thank You Page, we will look for a sessionStorage that has the current ladder step and the total number of steps.
5
- // If the current step is less than the total number of steps, we will redirect to the first page. If the current step is equal to the total number of steps, we will show the Thank You Page.
1
+ /**
2
+ * Docs: https://engrid.4sitestudios.com/component/optin-ladder
3
+ * This component is responsible for showing a ladder of checkboxes, one at a time, to the user.
4
+ * If the page is not embedded in an iframe, and there are EN's Opt-In fields on the page, we will store the values to sessionStorage upon Form Submit.
5
+ * If the page is embedded in an iframe and on a Thank You Page, we will look for .optin-ladder elements, compare the values to sessionStorage, and show the next checkbox in the ladder, removing all but the first match.
6
+ * If the page is embedded in an iframe and on a Thank You Page, and the child iFrame is also a Thank You Page, we will look for a sessionStorage that has the current ladder step and the total number of steps.
7
+ * If the current step is less than the total number of steps, we will redirect to the first page. If the current step is equal to the total number of steps, we will show the Thank You Page.
8
+ */
6
9
  import { EngridLogger, ENGrid, EnForm } from ".";
7
10
  export class OptInLadder {
8
11
  constructor() {
@@ -95,7 +98,7 @@ export class OptInLadder {
95
98
  if (!emailField || !emailField.value) {
96
99
  this.logger.log("Email field is empty");
97
100
  // Since this is a OptInLadder page with no e-mail address, hide the page
98
- this.hidePage();
101
+ this.hidePage(true);
99
102
  return;
100
103
  }
101
104
  const sessionStorageCheckboxValues = JSON.parse(sessionStorage.getItem("engrid.supporter.questions") || "{}");
@@ -179,6 +182,12 @@ export class OptInLadder {
179
182
  return;
180
183
  }
181
184
  const hasOptInLadderStop = sessionStorage.getItem("engrid.optin-ladder-stop");
185
+ const hasOptInLadderPersistStop = sessionStorage.getItem("engrid.optin-ladder-persist-stop");
186
+ if (hasOptInLadderPersistStop) {
187
+ this.logger.log("OptInLadder has been stopped with persist flag, showing the thank-you page");
188
+ sessionStorage.removeItem("engrid.optin-ladder-persist-stop");
189
+ return;
190
+ }
182
191
  if (hasOptInLadderStop) {
183
192
  this.logger.log("OptInLadder has been stopped");
184
193
  return;
@@ -186,9 +195,14 @@ export class OptInLadder {
186
195
  const sessionStorageOptInLadder = JSON.parse(sessionStorage.getItem("engrid.optin-ladder") || "{}");
187
196
  const currentStep = sessionStorageOptInLadder.step || 0;
188
197
  const totalSteps = sessionStorageOptInLadder.totalSteps || 0;
189
- if (currentStep <= totalSteps) {
190
- this.logger.log(`Current step ${currentStep} is less or equal to total steps ${totalSteps}`);
198
+ if (totalSteps === 0) {
199
+ this.logger.log("No total steps found in sessionStorage");
191
200
  this.hidePage();
201
+ return;
202
+ }
203
+ else if (currentStep <= totalSteps) {
204
+ this.logger.log(`Current step ${currentStep} is less or equal to total steps ${totalSteps}`);
205
+ this.hidePage(true);
192
206
  // Redirect to the first page
193
207
  window.location.href = this.getFirstPageUrl();
194
208
  return;
@@ -211,14 +225,6 @@ export class OptInLadder {
211
225
  sessionStorage.setItem("engrid.optin-ladder", JSON.stringify({ step, totalSteps }));
212
226
  this.logger.log(`Saved step ${step} of ${totalSteps} to sessionStorage`);
213
227
  }
214
- getFirstPageUrl() {
215
- // Get the current URL and replace the last path with 1?chain
216
- const url = new URL(window.location.href);
217
- const path = url.pathname.split("/");
218
- path.pop();
219
- path.push("1");
220
- return url.origin + path.join("/") + "?chain";
221
- }
222
228
  saveOptInsToSessionStorage(type = "parent") {
223
229
  // Grab all the checkboxes with the name starting with "supporter.questions"
224
230
  const checkboxes = document.querySelectorAll('input[name^="supporter.questions"]');
@@ -248,15 +254,32 @@ export class OptInLadder {
248
254
  isEmbeddedThankYouPage() {
249
255
  return ENGrid.getBodyData("embedded") === "thank-you-page-donation";
250
256
  }
251
- hidePage() {
252
- const engridPage = document.querySelector("#engrid");
253
- if (engridPage) {
254
- engridPage.classList.add("hide");
257
+ getPageUrl(page, chain = false) {
258
+ const url = new URL(window.location.href);
259
+ const path = url.pathname.split("/");
260
+ path[path.length - 1] = String(page);
261
+ return url.origin + path.join("/") + (chain ? "?chain" : "");
262
+ }
263
+ getFirstPageUrl() {
264
+ return this.getPageUrl(1, true);
265
+ }
266
+ hidePage(forceHide = false) {
267
+ if (ENGrid.getBodyData("opt-in-ladder-persist") === "true" && !forceHide) {
268
+ this.logger.log("Hide activated, but opt-in ladder persist is enabled, showing the thank-you page");
269
+ sessionStorage.setItem("engrid.optin-ladder-persist-stop", "Y");
270
+ window.location.href = this.getPageUrl(2);
271
+ }
272
+ else {
273
+ const engridPage = document.querySelector("#engrid");
274
+ if (engridPage) {
275
+ engridPage.classList.add("hide");
276
+ }
255
277
  }
256
278
  }
257
279
  clearSessionStorage() {
258
280
  sessionStorage.removeItem("engrid.supporter.questions");
259
281
  sessionStorage.removeItem("engrid.optin-ladder");
260
282
  sessionStorage.removeItem("engrid.optin-ladder-stop");
283
+ sessionStorage.removeItem("engrid.optin-ladder-persist-stop");
261
284
  }
262
285
  }
@@ -0,0 +1,10 @@
1
+ export declare class StickyNSG {
2
+ private logger;
3
+ private cookieName;
4
+ constructor();
5
+ private shouldRun;
6
+ private nsgActiveOnPage;
7
+ private deleteCookieIfGiftProcessComplete;
8
+ private createStickyNSGCookie;
9
+ private applyStickyNSGCookie;
10
+ }
@@ -0,0 +1,109 @@
1
+ import { EngridLogger } from "./logger";
2
+ import { ENGrid } from "./engrid";
3
+ import * as cookie from "./cookie";
4
+ export class StickyNSG {
5
+ constructor() {
6
+ this.logger = new EngridLogger("StickyNSG", "teal", "white", "📌");
7
+ this.cookieName = "engrid-sticky-nsg";
8
+ if (!this.shouldRun())
9
+ return;
10
+ this.logger.log("Sticky NSG is enabled");
11
+ this.deleteCookieIfGiftProcessComplete();
12
+ this.createStickyNSGCookie();
13
+ this.applyStickyNSGCookie();
14
+ }
15
+ shouldRun() {
16
+ return ENGrid.getOption("StickyNSG") === true;
17
+ }
18
+ /*
19
+ * Determine if NSG provided by EN is active on the page
20
+ */
21
+ nsgActiveOnPage() {
22
+ return (window.EngagingNetworks &&
23
+ window.EngagingNetworks.suggestedGift &&
24
+ typeof window.EngagingNetworks.suggestedGift === "object" &&
25
+ Object.keys(window.EngagingNetworks.suggestedGift).length > 0);
26
+ }
27
+ /*
28
+ * Delete the cookie if the gift process is complete
29
+ */
30
+ deleteCookieIfGiftProcessComplete() {
31
+ if (ENGrid.getGiftProcess()) {
32
+ this.logger.log("Gift process complete, removing sticky NSG cookie if it exists");
33
+ cookie.remove(this.cookieName);
34
+ }
35
+ }
36
+ /*
37
+ * Create the sticky NSG cookie if NSG is active on the page
38
+ */
39
+ createStickyNSGCookie() {
40
+ var _a, _b, _c, _d, _e, _f;
41
+ if (!this.nsgActiveOnPage()) {
42
+ this.logger.log("No NSG active on page, not creating sticky NSG cookie");
43
+ return;
44
+ }
45
+ const url = new URL(window.location.href);
46
+ if (url.searchParams.get("skipstickynsg") === "true") {
47
+ this.logger.log("'skipstickynsg' param present, not creating sticky NSG cookie");
48
+ return;
49
+ }
50
+ // We do some reformating to match the EngridAmounts format
51
+ // We also add "Other" to the amounts list
52
+ const nsg = window.EngagingNetworks.suggestedGift;
53
+ this.logger.log("Creating sticky NSG cookie", nsg);
54
+ const oneTimeNsg = (_a = nsg.single) === null || _a === void 0 ? void 0 : _a.reduce((acc, curr) => {
55
+ acc[curr.value] = curr.value;
56
+ return acc;
57
+ }, {});
58
+ const oneTimeDefault = (_c = (_b = nsg.single) === null || _b === void 0 ? void 0 : _b.find((gift) => gift.nextSuggestedGift)) === null || _c === void 0 ? void 0 : _c.value;
59
+ const recurringNsg = (_d = nsg.recurring) === null || _d === void 0 ? void 0 : _d.reduce((acc, curr) => {
60
+ acc[curr.value] = curr.value;
61
+ return acc;
62
+ }, {});
63
+ const recurringDefault = (_f = (_e = nsg.recurring) === null || _e === void 0 ? void 0 : _e.find((gift) => gift.nextSuggestedGift)) === null || _f === void 0 ? void 0 : _f.value;
64
+ const nsgCookieData = {};
65
+ if (oneTimeNsg && oneTimeDefault) {
66
+ nsgCookieData.onetime = {
67
+ amounts: oneTimeNsg,
68
+ default: oneTimeDefault,
69
+ stickyDefault: false,
70
+ };
71
+ }
72
+ if (recurringNsg && recurringDefault) {
73
+ nsgCookieData.monthly = {
74
+ amounts: recurringNsg,
75
+ default: recurringDefault,
76
+ stickyDefault: false,
77
+ };
78
+ }
79
+ if (Object.keys(nsgCookieData).length === 0) {
80
+ this.logger.log("No valid NSG data found to create sticky NSG cookie");
81
+ return;
82
+ }
83
+ const cookieValue = JSON.stringify(nsgCookieData);
84
+ cookie.set(this.cookieName, cookieValue, { path: "/", expires: 30 });
85
+ this.logger.log("Sticky NSG cookie created", cookieValue);
86
+ }
87
+ /*
88
+ * Apply the sticky NSG cookie values to window.EngridAmounts if NSG is not active on the page
89
+ */
90
+ applyStickyNSGCookie() {
91
+ if (this.nsgActiveOnPage()) {
92
+ this.logger.log("NSG active on page, not applying sticky NSG cookie, leaving the EN NSG values.");
93
+ return;
94
+ }
95
+ const cookieValue = cookie.get(this.cookieName);
96
+ if (!cookieValue) {
97
+ this.logger.log("No sticky NSG cookie found, nothing to apply");
98
+ return;
99
+ }
100
+ try {
101
+ const nsg = JSON.parse(cookieValue);
102
+ this.logger.log("Applying sticky NSG cookie values", nsg);
103
+ window.EngridAmounts = nsg;
104
+ }
105
+ catch (e) {
106
+ this.logger.error("Error parsing sticky NSG cookie, not applying", e);
107
+ }
108
+ }
109
+ }
@@ -0,0 +1,17 @@
1
+ export declare class StickyPrepopulation {
2
+ private logger;
3
+ private options;
4
+ private cookieName;
5
+ constructor();
6
+ private shouldRun;
7
+ private deleteCookie;
8
+ private createCookie;
9
+ private applyPrepopulation;
10
+ private getSupporterDetailsFromFields;
11
+ private encryptSupporterDetails;
12
+ private decryptSupporterDetails;
13
+ private createEncryptionKey;
14
+ private arrayBufferToBase64;
15
+ private base64ToArrayBuffer;
16
+ private getSeed;
17
+ }
@@ -0,0 +1,214 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { ENGrid } from "./engrid";
11
+ import { EngridLogger } from "./logger";
12
+ import * as cookie from "./cookie";
13
+ export class StickyPrepopulation {
14
+ constructor() {
15
+ this.logger = new EngridLogger("StickyPrepopulation", "teal", "white", "📌");
16
+ this.options = { fields: [] };
17
+ this.cookieName = "engrid-sticky-prepop";
18
+ if (!this.shouldRun()) {
19
+ return;
20
+ }
21
+ this.logger.log("StickyPrepopulation initialized");
22
+ if (ENGrid.getGiftProcess()) {
23
+ this.deleteCookie();
24
+ return;
25
+ }
26
+ if (ENGrid.getPageNumber() !== 1 || !ENGrid.getField("supporter.emailAddress")) {
27
+ this.logger.log("Not on page 1 or email field not present, not creating cookie or applying pre-population.");
28
+ return;
29
+ }
30
+ this.createCookie();
31
+ this.applyPrepopulation();
32
+ }
33
+ /*
34
+ * Determine if we should run the script
35
+ * Do not run if RememberMe is active
36
+ * Do not run if on a chain link
37
+ * Only run if StickyPrepopulation option is set with fields
38
+ */
39
+ shouldRun() {
40
+ if (ENGrid.getOption("RememberMe")) {
41
+ return false;
42
+ }
43
+ const url = new URL(window.location.href);
44
+ const options = ENGrid.getOption("StickyPrepopulation");
45
+ if (options && (options === null || options === void 0 ? void 0 : options.fields.length) > 0 && !url.searchParams.has("chain")) {
46
+ this.options = options;
47
+ return true;
48
+ }
49
+ else {
50
+ return false;
51
+ }
52
+ }
53
+ /*
54
+ * Delete the cookie if the gift process is complete
55
+ */
56
+ deleteCookie() {
57
+ this.logger.log("Gift process complete, removing sticky prepopulation cookie if it exists");
58
+ cookie.remove(this.cookieName);
59
+ }
60
+ /*
61
+ * Create the cookie if we're coming from a campaign link and supporterId is present
62
+ */
63
+ createCookie() {
64
+ var _a;
65
+ return __awaiter(this, void 0, void 0, function* () {
66
+ // If we're not coming from a campaign link, don't create the cookie
67
+ if (!((_a = window.pageJson) === null || _a === void 0 ? void 0 : _a.supporterId)) {
68
+ this.logger.log("No supporterId present, not creating sticky prepopulation cookie");
69
+ return;
70
+ }
71
+ try {
72
+ const encryptedSupporterDetails = yield this.encryptSupporterDetails(this.getSupporterDetailsFromFields());
73
+ cookie.set(this.cookieName, window.btoa(JSON.stringify({
74
+ encryptedData: encryptedSupporterDetails.encryptedData,
75
+ iv: encryptedSupporterDetails.iv,
76
+ pageId: ENGrid.getPageID()
77
+ })), { path: "/", expires: 7 });
78
+ }
79
+ catch (e) {
80
+ this.logger.log("Error creating sticky prepopulation cookie");
81
+ return;
82
+ }
83
+ this.logger.log("Sticky prepopulation cookie created");
84
+ });
85
+ }
86
+ /*
87
+ * If the cookie is present and supporterId is not (it's not a campaign link prefilled by EN),
88
+ * then apply the prepopulation
89
+ */
90
+ applyPrepopulation() {
91
+ var _a;
92
+ return __awaiter(this, void 0, void 0, function* () {
93
+ const cookieData = cookie.get(this.cookieName);
94
+ if (!cookieData) {
95
+ this.logger.log("No sticky prepopulation cookie found, not prepopulating fields");
96
+ return;
97
+ }
98
+ if ((_a = window.pageJson) === null || _a === void 0 ? void 0 : _a.supporterId) {
99
+ this.logger.log("SupporterId present, not applying sticky prepopulation");
100
+ return;
101
+ }
102
+ let supporterDetails = {};
103
+ try {
104
+ const encryptedSupporterDetails = JSON.parse(window.atob(cookieData));
105
+ if (!encryptedSupporterDetails || (encryptedSupporterDetails === null || encryptedSupporterDetails === void 0 ? void 0 : encryptedSupporterDetails.pageId) !== ENGrid.getPageID()) {
106
+ this.logger.log("No encrypted supporter details found in cookie, or page ID does not match");
107
+ return;
108
+ }
109
+ supporterDetails = JSON.parse(yield this.decryptSupporterDetails(this.base64ToArrayBuffer(encryptedSupporterDetails.encryptedData), new Uint8Array(this.base64ToArrayBuffer(encryptedSupporterDetails.iv))));
110
+ }
111
+ catch (e) {
112
+ this.logger.log("Error decrypting supporter details from cookie");
113
+ return;
114
+ }
115
+ this.options.fields.forEach((fieldName) => {
116
+ if (!supporterDetails[fieldName])
117
+ return;
118
+ ENGrid.setFieldValue(fieldName, decodeURIComponent(supporterDetails[fieldName]));
119
+ this.logger.log(`Setting "${fieldName}" to "${decodeURIComponent(supporterDetails[fieldName])}"`);
120
+ });
121
+ });
122
+ }
123
+ /*
124
+ * Get the supporter details from the form fields
125
+ */
126
+ getSupporterDetailsFromFields() {
127
+ const supporterDetails = {};
128
+ this.options.fields.forEach((fieldName) => {
129
+ let field = document.querySelector(`[name="${fieldName}"]`);
130
+ // If it is a radio or checkbox, get the checked value
131
+ if (field) {
132
+ if (field.type === "radio" || field.type === "checkbox") {
133
+ field = document.querySelector(`[name="${fieldName}"]:checked`);
134
+ }
135
+ supporterDetails[fieldName] = encodeURIComponent(field.value);
136
+ }
137
+ });
138
+ return supporterDetails;
139
+ }
140
+ /*
141
+ * Encrypt the supporter details
142
+ */
143
+ encryptSupporterDetails(supporterDetails) {
144
+ return __awaiter(this, void 0, void 0, function* () {
145
+ const encryptionKey = yield this.createEncryptionKey(this.getSeed());
146
+ const iv = window.crypto.getRandomValues(new Uint8Array(12));
147
+ const supporterDetailsString = JSON.stringify(supporterDetails);
148
+ const encryptedData = yield window.crypto.subtle.encrypt({
149
+ name: "AES-GCM",
150
+ iv: iv,
151
+ }, encryptionKey, new TextEncoder().encode(supporterDetailsString));
152
+ return {
153
+ encryptedData: this.arrayBufferToBase64(encryptedData),
154
+ iv: this.arrayBufferToBase64(iv),
155
+ };
156
+ });
157
+ }
158
+ /*
159
+ * Decrypt the supporter details
160
+ */
161
+ decryptSupporterDetails(encryptedSupporterDetails, iv) {
162
+ return __awaiter(this, void 0, void 0, function* () {
163
+ const encryptionKey = yield this.createEncryptionKey(this.getSeed());
164
+ const decryptedData = yield window.crypto.subtle.decrypt({ name: "AES-GCM", iv: iv }, encryptionKey, encryptedSupporterDetails);
165
+ return new TextDecoder().decode(decryptedData);
166
+ });
167
+ }
168
+ /*
169
+ * Create the encryption key
170
+ */
171
+ createEncryptionKey(seed) {
172
+ return __awaiter(this, void 0, void 0, function* () {
173
+ const encoder = new TextEncoder();
174
+ const keyMaterial = yield window.crypto.subtle.importKey("raw", encoder.encode(seed), { name: "PBKDF2" }, false, ["deriveKey"]);
175
+ return yield window.crypto.subtle.deriveKey({
176
+ name: "PBKDF2",
177
+ salt: encoder.encode(seed),
178
+ iterations: 100000,
179
+ hash: "SHA-256",
180
+ }, keyMaterial, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"]);
181
+ });
182
+ }
183
+ /*
184
+ * Convert an ArrayBuffer to a base64 string
185
+ */
186
+ arrayBufferToBase64(buffer) {
187
+ let binary = "";
188
+ const bytes = new Uint8Array(buffer);
189
+ const len = bytes.byteLength;
190
+ for (let i = 0; i < len; i++) {
191
+ binary += String.fromCharCode(bytes[i]);
192
+ }
193
+ return window.btoa(binary);
194
+ }
195
+ /*
196
+ * Create an Array Buffer from a base64 string
197
+ */
198
+ base64ToArrayBuffer(base64) {
199
+ const binary_string = window.atob(base64);
200
+ const len = binary_string.length;
201
+ const bytes = new Uint8Array(len);
202
+ for (let i = 0; i < len; i++) {
203
+ bytes[i] = binary_string.charCodeAt(i);
204
+ }
205
+ return bytes.buffer;
206
+ }
207
+ /*
208
+ * Derive a seed from the page URL
209
+ */
210
+ getSeed() {
211
+ const url = new URL(window.location.href);
212
+ return url.origin + url.pathname + (url.searchParams.get("ea.tracking.id") ? `?ea.tracking.id=${url.searchParams.get("ea.tracking.id")}` : '');
213
+ }
214
+ }
@@ -117,7 +117,12 @@ export class SwapAmounts {
117
117
  }));
118
118
  }
119
119
  shouldRun() {
120
- return !!window.EngridAmounts;
120
+ const hasNSG = window.EngagingNetworks.suggestedGift !== undefined &&
121
+ Object.keys(window.EngagingNetworks.suggestedGift).length > 0;
122
+ if (!!window.EngridAmounts && hasNSG) {
123
+ this.logger.log("Not swapping amounts because NSG is active on page");
124
+ }
125
+ return !!window.EngridAmounts && !hasNSG;
121
126
  }
122
127
  ignoreCurrentValue() {
123
128
  const urlParam = ENGrid.getUrlParameter("transaction.donationAmt");
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const AppVersion = "0.24.4";
1
+ export declare const AppVersion = "0.24.5";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const AppVersion = "0.24.4";
1
+ export const AppVersion = "0.24.5";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@4site/engrid-scripts",
3
- "version": "0.24.4",
3
+ "version": "0.24.5",
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": "591c9c9c6ae91db22b43352e23f948310a22d120",
28
+ "gitHead": "1db8b57a2a017c14aaa2f5704ee476be62df0970",
29
29
  "devDependencies": {
30
30
  "nodemon": "^2.0.20"
31
31
  }