Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/scripts/dist/data-layer.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ export declare class DataLayer {
private dataLayer;
private _form;
private static instance;
private encoder;
private endOfGiftProcessStorageKey;
private excludedFields;
private hashedFields;
private retainedFields;
constructor();
static getInstance(): DataLayer;
private transformJSON;
Expand All @@ -14,6 +16,7 @@ export declare class DataLayer {
private attachEventListeners;
private handleFieldValueChange;
private hash;
private shaHash;
private getFieldLabel;
addEndOfGiftProcessEvent(eventName: string, eventProperties?: object): void;
addEndOfGiftProcessVariable(variableName: string, variableValue?: any): void;
Expand Down
114 changes: 82 additions & 32 deletions packages/scripts/dist/data-layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,22 @@
// are replayed after a successful gift process load.
// Sensitive payment/bank fields are excluded; selected PII fields are Base64 “hashed” (btoa — not cryptographic).
// Replace with a real hash (e.g., SHA‑256) if required.
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { EngridLogger, ENGrid, EnForm, RememberMeEvents } from ".";
export class DataLayer {
constructor() {
this.logger = new EngridLogger("DataLayer", "#f1e5bc", "#009cdc", "📊");
this.dataLayer = window.dataLayer || [];
this._form = EnForm.getInstance();
this.encoder = new TextEncoder();
this.endOfGiftProcessStorageKey = "ENGRID_END_OF_GIFT_PROCESS_EVENTS";
this.excludedFields = [
// Credit Card
Expand Down Expand Up @@ -45,6 +55,14 @@ export class DataLayer {
"supporter.billingAddress2",
"supporter.billingAddress3",
];
this.retainedFields = [
// Supporter Address, Phone Numbers, and Address
"supporter.emailAddress",
"supporter.phoneNumber2",
"supporter.address1",
"supporter.address2",
"supporter.address3",
];
if (ENGrid.getOption("RememberMe")) {
RememberMeEvents.getInstance().onLoad.subscribe((hasData) => {
this.logger.log("Remember me - onLoad", hasData);
Expand Down Expand Up @@ -102,6 +120,13 @@ export class DataLayer {
dataLayerData[`EN_URLPARAM_${key.toUpperCase()}`] =
this.transformJSON(value);
});
this.retainedFields.forEach((fieldName) => {
const storedValue = localStorage.getItem(`EN_RETAINED_FIELD_${fieldName.toUpperCase()}`);
if (storedValue) {
dataLayerData[`EN_RETAINED_FIELD_${fieldName.toUpperCase()}`] =
storedValue;
}
});
if (ENGrid.getPageType() === "DONATION") {
const recurrFreqEls = document.querySelectorAll('[name="transaction.recurrfreq"]');
const recurrValues = [...recurrFreqEls].map((el) => el.value);
Expand Down Expand Up @@ -151,45 +176,70 @@ export class DataLayer {
}
handleFieldValueChange(el) {
var _a, _b, _c;
if (el.value === "" || this.excludedFields.includes(el.name))
return;
const value = this.hashedFields.includes(el.name)
? this.hash(el.value)
: el.value;
if (["checkbox", "radio"].includes(el.type)) {
if (el.checked) {
if (el.name === "en__pg") {
//Premium gift handling
this.dataLayer.push({
event: "EN_FORM_VALUE_UPDATED",
enFieldName: el.name,
enFieldLabel: "Premium Gift",
enFieldValue: (_b = (_a = el
.closest(".en__pg__body")) === null || _a === void 0 ? void 0 : _a.querySelector(".en__pg__name")) === null || _b === void 0 ? void 0 : _b.textContent,
enProductId: (_c = document.querySelector('[name="transaction.selprodvariantid"]')) === null || _c === void 0 ? void 0 : _c.value,
});
}
else {
this.dataLayer.push({
event: "EN_FORM_VALUE_UPDATED",
enFieldName: el.name,
enFieldLabel: this.getFieldLabel(el),
enFieldValue: value,
});
return __awaiter(this, void 0, void 0, function* () {
if (el.value === "" || this.excludedFields.includes(el.name))
return;
const value = this.hashedFields.includes(el.name)
? this.hash(el.value)
: el.value;
if (["checkbox", "radio"].includes(el.type)) {
if (el.checked) {
if (el.name === "en__pg") {
//Premium gift handling
this.dataLayer.push({
event: "EN_FORM_VALUE_UPDATED",
enFieldName: el.name,
enFieldLabel: "Premium Gift",
enFieldValue: (_b = (_a = el
.closest(".en__pg__body")) === null || _a === void 0 ? void 0 : _a.querySelector(".en__pg__name")) === null || _b === void 0 ? void 0 : _b.textContent,
enProductId: (_c = document.querySelector('[name="transaction.selprodvariantid"]')) === null || _c === void 0 ? void 0 : _c.value,
});
}
else {
this.dataLayer.push({
event: "EN_FORM_VALUE_UPDATED",
enFieldName: el.name,
enFieldLabel: this.getFieldLabel(el),
enFieldValue: value,
});
}
}
return;
}
return;
}
this.dataLayer.push({
event: "EN_FORM_VALUE_UPDATED",
enFieldName: el.name,
enFieldLabel: this.getFieldLabel(el),
enFieldValue: value,
if (this.retainedFields.includes(el.name)) {
const sha256value = yield this.shaHash(el.value);
localStorage.setItem(`EN_RETAINED_FIELD_${el.name.toUpperCase()}`, sha256value);
this.dataLayer.push({
event: "EN_RETAINED_VALUE_UPDATED",
enFieldName: el.name,
enFieldLabel: this.getFieldLabel(el),
enFieldValue: sha256value,
});
}
this.dataLayer.push({
event: "EN_FORM_VALUE_UPDATED",
enFieldName: el.name,
enFieldLabel: this.getFieldLabel(el),
enFieldValue: value,
});
});
}
hash(value) {
return btoa(value);
}
// TODO: Replace the hash function with this secure SHA-256 implementation later
shaHash(value) {
return __awaiter(this, void 0, void 0, function* () {
const data = this.encoder.encode(value);
const hashBuffer = yield crypto.subtle.digest('SHA-256', data);
return Array.from(new Uint8Array(hashBuffer))
.map((byte) => {
const hex = byte.toString(16);
return hex.length === 1 ? "0" + hex : hex;
})
.join("");
});
}
getFieldLabel(el) {
var _a, _b;
return ((_b = (_a = el.closest(".en__field")) === null || _a === void 0 ? void 0 : _a.querySelector("label")) === null || _b === void 0 ? void 0 : _b.textContent) || "";
Expand Down
Loading
Loading