@jmp-technologies/analytics 0.1.16 → 0.1.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/README.md +3 -1
- package/dist/{chunk-AEQGPTDY.js → chunk-SOHYQ5GT.js} +284 -79
- package/dist/chunk-SOHYQ5GT.js.map +1 -0
- package/dist/{chunk-P4IKTSPX.js → chunk-W7CQ5VG2.js} +2 -2
- package/dist/index.cjs +290 -79
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +70 -14
- package/dist/index.d.ts +70 -14
- package/dist/index.js +15 -3
- package/dist/next.cjs +295 -86
- package/dist/next.cjs.map +1 -1
- package/dist/next.d.cts +13 -6
- package/dist/next.d.ts +13 -6
- package/dist/next.js +27 -13
- package/dist/next.js.map +1 -1
- package/dist/react.cjs +263 -69
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +2 -2
- package/docs/legal-and-privacy.md +1 -0
- package/package.json +1 -1
- package/dist/chunk-AEQGPTDY.js.map +0 -1
- /package/dist/{chunk-P4IKTSPX.js.map → chunk-W7CQ5VG2.js.map} +0 -0
package/README.md
CHANGED
|
@@ -73,6 +73,8 @@ import {
|
|
|
73
73
|
|
|
74
74
|
Or call **`trackEmailClick(path)`** / **`trackCallClick(path)`** from `useJmpAnalytics()`.
|
|
75
75
|
|
|
76
|
+
For **SMS / WhatsApp / maps / scheduling** link taps, call **`trackContactClick(path, { channel, linkHost })`** — or set **`autoTrackContactLinks: true`** in your config (or **`NEXT_PUBLIC_JMP_AUTO_CONTACT_LINKS=true`**) to register one delegated `click` listener that classifies outbound links for you. Auto-tracking is **off by default**; per-link opt-out via `data-jmp-no-track` on the `<a>` (or any ancestor).
|
|
77
|
+
|
|
76
78
|
**Hooks:** `useJmpAnalytics()` from `@jmp-technologies/analytics/next` or `@jmp-technologies/analytics/react` for forms and custom events.
|
|
77
79
|
|
|
78
80
|
---
|
|
@@ -243,7 +245,7 @@ When present, `category` is an optional section label you can use to group FAQs
|
|
|
243
245
|
|
|
244
246
|
**Tracking events**
|
|
245
247
|
|
|
246
|
-
- `trackPageView`, `trackFormSubmit`, `trackCallClick`, `trackEmailClick` (since **0.1.10**), `trackLead`
|
|
248
|
+
- `trackPageView`, `trackFormSubmit`, `trackCallClick`, `trackEmailClick` (since **0.1.10**), `trackContactClick` (since **0.1.18** — SMS, WhatsApp, maps, scheduling), `trackLead`
|
|
247
249
|
|
|
248
250
|
**Content**
|
|
249
251
|
|
|
@@ -3,7 +3,6 @@ var JMP_SESSION_ID_KEY = "jmp_analytics_sid";
|
|
|
3
3
|
var JMP_LANDING_REFERER_KEY = "jmp_analytics_landing_referer";
|
|
4
4
|
var JMP_LANDING_REFERER_COOKIE = "jmp_lr";
|
|
5
5
|
var JMP_UTM_SESSION_KEY = "jmp_analytics_utm";
|
|
6
|
-
var JMP_EARLY_BEACON_PATH_KEY = "jmp_analytics_early_beacon_path";
|
|
7
6
|
var LANDING_REFERER_COOKIE_MAX_AGE_SEC = 30 * 60;
|
|
8
7
|
var JMP_UTM_PARAM_KEYS = [
|
|
9
8
|
"utm_source",
|
|
@@ -89,11 +88,9 @@ function captureLandingAttribution(siteHostname) {
|
|
|
89
88
|
const preservedReferer = storedReferer || cookieReferer;
|
|
90
89
|
const currentReferer = document.referrer?.trim() || null;
|
|
91
90
|
if (currentReferer && isExternalReferrerUrl(currentReferer, siteHostname)) {
|
|
92
|
-
|
|
93
|
-
sessionStorage.setItem(JMP_LANDING_REFERER_KEY, currentReferer);
|
|
94
|
-
}
|
|
91
|
+
sessionStorage.setItem(JMP_LANDING_REFERER_KEY, currentReferer);
|
|
95
92
|
writeLandingRefererCookie(currentReferer, siteHostname);
|
|
96
|
-
landingReferer =
|
|
93
|
+
landingReferer = currentReferer;
|
|
97
94
|
} else if (preservedReferer) {
|
|
98
95
|
landingReferer = preservedReferer;
|
|
99
96
|
if (!storedReferer) {
|
|
@@ -138,84 +135,175 @@ function getOrCreateSessionIdFromStorage() {
|
|
|
138
135
|
return void 0;
|
|
139
136
|
}
|
|
140
137
|
}
|
|
141
|
-
function
|
|
142
|
-
|
|
143
|
-
|
|
138
|
+
function buildLandingCaptureInlineScript() {
|
|
139
|
+
const cookieMaxAge = String(LANDING_REFERER_COOKIE_MAX_AGE_SEC);
|
|
140
|
+
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){}})();`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/contact-link-classifier.ts
|
|
144
|
+
var BOOKING_LINK_HOSTS = [
|
|
145
|
+
"calendly.com",
|
|
146
|
+
"cal.com",
|
|
147
|
+
"acuityscheduling.com",
|
|
148
|
+
"calendar.google.com",
|
|
149
|
+
"calendar.app.google",
|
|
150
|
+
"bookings.microsoft.com",
|
|
151
|
+
"outlook.office.com",
|
|
152
|
+
"outlook.office365.com",
|
|
153
|
+
"book.ms",
|
|
154
|
+
"setmore.com",
|
|
155
|
+
"simplybook.me",
|
|
156
|
+
"oncehub.com",
|
|
157
|
+
"scheduleonce.com",
|
|
158
|
+
"youcanbook.me",
|
|
159
|
+
"savvycal.com",
|
|
160
|
+
"tidycal.com",
|
|
161
|
+
"zohobookings.com",
|
|
162
|
+
"square.site",
|
|
163
|
+
"squareup.com",
|
|
164
|
+
"meetings.hubspot.com",
|
|
165
|
+
"jobber.com",
|
|
166
|
+
"housecallpro.com",
|
|
167
|
+
"servicetitan.com",
|
|
168
|
+
"thryv.com",
|
|
169
|
+
"honeybook.com",
|
|
170
|
+
"dubsado.com",
|
|
171
|
+
"fresha.com",
|
|
172
|
+
"booksy.com",
|
|
173
|
+
"vagaro.com",
|
|
174
|
+
"mindbodyonline.com",
|
|
175
|
+
"mindbody.io",
|
|
176
|
+
"janeapp.com",
|
|
177
|
+
"cliniko.com",
|
|
178
|
+
"practicebetter.io"
|
|
179
|
+
];
|
|
180
|
+
var WHATSAPP_HOSTS = ["wa.me", "api.whatsapp.com", "whatsapp.com"];
|
|
181
|
+
function hostnameMatches(host, pattern) {
|
|
182
|
+
const h = host.toLowerCase();
|
|
183
|
+
const p = pattern.toLowerCase();
|
|
184
|
+
return h === p || h.endsWith(`.${p}`);
|
|
185
|
+
}
|
|
186
|
+
function isMapsHost(host, pathname) {
|
|
187
|
+
if (hostnameMatches(host, "maps.apple.com")) {
|
|
188
|
+
return true;
|
|
144
189
|
}
|
|
145
|
-
|
|
146
|
-
return
|
|
147
|
-
}
|
|
148
|
-
|
|
190
|
+
if (hostnameMatches(host, "maps.app.goo.gl")) {
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
if (hostnameMatches(host, "google.com") || hostnameMatches(host, "google.co")) {
|
|
194
|
+
return pathname === "/maps" || pathname.startsWith("/maps/") || pathname.startsWith("/local");
|
|
149
195
|
}
|
|
196
|
+
return false;
|
|
150
197
|
}
|
|
151
|
-
function
|
|
152
|
-
|
|
153
|
-
|
|
198
|
+
function isWhatsAppHost(host, pathname) {
|
|
199
|
+
for (const pattern of WHATSAPP_HOSTS) {
|
|
200
|
+
if (!hostnameMatches(host, pattern)) {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (pattern === "whatsapp.com") {
|
|
204
|
+
return pathname.startsWith("/send");
|
|
205
|
+
}
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
function isBookingHost(host) {
|
|
211
|
+
for (const pattern of BOOKING_LINK_HOSTS) {
|
|
212
|
+
if (hostnameMatches(host, pattern)) {
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
function classifyContactHref(href) {
|
|
219
|
+
const raw = href.trim();
|
|
220
|
+
if (!raw || raw.startsWith("#") || raw.startsWith("javascript:")) {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
const lower = raw.toLowerCase();
|
|
224
|
+
if (lower.startsWith("tel:")) {
|
|
225
|
+
return { kind: "call" };
|
|
226
|
+
}
|
|
227
|
+
if (lower.startsWith("mailto:")) {
|
|
228
|
+
return { kind: "email" };
|
|
229
|
+
}
|
|
230
|
+
if (lower.startsWith("sms:") || lower.startsWith("smsto:")) {
|
|
231
|
+
return { kind: "contact_click", channel: "sms", linkHost: null };
|
|
154
232
|
}
|
|
233
|
+
if (lower.startsWith("geo:")) {
|
|
234
|
+
return { kind: "contact_click", channel: "maps", linkHost: null };
|
|
235
|
+
}
|
|
236
|
+
if (raw.startsWith("/") || raw.startsWith("?")) {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
let url;
|
|
155
240
|
try {
|
|
156
|
-
|
|
241
|
+
url = new URL(raw);
|
|
157
242
|
} catch {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
const host = url.hostname.toLowerCase();
|
|
249
|
+
const pathname = url.pathname.toLowerCase();
|
|
250
|
+
if (isWhatsAppHost(host, pathname)) {
|
|
251
|
+
return { kind: "contact_click", channel: "whatsapp", linkHost: host };
|
|
158
252
|
}
|
|
253
|
+
if (isMapsHost(host, pathname)) {
|
|
254
|
+
return { kind: "contact_click", channel: "maps", linkHost: host };
|
|
255
|
+
}
|
|
256
|
+
if (isBookingHost(host)) {
|
|
257
|
+
return { kind: "contact_click", channel: "booking", linkHost: host };
|
|
258
|
+
}
|
|
259
|
+
return null;
|
|
159
260
|
}
|
|
160
|
-
|
|
161
|
-
|
|
261
|
+
|
|
262
|
+
// src/index.ts
|
|
263
|
+
var JMP_ANALYTICS_SDK_VERSION = "0.1.18";
|
|
264
|
+
var JMP_TRACKING_CONSENT_STORAGE_KEY = "jmp_analytics_consent_v1";
|
|
265
|
+
var moduleTrackingConsentGranted = true;
|
|
266
|
+
var trackingConsentListeners = /* @__PURE__ */ new Set();
|
|
267
|
+
function readStoredTrackingConsent() {
|
|
268
|
+
if (typeof window === "undefined") {
|
|
162
269
|
return false;
|
|
163
270
|
}
|
|
164
|
-
|
|
165
|
-
|
|
271
|
+
try {
|
|
272
|
+
return localStorage.getItem(JMP_TRACKING_CONSENT_STORAGE_KEY) === "granted";
|
|
273
|
+
} catch {
|
|
166
274
|
return false;
|
|
167
275
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
metadata.referer = landingReferer;
|
|
177
|
-
}
|
|
178
|
-
const payload = {
|
|
179
|
-
type: "page_view",
|
|
180
|
-
path,
|
|
181
|
-
trackingKey: args.trackingKey,
|
|
182
|
-
...sessionId ? { sessionId } : {},
|
|
183
|
-
metadata
|
|
184
|
-
};
|
|
185
|
-
const headers = {
|
|
186
|
-
"Content-Type": "application/json",
|
|
187
|
-
"X-JMP-Tracking-Key": args.trackingKey
|
|
276
|
+
}
|
|
277
|
+
function isJmpTrackingConsentGranted() {
|
|
278
|
+
return moduleTrackingConsentGranted;
|
|
279
|
+
}
|
|
280
|
+
function subscribeJmpTrackingConsent(listener) {
|
|
281
|
+
trackingConsentListeners.add(listener);
|
|
282
|
+
return () => {
|
|
283
|
+
trackingConsentListeners.delete(listener);
|
|
188
284
|
};
|
|
189
|
-
|
|
190
|
-
|
|
285
|
+
}
|
|
286
|
+
function setJmpTrackingConsent(granted) {
|
|
287
|
+
if (moduleTrackingConsentGranted === granted) {
|
|
288
|
+
return;
|
|
191
289
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
method: "POST",
|
|
195
|
-
headers,
|
|
196
|
-
body: JSON.stringify(payload),
|
|
197
|
-
keepalive: true,
|
|
198
|
-
mode: "cors",
|
|
199
|
-
credentials: "omit"
|
|
200
|
-
}).catch(() => {
|
|
290
|
+
moduleTrackingConsentGranted = granted;
|
|
291
|
+
if (typeof window !== "undefined") {
|
|
201
292
|
try {
|
|
202
|
-
|
|
293
|
+
localStorage.setItem(
|
|
294
|
+
JMP_TRACKING_CONSENT_STORAGE_KEY,
|
|
295
|
+
granted ? "granted" : "denied"
|
|
296
|
+
);
|
|
203
297
|
} catch {
|
|
204
298
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const baseUrl = escapeInlineScript(args.baseUrl.replace(/\/$/, ""));
|
|
213
|
-
const trackingKey = escapeInlineScript(args.trackingKey);
|
|
214
|
-
const cookieMaxAge = String(LANDING_REFERER_COOKIE_MAX_AGE_SEC);
|
|
215
|
-
return `(function(){try{var k='${trackingKey}',b='${baseUrl}';if(!k||!b)return;var p=location.pathname+location.search||'/';var sentKey='${JMP_EARLY_BEACON_PATH_KEY}';if(sessionStorage.getItem(sentKey)===p)return;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)){if(!stored)sessionStorage.setItem('${JMP_LANDING_REFERER_KEY}',ref);writeCk(ref,h);lr=preserved||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)}var md={pageHostname:h,earlyBeacon:true};try{var sp=new URLSearchParams(location.search);['utm_source','utm_medium','utm_campaign','utm_content','utm_term'].forEach(function(n){var v=sp.get(n);if(v)md[n]=v})}catch(e){}if(lr)md.referer=lr;var body=JSON.stringify({type:'page_view',path:p,trackingKey:k,sessionId:sid||undefined,metadata:md});var hd={'Content-Type':'application/json','X-JMP-Tracking-Key':k};if(lr)hd['X-JMP-Visitor-Referer']=lr;sessionStorage.setItem(sentKey,p);fetch(b+'/api/events',{method:'POST',headers:hd,body:body,keepalive:true,mode:'cors',credentials:'omit'}).catch(function(){sessionStorage.removeItem(sentKey)})}catch(e){}})();`;
|
|
299
|
+
if (granted) {
|
|
300
|
+
captureLandingAttribution(window.location.hostname);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
for (const listener of trackingConsentListeners) {
|
|
304
|
+
listener();
|
|
305
|
+
}
|
|
216
306
|
}
|
|
217
|
-
|
|
218
|
-
// src/index.ts
|
|
219
307
|
function captureAndGetLandingReferer() {
|
|
220
308
|
if (typeof window === "undefined") {
|
|
221
309
|
return null;
|
|
@@ -290,9 +378,16 @@ function createJmpAnalytics(config) {
|
|
|
290
378
|
);
|
|
291
379
|
}
|
|
292
380
|
const fetchFn = resolvedFetch;
|
|
381
|
+
moduleTrackingConsentGranted = config.requireConsent ? readStoredTrackingConsent() : true;
|
|
382
|
+
function canSend() {
|
|
383
|
+
return moduleTrackingConsentGranted;
|
|
384
|
+
}
|
|
293
385
|
let lastDedupePath = "";
|
|
294
386
|
let lastDedupeAt = 0;
|
|
295
387
|
async function send(body) {
|
|
388
|
+
if (!canSend()) {
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
296
391
|
const url = `${baseUrl}/api/events`;
|
|
297
392
|
const headers = {
|
|
298
393
|
"Content-Type": "application/json",
|
|
@@ -348,6 +443,9 @@ function createJmpAnalytics(config) {
|
|
|
348
443
|
}
|
|
349
444
|
}
|
|
350
445
|
async function sendLead(body) {
|
|
446
|
+
if (!canSend()) {
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
351
449
|
const url = `${baseUrl}/api/leads`;
|
|
352
450
|
const headers = {
|
|
353
451
|
"Content-Type": "application/json",
|
|
@@ -410,6 +508,9 @@ function createJmpAnalytics(config) {
|
|
|
410
508
|
return false;
|
|
411
509
|
}
|
|
412
510
|
function trackPageView(options) {
|
|
511
|
+
if (!canSend()) {
|
|
512
|
+
return Promise.resolve();
|
|
513
|
+
}
|
|
413
514
|
const pathFromOptions = options?.path?.trim();
|
|
414
515
|
let pathname = "/";
|
|
415
516
|
let search = "";
|
|
@@ -425,9 +526,6 @@ function createJmpAnalytics(config) {
|
|
|
425
526
|
search = window.location.search.replace(/^\?/, "");
|
|
426
527
|
}
|
|
427
528
|
const path = search ? `${pathname}?${search}` : pathname;
|
|
428
|
-
if (wasEarlyBeaconSentForPath(path)) {
|
|
429
|
-
return Promise.resolve();
|
|
430
|
-
}
|
|
431
529
|
if (shouldDedupePageView(pathname)) {
|
|
432
530
|
return Promise.resolve();
|
|
433
531
|
}
|
|
@@ -478,6 +576,20 @@ function createJmpAnalytics(config) {
|
|
|
478
576
|
metadata: metadata ?? {}
|
|
479
577
|
});
|
|
480
578
|
}
|
|
579
|
+
function trackContactClick(path, options) {
|
|
580
|
+
const metadata = {
|
|
581
|
+
...options.metadata && typeof options.metadata === "object" ? options.metadata : {},
|
|
582
|
+
channel: options.channel
|
|
583
|
+
};
|
|
584
|
+
if (options.linkHost) {
|
|
585
|
+
metadata.linkHost = options.linkHost;
|
|
586
|
+
}
|
|
587
|
+
return send({
|
|
588
|
+
type: "contact_click",
|
|
589
|
+
path: path || "/",
|
|
590
|
+
metadata
|
|
591
|
+
});
|
|
592
|
+
}
|
|
481
593
|
function trackLead(options) {
|
|
482
594
|
const path = options?.path ?? (typeof window !== "undefined" ? window.location.pathname : "/");
|
|
483
595
|
const body = { path: path || "/" };
|
|
@@ -510,7 +622,20 @@ function createJmpAnalytics(config) {
|
|
|
510
622
|
if (type === "call_click") {
|
|
511
623
|
return trackCallClick(payload.path, payload.metadata);
|
|
512
624
|
}
|
|
513
|
-
|
|
625
|
+
if (type === "email_click") {
|
|
626
|
+
return trackEmailClick(payload.path, payload.metadata);
|
|
627
|
+
}
|
|
628
|
+
const meta = payload.metadata ?? {};
|
|
629
|
+
const channel = typeof meta.channel === "string" ? meta.channel : "";
|
|
630
|
+
const linkHost = typeof meta.linkHost === "string" ? meta.linkHost : null;
|
|
631
|
+
if (channel !== "sms" && channel !== "whatsapp" && channel !== "maps" && channel !== "booking") {
|
|
632
|
+
return Promise.resolve();
|
|
633
|
+
}
|
|
634
|
+
return trackContactClick(payload.path, {
|
|
635
|
+
channel,
|
|
636
|
+
linkHost,
|
|
637
|
+
metadata: meta
|
|
638
|
+
});
|
|
514
639
|
}
|
|
515
640
|
function subscribeToSpaNavigation() {
|
|
516
641
|
if (typeof window === "undefined") {
|
|
@@ -544,12 +669,75 @@ function createJmpAnalytics(config) {
|
|
|
544
669
|
history.replaceState = replace;
|
|
545
670
|
};
|
|
546
671
|
}
|
|
547
|
-
|
|
672
|
+
let lastContactClickKey = "";
|
|
673
|
+
let lastContactClickAt = 0;
|
|
674
|
+
function subscribeContactLinkAutoTrack() {
|
|
675
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
676
|
+
return () => {
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
const handler = (event) => {
|
|
680
|
+
if (!canSend()) {
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
const target = event.target;
|
|
684
|
+
if (!(target instanceof Element)) {
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
const anchor = target.closest("a[href]");
|
|
688
|
+
if (!(anchor instanceof HTMLAnchorElement)) {
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
if (anchor.closest("[data-jmp-no-track]")) {
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
const href = anchor.getAttribute("href") ?? "";
|
|
695
|
+
const classified = classifyContactHref(href);
|
|
696
|
+
if (!classified) {
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
const path = window.location.pathname;
|
|
700
|
+
if (classified.kind === "call") {
|
|
701
|
+
if (dedupeContactClick(`call_click:${path}`)) {
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
void trackCallClick(path);
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
if (classified.kind === "email") {
|
|
708
|
+
if (dedupeContactClick(`email_click:${path}`)) {
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
void trackEmailClick(path);
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
if (dedupeContactClick(`contact_click:${classified.channel}:${path}`)) {
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
void trackContactClick(path, {
|
|
718
|
+
channel: classified.channel,
|
|
719
|
+
linkHost: classified.linkHost
|
|
720
|
+
});
|
|
721
|
+
};
|
|
722
|
+
document.addEventListener("click", handler, { capture: true });
|
|
723
|
+
return () => {
|
|
724
|
+
document.removeEventListener("click", handler, { capture: true });
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
function dedupeContactClick(key) {
|
|
728
|
+
const now = Date.now();
|
|
729
|
+
if (key === lastContactClickKey && now - lastContactClickAt < 1e3) {
|
|
730
|
+
return true;
|
|
731
|
+
}
|
|
732
|
+
lastContactClickKey = key;
|
|
733
|
+
lastContactClickAt = now;
|
|
734
|
+
return false;
|
|
735
|
+
}
|
|
736
|
+
if (typeof window !== "undefined" && canSend()) {
|
|
548
737
|
captureLandingAttribution(window.location.hostname);
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
});
|
|
738
|
+
}
|
|
739
|
+
if (typeof window !== "undefined" && config.autoTrackContactLinks && canSend()) {
|
|
740
|
+
subscribeContactLinkAutoTrack();
|
|
553
741
|
}
|
|
554
742
|
return {
|
|
555
743
|
track,
|
|
@@ -557,8 +745,12 @@ function createJmpAnalytics(config) {
|
|
|
557
745
|
trackFormSubmit,
|
|
558
746
|
trackCallClick,
|
|
559
747
|
trackEmailClick,
|
|
748
|
+
trackContactClick,
|
|
560
749
|
trackLead,
|
|
561
|
-
subscribeToSpaNavigation
|
|
750
|
+
subscribeToSpaNavigation,
|
|
751
|
+
subscribeContactLinkAutoTrack,
|
|
752
|
+
setTrackingConsent: setJmpTrackingConsent,
|
|
753
|
+
isTrackingConsentGranted: isJmpTrackingConsentGranted
|
|
562
754
|
};
|
|
563
755
|
}
|
|
564
756
|
async function trackJmpServerPageView(config, options = {}) {
|
|
@@ -681,7 +873,10 @@ async function registerJmpContentWrappers(config) {
|
|
|
681
873
|
"Content-Type": "application/json",
|
|
682
874
|
"X-JMP-Tracking-Key": config.trackingKey
|
|
683
875
|
},
|
|
684
|
-
body: JSON.stringify({
|
|
876
|
+
body: JSON.stringify({
|
|
877
|
+
wrappers,
|
|
878
|
+
sdkVersion: JMP_ANALYTICS_SDK_VERSION
|
|
879
|
+
})
|
|
685
880
|
});
|
|
686
881
|
const text = await res.text();
|
|
687
882
|
if (!res.ok) {
|
|
@@ -779,11 +974,15 @@ function getJmpBrowserAnalyticsConfigFromEnv(overrides) {
|
|
|
779
974
|
const baseUrl = readProcessEnv("NEXT_PUBLIC_JMP_ANALYTICS_URL");
|
|
780
975
|
const trackingKey = readProcessEnv("NEXT_PUBLIC_JMP_TRACKING_KEY");
|
|
781
976
|
if (!baseUrl || !trackingKey) return null;
|
|
977
|
+
const requireConsentFromEnv = readProcessEnv("NEXT_PUBLIC_JMP_ANALYTICS_REQUIRE_CONSENT") === "true";
|
|
978
|
+
const autoTrackContactLinksFromEnv = readProcessEnv("NEXT_PUBLIC_JMP_AUTO_CONTACT_LINKS") === "true";
|
|
782
979
|
return {
|
|
783
980
|
baseUrl,
|
|
784
981
|
trackingKey,
|
|
785
982
|
pageViewDedupeMs: DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS,
|
|
786
|
-
...overrides
|
|
983
|
+
...overrides,
|
|
984
|
+
requireConsent: overrides?.requireConsent ?? requireConsentFromEnv,
|
|
985
|
+
autoTrackContactLinks: overrides?.autoTrackContactLinks ?? autoTrackContactLinksFromEnv
|
|
787
986
|
};
|
|
788
987
|
}
|
|
789
988
|
function getJmpServerContentConfigFromEnv(overrides) {
|
|
@@ -816,8 +1015,14 @@ function createJmpContentClientFromServerEnv(overrides) {
|
|
|
816
1015
|
}
|
|
817
1016
|
|
|
818
1017
|
export {
|
|
819
|
-
sendEarlyPageViewBeacon,
|
|
820
1018
|
buildLandingCaptureInlineScript,
|
|
1019
|
+
BOOKING_LINK_HOSTS,
|
|
1020
|
+
classifyContactHref,
|
|
1021
|
+
JMP_ANALYTICS_SDK_VERSION,
|
|
1022
|
+
JMP_TRACKING_CONSENT_STORAGE_KEY,
|
|
1023
|
+
isJmpTrackingConsentGranted,
|
|
1024
|
+
subscribeJmpTrackingConsent,
|
|
1025
|
+
setJmpTrackingConsent,
|
|
821
1026
|
createJmpAnalytics,
|
|
822
1027
|
trackJmpServerPageView,
|
|
823
1028
|
getJmpBlogPosts,
|
|
@@ -837,4 +1042,4 @@ export {
|
|
|
837
1042
|
getOrCreateJmpAnalyticsFromBrowserEnv,
|
|
838
1043
|
createJmpContentClientFromServerEnv
|
|
839
1044
|
};
|
|
840
|
-
//# sourceMappingURL=chunk-
|
|
1045
|
+
//# sourceMappingURL=chunk-SOHYQ5GT.js.map
|