@jmp-technologies/analytics 0.1.23 → 0.1.24
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/astro/client.cjs.map +1 -1
- package/dist/astro/client.js +2 -2
- package/dist/astro/content.cjs.map +1 -1
- package/dist/astro/content.d.cts +1 -1
- package/dist/astro/content.d.ts +1 -1
- package/dist/astro/content.js +3 -3
- package/dist/astro/integration.cjs +1 -1
- package/dist/astro/integration.cjs.map +1 -1
- package/dist/astro/integration.js +3 -3
- package/dist/astro/middleware.cjs.map +1 -1
- package/dist/astro/middleware.js +2 -2
- package/dist/astro/register.cjs +1 -1
- package/dist/astro/register.cjs.map +1 -1
- package/dist/astro/register.js +3 -3
- package/dist/{chunk-W6VYMD2P.js → chunk-2AUF7ENL.js} +6 -4
- package/dist/{chunk-W6VYMD2P.js.map → chunk-2AUF7ENL.js.map} +1 -1
- package/dist/{chunk-776LXUWH.js → chunk-7MLPSL6T.js} +2 -2
- package/dist/chunk-CDORZ2YJ.js +154 -0
- package/dist/chunk-CDORZ2YJ.js.map +1 -0
- package/dist/{chunk-QUNCGUEE.js → chunk-GPPQ3TYI.js} +3 -3
- package/dist/{chunk-B3FYJIXY.js → chunk-MOX7RJUU.js} +1 -1
- package/dist/{chunk-B3FYJIXY.js.map → chunk-MOX7RJUU.js.map} +1 -1
- package/dist/{chunk-HIJ6OV4S.js → chunk-WCI6VOVY.js} +3 -3
- package/dist/{chunk-G3SNRG7J.js → chunk-ZP4L5DCM.js} +2 -2
- package/dist/index.cjs +25 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/next/content.cjs.map +1 -1
- package/dist/next/content.d.cts +1 -1
- package/dist/next/content.d.ts +1 -1
- package/dist/next/content.js +3 -3
- package/dist/next/instrumentation.cjs +1 -1
- package/dist/next/instrumentation.cjs.map +1 -1
- package/dist/next/instrumentation.js +2 -2
- package/dist/next/middleware.cjs.map +1 -1
- package/dist/next/middleware.js +2 -2
- package/dist/next/site-root-server.cjs +1001 -0
- package/dist/next/site-root-server.cjs.map +1 -0
- package/dist/next/site-root-server.d.cts +16 -0
- package/dist/next/site-root-server.d.ts +16 -0
- package/dist/next/site-root-server.js +32 -0
- package/dist/next/site-root-server.js.map +1 -0
- package/dist/next.cjs +53 -15
- package/dist/next.cjs.map +1 -1
- package/dist/next.d.cts +13 -5
- package/dist/next.d.ts +13 -5
- package/dist/next.js +11 -108
- package/dist/next.js.map +1 -1
- package/dist/react.cjs +8 -1
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +3 -3
- package/dist/{site-config-BRuZWGSW.d.cts → site-config-BXs91Yex.d.cts} +5 -0
- package/dist/{site-config-hkGMSeT7.d.ts → site-config-DpMgrfeN.d.ts} +5 -0
- package/package.json +7 -2
- /package/dist/{chunk-776LXUWH.js.map → chunk-7MLPSL6T.js.map} +0 -0
- /package/dist/{chunk-QUNCGUEE.js.map → chunk-GPPQ3TYI.js.map} +0 -0
- /package/dist/{chunk-HIJ6OV4S.js.map → chunk-WCI6VOVY.js.map} +0 -0
- /package/dist/{chunk-G3SNRG7J.js.map → chunk-ZP4L5DCM.js.map} +0 -0
|
@@ -0,0 +1,1001 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/next/site-root-server.tsx
|
|
31
|
+
var site_root_server_exports = {};
|
|
32
|
+
__export(site_root_server_exports, {
|
|
33
|
+
JmpSiteRootServer: () => JmpSiteRootServer
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(site_root_server_exports);
|
|
36
|
+
|
|
37
|
+
// src/env-public.ts
|
|
38
|
+
function firstDefined(...values) {
|
|
39
|
+
for (const v of values) {
|
|
40
|
+
if (v) return v;
|
|
41
|
+
}
|
|
42
|
+
return void 0;
|
|
43
|
+
}
|
|
44
|
+
function readJmpPublicEnvFromProcessEnv() {
|
|
45
|
+
if (typeof process === "undefined" || !process.env) {
|
|
46
|
+
return {};
|
|
47
|
+
}
|
|
48
|
+
const { env } = process;
|
|
49
|
+
const analyticsUrl = firstDefined(
|
|
50
|
+
env.NEXT_PUBLIC_JMP_ANALYTICS_URL,
|
|
51
|
+
env.PUBLIC_JMP_ANALYTICS_URL
|
|
52
|
+
);
|
|
53
|
+
const trackingKey = firstDefined(
|
|
54
|
+
env.NEXT_PUBLIC_JMP_TRACKING_KEY,
|
|
55
|
+
env.PUBLIC_JMP_TRACKING_KEY
|
|
56
|
+
);
|
|
57
|
+
const requireConsentRaw = firstDefined(
|
|
58
|
+
env.NEXT_PUBLIC_JMP_ANALYTICS_REQUIRE_CONSENT,
|
|
59
|
+
env.PUBLIC_JMP_ANALYTICS_REQUIRE_CONSENT
|
|
60
|
+
);
|
|
61
|
+
const autoContactRaw = firstDefined(
|
|
62
|
+
env.NEXT_PUBLIC_JMP_AUTO_CONTACT_LINKS,
|
|
63
|
+
env.PUBLIC_JMP_AUTO_CONTACT_LINKS
|
|
64
|
+
);
|
|
65
|
+
return {
|
|
66
|
+
analyticsUrl,
|
|
67
|
+
trackingKey,
|
|
68
|
+
requireConsent: requireConsentRaw === "true",
|
|
69
|
+
autoContactLinks: autoContactRaw === "true"
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function readJmpPublicEnvFromProcess(overrides) {
|
|
73
|
+
const fromEnv = readJmpPublicEnvFromProcessEnv();
|
|
74
|
+
return {
|
|
75
|
+
analyticsUrl: firstDefined(overrides?.analyticsUrl, fromEnv.analyticsUrl),
|
|
76
|
+
trackingKey: firstDefined(overrides?.trackingKey, fromEnv.trackingKey),
|
|
77
|
+
requireConsent: overrides?.requireConsent ?? fromEnv.requireConsent,
|
|
78
|
+
autoContactLinks: overrides?.autoContactLinks ?? fromEnv.autoContactLinks
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/site-config.ts
|
|
83
|
+
var registeredSite = null;
|
|
84
|
+
function getJmpSiteDefinition() {
|
|
85
|
+
return registeredSite;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/next.tsx
|
|
89
|
+
var import_script = __toESM(require("next/script"), 1);
|
|
90
|
+
var import_react2 = require("react");
|
|
91
|
+
var import_navigation = require("next/navigation");
|
|
92
|
+
|
|
93
|
+
// src/landing-capture.ts
|
|
94
|
+
var JMP_SESSION_ID_KEY = "jmp_analytics_sid";
|
|
95
|
+
var JMP_LANDING_REFERER_KEY = "jmp_analytics_landing_referer";
|
|
96
|
+
var JMP_LANDING_REFERER_COOKIE = "jmp_lr";
|
|
97
|
+
var JMP_UTM_SESSION_KEY = "jmp_analytics_utm";
|
|
98
|
+
var LANDING_REFERER_COOKIE_MAX_AGE_SEC = 30 * 60;
|
|
99
|
+
var JMP_UTM_PARAM_KEYS = [
|
|
100
|
+
"utm_source",
|
|
101
|
+
"utm_medium",
|
|
102
|
+
"utm_campaign",
|
|
103
|
+
"utm_content",
|
|
104
|
+
"utm_term"
|
|
105
|
+
];
|
|
106
|
+
function refererHostFromUrl(referer) {
|
|
107
|
+
const trimmed = referer.trim();
|
|
108
|
+
try {
|
|
109
|
+
return new URL(trimmed).hostname.replace(/^www\./i, "").toLowerCase();
|
|
110
|
+
} catch {
|
|
111
|
+
try {
|
|
112
|
+
return new URL(`https://${trimmed}`).hostname.replace(/^www\./i, "").toLowerCase();
|
|
113
|
+
} catch {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function isExternalReferrerUrl(referer, siteHostname) {
|
|
119
|
+
const refHost = refererHostFromUrl(referer);
|
|
120
|
+
if (!refHost) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
const site = siteHostname.replace(/^www\./i, "").toLowerCase();
|
|
124
|
+
return refHost !== site && !refHost.endsWith(`.${site}`);
|
|
125
|
+
}
|
|
126
|
+
function registrableSiteDomain(siteHostname) {
|
|
127
|
+
const site = siteHostname.replace(/^www\./i, "").toLowerCase();
|
|
128
|
+
if (!site || site === "localhost" || site.endsWith(".local")) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
if (!site.includes(".")) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
return site;
|
|
135
|
+
}
|
|
136
|
+
function readLandingRefererCookie(siteHostname) {
|
|
137
|
+
if (typeof document === "undefined") {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
const apex = registrableSiteDomain(siteHostname);
|
|
141
|
+
if (!apex) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
const prefix = `${JMP_LANDING_REFERER_COOKIE}=`;
|
|
145
|
+
for (const part of document.cookie.split(";")) {
|
|
146
|
+
const trimmed = part.trim();
|
|
147
|
+
if (!trimmed.startsWith(prefix)) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
const value = decodeURIComponent(trimmed.slice(prefix.length)).trim();
|
|
152
|
+
return value.length > 0 ? value : null;
|
|
153
|
+
} catch {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
function writeLandingRefererCookie(referer, siteHostname) {
|
|
160
|
+
if (typeof document === "undefined") {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const apex = registrableSiteDomain(siteHostname);
|
|
164
|
+
if (!apex) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const value = encodeURIComponent(referer.trim().slice(0, 2048));
|
|
168
|
+
const secure = typeof window !== "undefined" && window.location.protocol === "https:" ? "; Secure" : "";
|
|
169
|
+
document.cookie = `${JMP_LANDING_REFERER_COOKIE}=${value}; Domain=.${apex}; Path=/; Max-Age=${LANDING_REFERER_COOKIE_MAX_AGE_SEC}; SameSite=Lax${secure}`;
|
|
170
|
+
}
|
|
171
|
+
function captureLandingAttribution(siteHostname) {
|
|
172
|
+
if (typeof document === "undefined" || typeof window === "undefined") {
|
|
173
|
+
return { landingReferer: null, utm: {} };
|
|
174
|
+
}
|
|
175
|
+
let landingReferer = null;
|
|
176
|
+
let utm = {};
|
|
177
|
+
try {
|
|
178
|
+
const storedReferer = sessionStorage.getItem(JMP_LANDING_REFERER_KEY)?.trim() || null;
|
|
179
|
+
const cookieReferer = readLandingRefererCookie(siteHostname);
|
|
180
|
+
const preservedReferer = storedReferer || cookieReferer;
|
|
181
|
+
const currentReferer = document.referrer?.trim() || null;
|
|
182
|
+
if (currentReferer && isExternalReferrerUrl(currentReferer, siteHostname)) {
|
|
183
|
+
sessionStorage.setItem(JMP_LANDING_REFERER_KEY, currentReferer);
|
|
184
|
+
writeLandingRefererCookie(currentReferer, siteHostname);
|
|
185
|
+
landingReferer = currentReferer;
|
|
186
|
+
} else if (preservedReferer) {
|
|
187
|
+
landingReferer = preservedReferer;
|
|
188
|
+
if (!storedReferer) {
|
|
189
|
+
sessionStorage.setItem(JMP_LANDING_REFERER_KEY, preservedReferer);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const params = new URLSearchParams(window.location.search);
|
|
193
|
+
const rawUtm = sessionStorage.getItem(JMP_UTM_SESSION_KEY);
|
|
194
|
+
if (rawUtm) {
|
|
195
|
+
const parsed = JSON.parse(rawUtm);
|
|
196
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
197
|
+
utm = parsed;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
let updated = false;
|
|
201
|
+
for (const key of JMP_UTM_PARAM_KEYS) {
|
|
202
|
+
const value = params.get(key)?.trim();
|
|
203
|
+
if (value) {
|
|
204
|
+
utm[key] = value;
|
|
205
|
+
updated = true;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (updated) {
|
|
209
|
+
sessionStorage.setItem(JMP_UTM_SESSION_KEY, JSON.stringify(utm));
|
|
210
|
+
}
|
|
211
|
+
} catch {
|
|
212
|
+
}
|
|
213
|
+
return { landingReferer, utm };
|
|
214
|
+
}
|
|
215
|
+
function getOrCreateSessionIdFromStorage() {
|
|
216
|
+
if (typeof window === "undefined" || !globalThis.crypto?.randomUUID) {
|
|
217
|
+
return void 0;
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
let id = sessionStorage.getItem(JMP_SESSION_ID_KEY);
|
|
221
|
+
if (!id) {
|
|
222
|
+
id = crypto.randomUUID();
|
|
223
|
+
sessionStorage.setItem(JMP_SESSION_ID_KEY, id);
|
|
224
|
+
}
|
|
225
|
+
return id;
|
|
226
|
+
} catch {
|
|
227
|
+
return void 0;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
function buildLandingCaptureInlineScript() {
|
|
231
|
+
const cookieMaxAge = String(LANDING_REFERER_COOKIE_MAX_AGE_SEC);
|
|
232
|
+
return `(function(){try{var h=location.hostname;var ref=document.referrer||'';function rh(u){try{return new URL(u).hostname.replace(/^www\\./i,'').toLowerCase()}catch(e){try{return new URL('https://'+u).hostname.replace(/^www\\./i,'').toLowerCase()}catch(e2){return null}}}function ext(r){var x=rh(r);if(!x)return false;var s=h.replace(/^www\\./i,'').toLowerCase();return x!==s&&x.slice(-('.'+s).length)!==('.'+s)}function apexDom(hn){var s=hn.replace(/^www\\./i,'').toLowerCase();if(!s||s==='localhost'||/\\.local$/i.test(s)||s.indexOf('.')<0)return null;return s}function readCk(hn){var apex=apexDom(hn);if(!apex)return '';var pre='${JMP_LANDING_REFERER_COOKIE}=';var parts=document.cookie.split(';');for(var i=0;i<parts.length;i++){var t=parts[i].trim();if(t.indexOf(pre)===0){try{return decodeURIComponent(t.slice(pre.length)).trim()}catch(e){return ''}}}return ''}function writeCk(r,hn){var apex=apexDom(hn);if(!apex)return;var sec=location.protocol==='https:'?'; Secure':'';document.cookie='${JMP_LANDING_REFERER_COOKIE}='+encodeURIComponent(r.slice(0,2048))+'; Domain=.'+apex+'; Path=/; Max-Age=${cookieMaxAge}; SameSite=Lax'+sec}var stored=sessionStorage.getItem('${JMP_LANDING_REFERER_KEY}')||'';var ck=readCk(h);var preserved=stored||ck;var lr='';if(ref&&ext(ref)){sessionStorage.setItem('${JMP_LANDING_REFERER_KEY}',ref);writeCk(ref,h);lr=ref}else{lr=preserved||'';if(lr&&!stored)sessionStorage.setItem('${JMP_LANDING_REFERER_KEY}',lr)}var sid=sessionStorage.getItem('${JMP_SESSION_ID_KEY}');if(!sid&&crypto&&crypto.randomUUID){sid=crypto.randomUUID();sessionStorage.setItem('${JMP_SESSION_ID_KEY}',sid)}try{var sp=new URLSearchParams(location.search);var utm={};var updated=false;['utm_source','utm_medium','utm_campaign','utm_content','utm_term'].forEach(function(n){var v=sp.get(n);if(v){utm[n]=v;updated=true}});if(updated)sessionStorage.setItem('${JMP_UTM_SESSION_KEY}',JSON.stringify(utm))}catch(e){}}catch(e){}})();`;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// src/constants.ts
|
|
236
|
+
var JMP_TRACKING_CONSENT_STORAGE_KEY = "jmp_analytics_consent_v1";
|
|
237
|
+
var DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS = 2500;
|
|
238
|
+
|
|
239
|
+
// src/tracker/page-view-dedupe.ts
|
|
240
|
+
var JMP_PAGE_VIEW_DEDUPE_STORAGE_KEY = "jmp_analytics_last_pv";
|
|
241
|
+
var moduleLastPath = "";
|
|
242
|
+
var moduleLastAt = 0;
|
|
243
|
+
function shouldDedupeBrowserPageView(pathname, dedupeMs, storage = null) {
|
|
244
|
+
if (dedupeMs <= 0) {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
const now = Date.now();
|
|
248
|
+
if (pathname === moduleLastPath && now - moduleLastAt < dedupeMs) {
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
try {
|
|
252
|
+
const raw = storage?.getItem(JMP_PAGE_VIEW_DEDUPE_STORAGE_KEY);
|
|
253
|
+
if (raw) {
|
|
254
|
+
const prev = JSON.parse(raw);
|
|
255
|
+
if (prev.path === pathname && now - prev.at < dedupeMs) {
|
|
256
|
+
return true;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
storage?.setItem(
|
|
260
|
+
JMP_PAGE_VIEW_DEDUPE_STORAGE_KEY,
|
|
261
|
+
JSON.stringify({ path: pathname, at: now })
|
|
262
|
+
);
|
|
263
|
+
} catch {
|
|
264
|
+
}
|
|
265
|
+
moduleLastPath = pathname;
|
|
266
|
+
moduleLastAt = now;
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// src/contact-link-classifier.ts
|
|
271
|
+
var BOOKING_LINK_HOSTS = [
|
|
272
|
+
"calendly.com",
|
|
273
|
+
"cal.com",
|
|
274
|
+
"acuityscheduling.com",
|
|
275
|
+
"calendar.google.com",
|
|
276
|
+
"calendar.app.google",
|
|
277
|
+
"bookings.microsoft.com",
|
|
278
|
+
"outlook.office.com",
|
|
279
|
+
"outlook.office365.com",
|
|
280
|
+
"book.ms",
|
|
281
|
+
"setmore.com",
|
|
282
|
+
"simplybook.me",
|
|
283
|
+
"oncehub.com",
|
|
284
|
+
"scheduleonce.com",
|
|
285
|
+
"youcanbook.me",
|
|
286
|
+
"savvycal.com",
|
|
287
|
+
"tidycal.com",
|
|
288
|
+
"zohobookings.com",
|
|
289
|
+
"square.site",
|
|
290
|
+
"squareup.com",
|
|
291
|
+
"meetings.hubspot.com",
|
|
292
|
+
"jobber.com",
|
|
293
|
+
"housecallpro.com",
|
|
294
|
+
"servicetitan.com",
|
|
295
|
+
"thryv.com",
|
|
296
|
+
"honeybook.com",
|
|
297
|
+
"dubsado.com",
|
|
298
|
+
"fresha.com",
|
|
299
|
+
"booksy.com",
|
|
300
|
+
"vagaro.com",
|
|
301
|
+
"mindbodyonline.com",
|
|
302
|
+
"mindbody.io",
|
|
303
|
+
"janeapp.com",
|
|
304
|
+
"cliniko.com",
|
|
305
|
+
"practicebetter.io"
|
|
306
|
+
];
|
|
307
|
+
var WHATSAPP_HOSTS = ["wa.me", "api.whatsapp.com", "whatsapp.com"];
|
|
308
|
+
function hostnameMatches(host, pattern) {
|
|
309
|
+
const h = host.toLowerCase();
|
|
310
|
+
const p = pattern.toLowerCase();
|
|
311
|
+
return h === p || h.endsWith(`.${p}`);
|
|
312
|
+
}
|
|
313
|
+
function isMapsHost(host, pathname) {
|
|
314
|
+
if (hostnameMatches(host, "maps.apple.com")) {
|
|
315
|
+
return true;
|
|
316
|
+
}
|
|
317
|
+
if (hostnameMatches(host, "maps.app.goo.gl")) {
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
if (hostnameMatches(host, "google.com") || hostnameMatches(host, "google.co")) {
|
|
321
|
+
return pathname === "/maps" || pathname.startsWith("/maps/") || pathname.startsWith("/local");
|
|
322
|
+
}
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
function isWhatsAppHost(host, pathname) {
|
|
326
|
+
for (const pattern of WHATSAPP_HOSTS) {
|
|
327
|
+
if (!hostnameMatches(host, pattern)) {
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
if (pattern === "whatsapp.com") {
|
|
331
|
+
return pathname.startsWith("/send");
|
|
332
|
+
}
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
function isBookingHost(host) {
|
|
338
|
+
for (const pattern of BOOKING_LINK_HOSTS) {
|
|
339
|
+
if (hostnameMatches(host, pattern)) {
|
|
340
|
+
return true;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
function classifyContactHref(href) {
|
|
346
|
+
const raw = href.trim();
|
|
347
|
+
if (!raw || raw.startsWith("#") || raw.startsWith("javascript:")) {
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
const lower = raw.toLowerCase();
|
|
351
|
+
if (lower.startsWith("tel:")) {
|
|
352
|
+
return { kind: "call" };
|
|
353
|
+
}
|
|
354
|
+
if (lower.startsWith("mailto:")) {
|
|
355
|
+
return { kind: "email" };
|
|
356
|
+
}
|
|
357
|
+
if (lower.startsWith("sms:") || lower.startsWith("smsto:")) {
|
|
358
|
+
return { kind: "contact_click", channel: "sms", linkHost: null };
|
|
359
|
+
}
|
|
360
|
+
if (lower.startsWith("geo:")) {
|
|
361
|
+
return { kind: "contact_click", channel: "maps", linkHost: null };
|
|
362
|
+
}
|
|
363
|
+
if (raw.startsWith("/") || raw.startsWith("?")) {
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
let url;
|
|
367
|
+
try {
|
|
368
|
+
url = new URL(raw);
|
|
369
|
+
} catch {
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
const host = url.hostname.toLowerCase();
|
|
376
|
+
const pathname = url.pathname.toLowerCase();
|
|
377
|
+
if (isWhatsAppHost(host, pathname)) {
|
|
378
|
+
return { kind: "contact_click", channel: "whatsapp", linkHost: host };
|
|
379
|
+
}
|
|
380
|
+
if (isMapsHost(host, pathname)) {
|
|
381
|
+
return { kind: "contact_click", channel: "maps", linkHost: host };
|
|
382
|
+
}
|
|
383
|
+
if (isBookingHost(host)) {
|
|
384
|
+
return { kind: "contact_click", channel: "booking", linkHost: host };
|
|
385
|
+
}
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// src/consent.ts
|
|
390
|
+
var moduleTrackingConsentGranted = true;
|
|
391
|
+
var trackingConsentListeners = /* @__PURE__ */ new Set();
|
|
392
|
+
function readStoredTrackingConsent() {
|
|
393
|
+
if (typeof window === "undefined") {
|
|
394
|
+
return false;
|
|
395
|
+
}
|
|
396
|
+
try {
|
|
397
|
+
return localStorage.getItem(JMP_TRACKING_CONSENT_STORAGE_KEY) === "granted";
|
|
398
|
+
} catch {
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
function initJmpTrackingConsent(requireConsent) {
|
|
403
|
+
moduleTrackingConsentGranted = requireConsent ? readStoredTrackingConsent() : true;
|
|
404
|
+
}
|
|
405
|
+
function isJmpTrackingConsentGranted() {
|
|
406
|
+
return moduleTrackingConsentGranted;
|
|
407
|
+
}
|
|
408
|
+
function subscribeJmpTrackingConsent(listener) {
|
|
409
|
+
trackingConsentListeners.add(listener);
|
|
410
|
+
return () => {
|
|
411
|
+
trackingConsentListeners.delete(listener);
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
function setJmpTrackingConsent(granted) {
|
|
415
|
+
if (moduleTrackingConsentGranted === granted) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
moduleTrackingConsentGranted = granted;
|
|
419
|
+
if (typeof window !== "undefined") {
|
|
420
|
+
try {
|
|
421
|
+
localStorage.setItem(
|
|
422
|
+
JMP_TRACKING_CONSENT_STORAGE_KEY,
|
|
423
|
+
granted ? "granted" : "denied"
|
|
424
|
+
);
|
|
425
|
+
} catch {
|
|
426
|
+
}
|
|
427
|
+
if (granted) {
|
|
428
|
+
captureLandingAttribution(window.location.hostname);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
for (const listener of trackingConsentListeners) {
|
|
432
|
+
listener();
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// src/http.ts
|
|
437
|
+
function responseLooksLikeHtml(body) {
|
|
438
|
+
const s = body.trimStart();
|
|
439
|
+
return s.startsWith("<!") || s.toLowerCase().startsWith("<html") || s.startsWith("<") && /<html[\s>]/i.test(s.slice(0, 500));
|
|
440
|
+
}
|
|
441
|
+
function normalizeBaseUrl(baseUrl) {
|
|
442
|
+
return baseUrl.replace(/\/$/, "");
|
|
443
|
+
}
|
|
444
|
+
function resolveFetch(fetchImpl, context) {
|
|
445
|
+
const resolved = fetchImpl ?? (typeof globalThis !== "undefined" && globalThis.fetch ? globalThis.fetch.bind(globalThis) : void 0);
|
|
446
|
+
if (!resolved) {
|
|
447
|
+
throw new Error(`${context}: fetch is not available; pass fetchImpl in config`);
|
|
448
|
+
}
|
|
449
|
+
return resolved;
|
|
450
|
+
}
|
|
451
|
+
function formatApiErrorMessage(status, text, prefix) {
|
|
452
|
+
if (responseLooksLikeHtml(text)) {
|
|
453
|
+
return `${prefix} HTTP ${status} \u2014 response was HTML (not the JSON API). Check baseUrl points at the JMP Analytics dashboard.`;
|
|
454
|
+
}
|
|
455
|
+
let parsed = {};
|
|
456
|
+
try {
|
|
457
|
+
parsed = JSON.parse(text);
|
|
458
|
+
} catch {
|
|
459
|
+
parsed = { error: text };
|
|
460
|
+
}
|
|
461
|
+
const detail = parsed.error ?? text;
|
|
462
|
+
return [
|
|
463
|
+
`${prefix} HTTP ${status}`,
|
|
464
|
+
parsed.code ? `[${parsed.code}]` : null,
|
|
465
|
+
detail,
|
|
466
|
+
parsed.hint ? `Hint: ${parsed.hint}` : null,
|
|
467
|
+
parsed.expectedDomain ? `Domain in JMP admin: ${parsed.expectedDomain}` : null
|
|
468
|
+
].filter(Boolean).join(" \u2014 ");
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// src/tracker/helpers.ts
|
|
472
|
+
function captureAndGetLandingReferer() {
|
|
473
|
+
if (typeof window === "undefined") {
|
|
474
|
+
return null;
|
|
475
|
+
}
|
|
476
|
+
return captureLandingAttribution(window.location.hostname).landingReferer;
|
|
477
|
+
}
|
|
478
|
+
function mergeSessionUtms(metadata) {
|
|
479
|
+
if (typeof window === "undefined") {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
const { utm } = captureLandingAttribution(window.location.hostname);
|
|
483
|
+
for (const key of JMP_UTM_PARAM_KEYS) {
|
|
484
|
+
if (metadata[key] === void 0 && utm[key]) {
|
|
485
|
+
metadata[key] = utm[key];
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
function splitPathAndSearch(pathInput) {
|
|
490
|
+
const qIndex = pathInput.indexOf("?");
|
|
491
|
+
if (qIndex === -1) {
|
|
492
|
+
return { pathname: pathInput || "/", search: "" };
|
|
493
|
+
}
|
|
494
|
+
return {
|
|
495
|
+
pathname: pathInput.slice(0, qIndex) || "/",
|
|
496
|
+
search: pathInput.slice(qIndex + 1)
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
function inferDevice() {
|
|
500
|
+
if (typeof window === "undefined") {
|
|
501
|
+
return "unknown";
|
|
502
|
+
}
|
|
503
|
+
return window.innerWidth < 768 ? "mobile" : "desktop";
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// src/tracker/create-jmp-analytics.ts
|
|
507
|
+
function createJmpAnalytics(config) {
|
|
508
|
+
const baseUrl = normalizeBaseUrl(config.baseUrl);
|
|
509
|
+
const fetchFn = resolveFetch(config.fetchImpl, "createJmpAnalytics");
|
|
510
|
+
initJmpTrackingConsent(config.requireConsent);
|
|
511
|
+
function canSend() {
|
|
512
|
+
return isJmpTrackingConsentGranted();
|
|
513
|
+
}
|
|
514
|
+
async function send(body) {
|
|
515
|
+
if (!canSend()) {
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
const url = `${baseUrl}/api/events`;
|
|
519
|
+
const headers = {
|
|
520
|
+
"Content-Type": "application/json",
|
|
521
|
+
"X-JMP-Tracking-Key": config.trackingKey
|
|
522
|
+
};
|
|
523
|
+
if (body.type === "page_view" && typeof window !== "undefined") {
|
|
524
|
+
const landingReferer = captureAndGetLandingReferer();
|
|
525
|
+
if (landingReferer) {
|
|
526
|
+
headers["X-JMP-Visitor-Referer"] = landingReferer;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
const sid = getOrCreateSessionIdFromStorage();
|
|
530
|
+
const payload = { ...body, ...sid ? { sessionId: sid } : {} };
|
|
531
|
+
if (config.debug) {
|
|
532
|
+
console.debug("[@jmp-technologies/analytics]", "POST", url, payload);
|
|
533
|
+
}
|
|
534
|
+
const res = await fetchFn(url, {
|
|
535
|
+
method: "POST",
|
|
536
|
+
headers,
|
|
537
|
+
body: JSON.stringify(payload),
|
|
538
|
+
keepalive: true
|
|
539
|
+
});
|
|
540
|
+
if (!res.ok) {
|
|
541
|
+
const text = await res.text();
|
|
542
|
+
const message = formatApiErrorMessage(res.status, text, "JMP Analytics");
|
|
543
|
+
if (config.debug) {
|
|
544
|
+
console.debug("[@jmp-technologies/analytics]", "error response", { status: res.status, raw: text });
|
|
545
|
+
}
|
|
546
|
+
throw new Error(message);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
async function sendLead(body) {
|
|
550
|
+
if (!canSend()) {
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
const url = `${baseUrl}/api/leads`;
|
|
554
|
+
const headers = {
|
|
555
|
+
"Content-Type": "application/json",
|
|
556
|
+
"X-JMP-Tracking-Key": config.trackingKey
|
|
557
|
+
};
|
|
558
|
+
const sid = getOrCreateSessionIdFromStorage();
|
|
559
|
+
const payload = { ...body, ...sid ? { sessionId: sid } : {} };
|
|
560
|
+
if (config.debug) {
|
|
561
|
+
console.debug("[@jmp-technologies/analytics]", "POST", url, payload);
|
|
562
|
+
}
|
|
563
|
+
const res = await fetchFn(url, {
|
|
564
|
+
method: "POST",
|
|
565
|
+
headers,
|
|
566
|
+
body: JSON.stringify(payload),
|
|
567
|
+
keepalive: true
|
|
568
|
+
});
|
|
569
|
+
if (!res.ok) {
|
|
570
|
+
const text = await res.text();
|
|
571
|
+
const message = formatApiErrorMessage(res.status, text, "JMP Analytics");
|
|
572
|
+
if (config.debug) {
|
|
573
|
+
console.debug("[@jmp-technologies/analytics]", "error response", { status: res.status, raw: text });
|
|
574
|
+
}
|
|
575
|
+
throw new Error(message);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
function trackPageView(options) {
|
|
579
|
+
if (!canSend()) {
|
|
580
|
+
return Promise.resolve();
|
|
581
|
+
}
|
|
582
|
+
const pathFromOptions = options?.path?.trim();
|
|
583
|
+
let pathname = "/";
|
|
584
|
+
let search = "";
|
|
585
|
+
if (pathFromOptions) {
|
|
586
|
+
const split = splitPathAndSearch(pathFromOptions);
|
|
587
|
+
pathname = split.pathname;
|
|
588
|
+
search = split.search;
|
|
589
|
+
} else if (typeof window !== "undefined") {
|
|
590
|
+
pathname = window.location.pathname;
|
|
591
|
+
search = window.location.search.replace(/^\?/, "");
|
|
592
|
+
}
|
|
593
|
+
if (!search && typeof window !== "undefined") {
|
|
594
|
+
search = window.location.search.replace(/^\?/, "");
|
|
595
|
+
}
|
|
596
|
+
const path = search ? `${pathname}?${search}` : pathname;
|
|
597
|
+
const dedupeMs = config.pageViewDedupeMs ?? DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS;
|
|
598
|
+
const storage = typeof window !== "undefined" ? window.sessionStorage : null;
|
|
599
|
+
if (shouldDedupeBrowserPageView(pathname, dedupeMs, storage)) {
|
|
600
|
+
return Promise.resolve();
|
|
601
|
+
}
|
|
602
|
+
const metadata = options?.metadata && typeof options.metadata === "object" ? { ...options.metadata } : {};
|
|
603
|
+
if (typeof window !== "undefined") {
|
|
604
|
+
metadata.pageHostname = window.location.hostname;
|
|
605
|
+
const params = new URLSearchParams(
|
|
606
|
+
search ? `?${search}` : window.location.search
|
|
607
|
+
);
|
|
608
|
+
for (const key of JMP_UTM_PARAM_KEYS) {
|
|
609
|
+
const v = params.get(key);
|
|
610
|
+
if (v && metadata[key] === void 0) {
|
|
611
|
+
metadata[key] = v;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
mergeSessionUtms(metadata);
|
|
615
|
+
const landingReferer = captureAndGetLandingReferer();
|
|
616
|
+
if (landingReferer && metadata.referer === void 0) {
|
|
617
|
+
metadata.referer = landingReferer;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
return send({
|
|
621
|
+
type: "page_view",
|
|
622
|
+
path: path || "/",
|
|
623
|
+
device: options?.device ?? inferDevice(),
|
|
624
|
+
country: options?.country,
|
|
625
|
+
metadata
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
function trackFormSubmit(path, metadata) {
|
|
629
|
+
return send({
|
|
630
|
+
type: "form_submit",
|
|
631
|
+
path: path || "/",
|
|
632
|
+
metadata: metadata ?? {}
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
function trackCallClick(path, metadata) {
|
|
636
|
+
return send({
|
|
637
|
+
type: "call_click",
|
|
638
|
+
path: path || "/",
|
|
639
|
+
metadata: metadata ?? {}
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
function trackEmailClick(path, metadata) {
|
|
643
|
+
return send({
|
|
644
|
+
type: "email_click",
|
|
645
|
+
path: path || "/",
|
|
646
|
+
metadata: metadata ?? {}
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
function trackContactClick(path, options) {
|
|
650
|
+
const metadata = {
|
|
651
|
+
...options.metadata && typeof options.metadata === "object" ? options.metadata : {},
|
|
652
|
+
channel: options.channel
|
|
653
|
+
};
|
|
654
|
+
if (options.linkHost) {
|
|
655
|
+
metadata.linkHost = options.linkHost;
|
|
656
|
+
}
|
|
657
|
+
return send({
|
|
658
|
+
type: "contact_click",
|
|
659
|
+
path: path || "/",
|
|
660
|
+
metadata
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
function trackLead(options) {
|
|
664
|
+
const path = options?.path ?? (typeof window !== "undefined" ? window.location.pathname : "/");
|
|
665
|
+
const body = { path: path || "/" };
|
|
666
|
+
if (options?.name) body.name = options.name;
|
|
667
|
+
if (options?.email) body.email = options.email;
|
|
668
|
+
if (options?.phone) body.phone = options.phone;
|
|
669
|
+
if (options?.company) body.company = options.company;
|
|
670
|
+
if (options?.website) body.website = options.website;
|
|
671
|
+
if (options?.subject) body.subject = options.subject;
|
|
672
|
+
if (options?.message) body.message = options.message;
|
|
673
|
+
if (options?.metadata && typeof options.metadata === "object") {
|
|
674
|
+
body.metadata = options.metadata;
|
|
675
|
+
}
|
|
676
|
+
if (options?.sessionId) body.sessionId = options.sessionId;
|
|
677
|
+
return sendLead(body);
|
|
678
|
+
}
|
|
679
|
+
function track(type, payload) {
|
|
680
|
+
if (type === "page_view") {
|
|
681
|
+
return trackPageView({
|
|
682
|
+
path: payload.path,
|
|
683
|
+
source: payload.source,
|
|
684
|
+
device: payload.device,
|
|
685
|
+
country: payload.country,
|
|
686
|
+
metadata: payload.metadata
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
if (type === "form_submit") {
|
|
690
|
+
return trackFormSubmit(payload.path, payload.metadata);
|
|
691
|
+
}
|
|
692
|
+
if (type === "call_click") {
|
|
693
|
+
return trackCallClick(payload.path, payload.metadata);
|
|
694
|
+
}
|
|
695
|
+
if (type === "email_click") {
|
|
696
|
+
return trackEmailClick(payload.path, payload.metadata);
|
|
697
|
+
}
|
|
698
|
+
const meta = payload.metadata ?? {};
|
|
699
|
+
const channel = typeof meta.channel === "string" ? meta.channel : "";
|
|
700
|
+
const linkHost = typeof meta.linkHost === "string" ? meta.linkHost : null;
|
|
701
|
+
if (channel !== "sms" && channel !== "whatsapp" && channel !== "maps" && channel !== "booking") {
|
|
702
|
+
return Promise.resolve();
|
|
703
|
+
}
|
|
704
|
+
return trackContactClick(payload.path, {
|
|
705
|
+
channel,
|
|
706
|
+
linkHost,
|
|
707
|
+
metadata: meta
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
function subscribeToSpaNavigation() {
|
|
711
|
+
if (typeof window === "undefined") {
|
|
712
|
+
return () => {
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
let lastPath = window.location.pathname;
|
|
716
|
+
const run = () => {
|
|
717
|
+
const path = window.location.pathname;
|
|
718
|
+
if (path !== lastPath) {
|
|
719
|
+
lastPath = path;
|
|
720
|
+
void trackPageView({ path });
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
window.addEventListener("popstate", run);
|
|
724
|
+
const push = history.pushState.bind(history);
|
|
725
|
+
const replace = history.replaceState.bind(history);
|
|
726
|
+
history.pushState = (...args) => {
|
|
727
|
+
const r = push(...args);
|
|
728
|
+
queueMicrotask(run);
|
|
729
|
+
return r;
|
|
730
|
+
};
|
|
731
|
+
history.replaceState = (...args) => {
|
|
732
|
+
const r = replace(...args);
|
|
733
|
+
queueMicrotask(run);
|
|
734
|
+
return r;
|
|
735
|
+
};
|
|
736
|
+
return () => {
|
|
737
|
+
window.removeEventListener("popstate", run);
|
|
738
|
+
history.pushState = push;
|
|
739
|
+
history.replaceState = replace;
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
let lastContactClickKey = "";
|
|
743
|
+
let lastContactClickAt = 0;
|
|
744
|
+
function subscribeContactLinkAutoTrack() {
|
|
745
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
746
|
+
return () => {
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
const handler = (event) => {
|
|
750
|
+
if (!canSend()) {
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
const target = event.target;
|
|
754
|
+
if (!(target instanceof Element)) {
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
const anchor = target.closest("a[href]");
|
|
758
|
+
if (!(anchor instanceof HTMLAnchorElement)) {
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
if (anchor.closest("[data-jmp-no-track]") || anchor.closest("[data-jmp-tracked]")) {
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
const href = anchor.getAttribute("href") ?? "";
|
|
765
|
+
const classified = classifyContactHref(href);
|
|
766
|
+
if (!classified) {
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
const path = window.location.pathname;
|
|
770
|
+
if (classified.kind === "call") {
|
|
771
|
+
if (dedupeContactClick(`call_click:${path}`)) {
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
void trackCallClick(path);
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
if (classified.kind === "email") {
|
|
778
|
+
if (dedupeContactClick(`email_click:${path}`)) {
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
void trackEmailClick(path);
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
if (dedupeContactClick(`contact_click:${classified.channel}:${path}`)) {
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
void trackContactClick(path, {
|
|
788
|
+
channel: classified.channel,
|
|
789
|
+
linkHost: classified.linkHost
|
|
790
|
+
});
|
|
791
|
+
};
|
|
792
|
+
document.addEventListener("click", handler, { capture: true });
|
|
793
|
+
return () => {
|
|
794
|
+
document.removeEventListener("click", handler, { capture: true });
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
function dedupeContactClick(key) {
|
|
798
|
+
const now = Date.now();
|
|
799
|
+
if (key === lastContactClickKey && now - lastContactClickAt < 1e3) {
|
|
800
|
+
return true;
|
|
801
|
+
}
|
|
802
|
+
lastContactClickKey = key;
|
|
803
|
+
lastContactClickAt = now;
|
|
804
|
+
return false;
|
|
805
|
+
}
|
|
806
|
+
if (typeof window !== "undefined" && canSend()) {
|
|
807
|
+
captureLandingAttribution(window.location.hostname);
|
|
808
|
+
}
|
|
809
|
+
if (typeof window !== "undefined" && config.autoTrackContactLinks && canSend()) {
|
|
810
|
+
subscribeContactLinkAutoTrack();
|
|
811
|
+
}
|
|
812
|
+
return {
|
|
813
|
+
track,
|
|
814
|
+
trackPageView,
|
|
815
|
+
trackFormSubmit,
|
|
816
|
+
trackCallClick,
|
|
817
|
+
trackEmailClick,
|
|
818
|
+
trackContactClick,
|
|
819
|
+
trackLead,
|
|
820
|
+
subscribeToSpaNavigation,
|
|
821
|
+
subscribeContactLinkAutoTrack,
|
|
822
|
+
setTrackingConsent: setJmpTrackingConsent,
|
|
823
|
+
isTrackingConsentGranted: isJmpTrackingConsentGranted
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// src/env/browser.ts
|
|
828
|
+
function getJmpBrowserAnalyticsConfigFromEnv(overrides) {
|
|
829
|
+
const pub = readJmpPublicEnvFromProcess();
|
|
830
|
+
const site = getJmpSiteDefinition();
|
|
831
|
+
const baseUrl = overrides?.baseUrl ?? pub.analyticsUrl ?? site?.analyticsPortalUrl;
|
|
832
|
+
const trackingKey = overrides?.trackingKey ?? pub.trackingKey;
|
|
833
|
+
if (!baseUrl || !trackingKey) return null;
|
|
834
|
+
return {
|
|
835
|
+
baseUrl,
|
|
836
|
+
trackingKey,
|
|
837
|
+
pageViewDedupeMs: DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS,
|
|
838
|
+
...overrides,
|
|
839
|
+
requireConsent: overrides?.requireConsent ?? pub.requireConsent,
|
|
840
|
+
autoTrackContactLinks: overrides?.autoTrackContactLinks ?? pub.autoContactLinks
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
var jmpAnalyticsBrowserSingleton = null;
|
|
844
|
+
function getOrCreateJmpAnalyticsFromBrowserEnv(overrides) {
|
|
845
|
+
if (typeof window === "undefined") return null;
|
|
846
|
+
if (jmpAnalyticsBrowserSingleton) return jmpAnalyticsBrowserSingleton;
|
|
847
|
+
const config = getJmpBrowserAnalyticsConfigFromEnv(overrides);
|
|
848
|
+
if (!config) return null;
|
|
849
|
+
jmpAnalyticsBrowserSingleton = createJmpAnalytics(config);
|
|
850
|
+
return jmpAnalyticsBrowserSingleton;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// src/env/browser-next.ts
|
|
854
|
+
function readNextAutoContactLinksEnv() {
|
|
855
|
+
if (typeof process === "undefined" || !process.env) {
|
|
856
|
+
return void 0;
|
|
857
|
+
}
|
|
858
|
+
const { env } = process;
|
|
859
|
+
const raw = env.NEXT_PUBLIC_JMP_AUTO_CONTACT_LINKS ?? env.PUBLIC_JMP_AUTO_CONTACT_LINKS;
|
|
860
|
+
return typeof raw === "string" && raw.length > 0 ? raw : void 0;
|
|
861
|
+
}
|
|
862
|
+
function getJmpNextAnalyticsConfigOverrides() {
|
|
863
|
+
const autoEnv = readNextAutoContactLinksEnv();
|
|
864
|
+
const autoContactLinks = autoEnv === "false" ? false : autoEnv === "true" ? true : true;
|
|
865
|
+
return { autoTrackContactLinks: autoContactLinks };
|
|
866
|
+
}
|
|
867
|
+
function getOrCreateJmpAnalyticsForNextApp(overrides) {
|
|
868
|
+
return getOrCreateJmpAnalyticsFromBrowserEnv({
|
|
869
|
+
...getJmpNextAnalyticsConfigOverrides(),
|
|
870
|
+
...overrides
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
// src/react.tsx
|
|
875
|
+
var import_react = require("react");
|
|
876
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
877
|
+
|
|
878
|
+
// src/next.tsx
|
|
879
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
880
|
+
function JmpLandingCaptureScript({
|
|
881
|
+
trackingKey = readJmpPublicEnvFromProcess().trackingKey
|
|
882
|
+
} = {}) {
|
|
883
|
+
if (!trackingKey?.trim()) {
|
|
884
|
+
return null;
|
|
885
|
+
}
|
|
886
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
887
|
+
import_script.default,
|
|
888
|
+
{
|
|
889
|
+
id: "jmp-landing-capture",
|
|
890
|
+
strategy: "beforeInteractive",
|
|
891
|
+
dangerouslySetInnerHTML: {
|
|
892
|
+
__html: buildLandingCaptureInlineScript()
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
);
|
|
896
|
+
}
|
|
897
|
+
function JmpNextPageViewTrackerInner({
|
|
898
|
+
configOverrides
|
|
899
|
+
}) {
|
|
900
|
+
const pathname = (0, import_navigation.usePathname)();
|
|
901
|
+
const searchParams = (0, import_navigation.useSearchParams)();
|
|
902
|
+
const query = searchParams?.toString() ?? "";
|
|
903
|
+
const path = query ? `${pathname}?${query}` : pathname;
|
|
904
|
+
(0, import_react2.useLayoutEffect)(() => {
|
|
905
|
+
const client = getOrCreateJmpAnalyticsForNextApp(configOverrides);
|
|
906
|
+
void client?.trackPageView({ path });
|
|
907
|
+
}, [path, configOverrides]);
|
|
908
|
+
return null;
|
|
909
|
+
}
|
|
910
|
+
function JmpNextPageViewTracker({
|
|
911
|
+
configOverrides
|
|
912
|
+
}) {
|
|
913
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react2.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(JmpNextPageViewTrackerInner, { configOverrides }) });
|
|
914
|
+
}
|
|
915
|
+
function useJmpTrackingActive(deferUntilConsent) {
|
|
916
|
+
const consentGranted = (0, import_react2.useSyncExternalStore)(
|
|
917
|
+
subscribeJmpTrackingConsent,
|
|
918
|
+
() => isJmpTrackingConsentGranted(),
|
|
919
|
+
() => false
|
|
920
|
+
);
|
|
921
|
+
return !deferUntilConsent || consentGranted;
|
|
922
|
+
}
|
|
923
|
+
function jmpConfigOverridesFromProps(props) {
|
|
924
|
+
const { analyticsUrl, trackingKey } = props;
|
|
925
|
+
if (!analyticsUrl?.trim() && !trackingKey?.trim()) {
|
|
926
|
+
return void 0;
|
|
927
|
+
}
|
|
928
|
+
return {
|
|
929
|
+
...analyticsUrl?.trim() ? { baseUrl: analyticsUrl.trim() } : {},
|
|
930
|
+
...trackingKey?.trim() ? { trackingKey: trackingKey.trim() } : {}
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
function JmpNextAnalyticsBootstrap({
|
|
934
|
+
configOverrides
|
|
935
|
+
}) {
|
|
936
|
+
(0, import_react2.useLayoutEffect)(() => {
|
|
937
|
+
getOrCreateJmpAnalyticsForNextApp(configOverrides);
|
|
938
|
+
}, [configOverrides]);
|
|
939
|
+
return null;
|
|
940
|
+
}
|
|
941
|
+
function JmpNextAnalytics({
|
|
942
|
+
deferUntilConsent = readJmpPublicEnvFromProcess().requireConsent === true,
|
|
943
|
+
analyticsUrl,
|
|
944
|
+
trackingKey
|
|
945
|
+
} = {}) {
|
|
946
|
+
const active = useJmpTrackingActive(deferUntilConsent);
|
|
947
|
+
const configOverrides = (0, import_react2.useMemo)(
|
|
948
|
+
() => jmpConfigOverridesFromProps({ analyticsUrl, trackingKey }),
|
|
949
|
+
[analyticsUrl, trackingKey]
|
|
950
|
+
);
|
|
951
|
+
if (!active) {
|
|
952
|
+
return null;
|
|
953
|
+
}
|
|
954
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
955
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(JmpNextAnalyticsBootstrap, { configOverrides }),
|
|
956
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(JmpLandingCaptureScript, { trackingKey }),
|
|
957
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(JmpNextPageViewTracker, { configOverrides })
|
|
958
|
+
] });
|
|
959
|
+
}
|
|
960
|
+
function JmpSiteRoot({
|
|
961
|
+
children,
|
|
962
|
+
deferUntilConsent,
|
|
963
|
+
analyticsUrl,
|
|
964
|
+
trackingKey
|
|
965
|
+
}) {
|
|
966
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
967
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
968
|
+
JmpNextAnalytics,
|
|
969
|
+
{
|
|
970
|
+
deferUntilConsent,
|
|
971
|
+
analyticsUrl,
|
|
972
|
+
trackingKey
|
|
973
|
+
}
|
|
974
|
+
),
|
|
975
|
+
children
|
|
976
|
+
] });
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
// src/next/site-root-server.tsx
|
|
980
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
981
|
+
function JmpSiteRootServer({
|
|
982
|
+
children,
|
|
983
|
+
deferUntilConsent
|
|
984
|
+
}) {
|
|
985
|
+
const pub = readJmpPublicEnvFromProcess();
|
|
986
|
+
const site = getJmpSiteDefinition();
|
|
987
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
988
|
+
JmpSiteRoot,
|
|
989
|
+
{
|
|
990
|
+
analyticsUrl: pub.analyticsUrl ?? site?.analyticsPortalUrl,
|
|
991
|
+
trackingKey: pub.trackingKey,
|
|
992
|
+
deferUntilConsent,
|
|
993
|
+
children
|
|
994
|
+
}
|
|
995
|
+
);
|
|
996
|
+
}
|
|
997
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
998
|
+
0 && (module.exports = {
|
|
999
|
+
JmpSiteRootServer
|
|
1000
|
+
});
|
|
1001
|
+
//# sourceMappingURL=site-root-server.cjs.map
|