@morgan-and-morgan/intake-agent 1.1.1 → 1.2.0
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/mm-intake-widget.js +107 -3
- package/package.json +2 -1
package/dist/mm-intake-widget.js
CHANGED
|
@@ -11,6 +11,92 @@
|
|
|
11
11
|
chatUrl
|
|
12
12
|
};
|
|
13
13
|
};
|
|
14
|
+
const ATTRIBUTION_FIELD_MAX_LENGTH = {
|
|
15
|
+
tracked_landing_page: 4096,
|
|
16
|
+
tracked_landing_timestamp: 255,
|
|
17
|
+
conversion_page: 255,
|
|
18
|
+
ga_client_id: 255,
|
|
19
|
+
gclid_value: 255,
|
|
20
|
+
marketing_source: 255,
|
|
21
|
+
marketing_sub_source: 255
|
|
22
|
+
};
|
|
23
|
+
const ATTRIBUTION_FIELDS = Object.keys(ATTRIBUTION_FIELD_MAX_LENGTH);
|
|
24
|
+
const sanitizeIntakeChatAttribution = (value) => {
|
|
25
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const record = value;
|
|
29
|
+
const sanitized = {};
|
|
30
|
+
for (const field of ATTRIBUTION_FIELDS) {
|
|
31
|
+
const fieldValue = record[field];
|
|
32
|
+
const trimmed = typeof fieldValue === "string" ? fieldValue.trim() : "";
|
|
33
|
+
if (trimmed !== "") {
|
|
34
|
+
sanitized[field] = trimmed.slice(0, ATTRIBUTION_FIELD_MAX_LENGTH[field]);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return Object.keys(sanitized).length === 0 ? null : sanitized;
|
|
38
|
+
};
|
|
39
|
+
const LANDING_PAGE_COOKIE = "tracked_landing_page";
|
|
40
|
+
const LANDING_TIMESTAMP_COOKIE = "tracked_landing_timestamp";
|
|
41
|
+
const readTimestampCookie = (cookie) => {
|
|
42
|
+
const raw = readCookie(cookie, LANDING_TIMESTAMP_COOKIE);
|
|
43
|
+
return raw === void 0 ? void 0 : decodeCookieValue(raw);
|
|
44
|
+
};
|
|
45
|
+
const readCookie = (cookie, name) => {
|
|
46
|
+
var _a;
|
|
47
|
+
const raw = (_a = cookie.split(";").map((entry) => entry.trim()).find((entry) => entry.startsWith(`${name}=`))) == null ? void 0 : _a.slice(name.length + 1);
|
|
48
|
+
return raw === void 0 || raw === "" ? void 0 : raw;
|
|
49
|
+
};
|
|
50
|
+
const decodeCookieValue = (raw) => {
|
|
51
|
+
try {
|
|
52
|
+
return decodeURIComponent(raw);
|
|
53
|
+
} catch {
|
|
54
|
+
return raw;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
const parseUrl = (value) => {
|
|
58
|
+
try {
|
|
59
|
+
return new URL(value);
|
|
60
|
+
} catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const readLandingPage = (cookie) => {
|
|
65
|
+
const raw = readCookie(cookie, LANDING_PAGE_COOKIE);
|
|
66
|
+
if (raw === void 0 || parseUrl(raw) !== null) {
|
|
67
|
+
return raw;
|
|
68
|
+
}
|
|
69
|
+
const decoded = decodeCookieValue(raw);
|
|
70
|
+
return parseUrl(decoded) === null ? raw : decoded;
|
|
71
|
+
};
|
|
72
|
+
const readGaClientId = (cookie) => {
|
|
73
|
+
const raw = readCookie(cookie, "_ga");
|
|
74
|
+
if (raw === void 0) {
|
|
75
|
+
return void 0;
|
|
76
|
+
}
|
|
77
|
+
const value = decodeCookieValue(raw);
|
|
78
|
+
const parts = value.split(".");
|
|
79
|
+
return parts.length >= 4 ? parts.slice(-2).join(".") : value;
|
|
80
|
+
};
|
|
81
|
+
const readGclid = (landingUrl) => {
|
|
82
|
+
var _a, _b;
|
|
83
|
+
return landingUrl === void 0 ? void 0 : (_b = (_a = parseUrl(landingUrl)) == null ? void 0 : _a.searchParams.get("gclid")) != null ? _b : void 0;
|
|
84
|
+
};
|
|
85
|
+
const readPathname = (url) => {
|
|
86
|
+
var _a;
|
|
87
|
+
return (_a = parseUrl(url)) == null ? void 0 : _a.pathname;
|
|
88
|
+
};
|
|
89
|
+
const buildHostAttribution = (currentUrl = globalThis.location.href, cookie = document.cookie) => {
|
|
90
|
+
var _a;
|
|
91
|
+
const landingPage = readLandingPage(cookie);
|
|
92
|
+
return (_a = sanitizeIntakeChatAttribution({
|
|
93
|
+
tracked_landing_page: landingPage,
|
|
94
|
+
tracked_landing_timestamp: readTimestampCookie(cookie),
|
|
95
|
+
conversion_page: readPathname(currentUrl),
|
|
96
|
+
ga_client_id: readGaClientId(cookie),
|
|
97
|
+
gclid_value: readGclid(landingPage)
|
|
98
|
+
})) != null ? _a : {};
|
|
99
|
+
};
|
|
14
100
|
const OPEN_STATE_KEY = "mm-intake-widget-open";
|
|
15
101
|
const readOpenState = (storage = sessionStorage) => {
|
|
16
102
|
try {
|
|
@@ -105,6 +191,13 @@
|
|
|
105
191
|
return null;
|
|
106
192
|
}
|
|
107
193
|
};
|
|
194
|
+
const hasCompletedSigningEvent = (currentUrl = globalThis.location.href) => {
|
|
195
|
+
try {
|
|
196
|
+
return new URL(currentUrl).searchParams.get("event") === SIGNING_COMPLETE_EVENT;
|
|
197
|
+
} catch {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
};
|
|
108
201
|
const LUCIDE_ICON_PATHS = {
|
|
109
202
|
"message-circle": '<path d="M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719"/>',
|
|
110
203
|
"x": '<path d="M18 6 6 18"/><path d="m6 6 12 12"/>',
|
|
@@ -213,7 +306,7 @@
|
|
|
213
306
|
} catch {
|
|
214
307
|
}
|
|
215
308
|
}
|
|
216
|
-
const
|
|
309
|
+
const returnedFromSigning = hasCompletedSigningEvent();
|
|
217
310
|
let chatHasLoaded = false;
|
|
218
311
|
let chatCloseTimer;
|
|
219
312
|
let chatReadyTimer;
|
|
@@ -234,8 +327,19 @@
|
|
|
234
327
|
const hostUrl = buildSigningReturnUrl(currentUrl);
|
|
235
328
|
postToChat(
|
|
236
329
|
createEmbedProtocolMessage(EMBED_MESSAGE_TYPE.HOST_READY, {
|
|
330
|
+
// Marketing attribution (MX2-25162): the marketing site's first-touch
|
|
331
|
+
// cookies and the page the chat opened on are only visible from this
|
|
332
|
+
// page, never from the cross-origin chat frame. The chat forwards them
|
|
333
|
+
// on its first /chat turn and Salesforce builds the intake's
|
|
334
|
+
// Marketing_Tracking_Info__c record from them.
|
|
335
|
+
attribution: buildHostAttribution(),
|
|
237
336
|
...hostUrl === null ? {} : { hostUrl },
|
|
238
|
-
...completedEnvelopeId === null ? {} : { completedEnvelopeId }
|
|
337
|
+
...completedEnvelopeId === null ? {} : { completedEnvelopeId },
|
|
338
|
+
// Sent even without an envelope id: the Salesforce service is what
|
|
339
|
+
// appends `envelope_id` to the return URL, and the prod org has shipped
|
|
340
|
+
// without that append. The chat's signing card falls back to its own
|
|
341
|
+
// envelope id on this flag.
|
|
342
|
+
...hasCompletedSigningEvent(currentUrl) ? { signingComplete: true } : {}
|
|
239
343
|
})
|
|
240
344
|
);
|
|
241
345
|
};
|
|
@@ -491,7 +595,7 @@
|
|
|
491
595
|
globalThis.addEventListener("message", handleMessage);
|
|
492
596
|
document.addEventListener("keydown", handleKeydown);
|
|
493
597
|
syncLauncherLabel();
|
|
494
|
-
if (
|
|
598
|
+
if (returnedFromSigning || readOpenState()) {
|
|
495
599
|
openChat();
|
|
496
600
|
}
|
|
497
601
|
return Object.freeze({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morgan-and-morgan/intake-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Embeddable chat widget loader.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"files": [
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"exports": {
|
|
12
12
|
".": "./dist/mm-intake-widget.js",
|
|
13
13
|
"./widget": "./src/widget.ts",
|
|
14
|
+
"./attribution": "./src/attribution.ts",
|
|
14
15
|
"./protocol": "./src/protocol.ts",
|
|
15
16
|
"./signing": "./src/signing.ts",
|
|
16
17
|
"./loader-config": "./src/loader-config.ts"
|