@jmp-technologies/analytics 0.1.17 → 0.1.19
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-3ELQETAA.js → chunk-EPNF76VK.js} +2 -2
- package/dist/{chunk-MUHLIN7P.js → chunk-GB37RVJJ.js} +339 -103
- package/dist/chunk-GB37RVJJ.js.map +1 -0
- package/dist/index.cjs +348 -104
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +112 -35
- package/dist/index.d.ts +112 -35
- package/dist/index.js +21 -5
- package/dist/next/content.cjs +322 -0
- package/dist/next/content.cjs.map +1 -0
- package/dist/next/content.d.cts +37 -0
- package/dist/next/content.d.ts +37 -0
- package/dist/next/content.js +172 -0
- package/dist/next/content.js.map +1 -0
- package/dist/next/instrumentation.cjs +131 -0
- package/dist/next/instrumentation.cjs.map +1 -0
- package/dist/next/instrumentation.d.cts +7 -0
- package/dist/next/instrumentation.d.ts +7 -0
- package/dist/next/instrumentation.js +18 -0
- package/dist/next/instrumentation.js.map +1 -0
- package/dist/next/middleware.cjs +147 -0
- package/dist/next/middleware.cjs.map +1 -0
- package/dist/next/middleware.d.cts +13 -0
- package/dist/next/middleware.d.ts +13 -0
- package/dist/next/middleware.js +22 -0
- package/dist/next/middleware.js.map +1 -0
- package/dist/next.cjs +322 -86
- package/dist/next.cjs.map +1 -1
- package/dist/next.d.cts +24 -8
- package/dist/next.d.ts +24 -8
- package/dist/next.js +41 -13
- package/dist/next.js.map +1 -1
- package/dist/react.cjs +279 -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 +17 -2
- package/dist/chunk-MUHLIN7P.js.map +0 -1
- /package/dist/{chunk-3ELQETAA.js.map → chunk-EPNF76VK.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
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getOrCreateJmpAnalyticsFromBrowserEnv
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-GB37RVJJ.js";
|
|
4
4
|
|
|
5
5
|
// src/react.tsx
|
|
6
6
|
import { useMemo } from "react";
|
|
@@ -52,4 +52,4 @@ export {
|
|
|
52
52
|
JmpTrackedTelLink,
|
|
53
53
|
JmpTrackedMailtoLink
|
|
54
54
|
};
|
|
55
|
-
//# sourceMappingURL=chunk-
|
|
55
|
+
//# sourceMappingURL=chunk-EPNF76VK.js.map
|
|
@@ -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",
|
|
@@ -136,84 +135,207 @@ function getOrCreateSessionIdFromStorage() {
|
|
|
136
135
|
return void 0;
|
|
137
136
|
}
|
|
138
137
|
}
|
|
139
|
-
function
|
|
140
|
-
|
|
141
|
-
|
|
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;
|
|
142
189
|
}
|
|
143
|
-
|
|
144
|
-
return
|
|
145
|
-
}
|
|
146
|
-
|
|
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");
|
|
147
195
|
}
|
|
196
|
+
return false;
|
|
148
197
|
}
|
|
149
|
-
function
|
|
150
|
-
|
|
151
|
-
|
|
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;
|
|
152
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 };
|
|
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;
|
|
153
240
|
try {
|
|
154
|
-
|
|
241
|
+
url = new URL(raw);
|
|
155
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 };
|
|
156
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;
|
|
157
260
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
261
|
+
|
|
262
|
+
// src/site-config.ts
|
|
263
|
+
var registeredSite = null;
|
|
264
|
+
function readNextPublicEnv(key) {
|
|
265
|
+
if (typeof process === "undefined" || !process.env) return void 0;
|
|
266
|
+
const v = key === "NEXT_PUBLIC_JMP_ANALYTICS_URL" ? process.env.NEXT_PUBLIC_JMP_ANALYTICS_URL : process.env.NEXT_PUBLIC_JMP_TRACKING_KEY;
|
|
267
|
+
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
268
|
+
}
|
|
269
|
+
function getServerConfigFromEnv() {
|
|
270
|
+
const baseUrl = readNextPublicEnv("NEXT_PUBLIC_JMP_ANALYTICS_URL");
|
|
271
|
+
const trackingKey = readNextPublicEnv("NEXT_PUBLIC_JMP_TRACKING_KEY");
|
|
272
|
+
if (!baseUrl || !trackingKey) return null;
|
|
273
|
+
return { baseUrl, trackingKey };
|
|
274
|
+
}
|
|
275
|
+
function defineJmpSite(definition) {
|
|
276
|
+
registeredSite = definition;
|
|
277
|
+
return definition;
|
|
278
|
+
}
|
|
279
|
+
function getJmpSiteDefinition() {
|
|
280
|
+
return registeredSite;
|
|
281
|
+
}
|
|
282
|
+
function getJmpContentConfig() {
|
|
283
|
+
const fromEnv = getServerConfigFromEnv();
|
|
284
|
+
if (!fromEnv) {
|
|
285
|
+
return null;
|
|
161
286
|
}
|
|
162
|
-
const
|
|
163
|
-
|
|
287
|
+
const site = getJmpSiteDefinition();
|
|
288
|
+
return {
|
|
289
|
+
...fromEnv,
|
|
290
|
+
contentWrappers: site?.contentWrappers ?? fromEnv.contentWrappers
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// src/index.ts
|
|
295
|
+
var JMP_ANALYTICS_SDK_VERSION = "0.1.19";
|
|
296
|
+
var JMP_TRACKING_CONSENT_STORAGE_KEY = "jmp_analytics_consent_v1";
|
|
297
|
+
var moduleTrackingConsentGranted = true;
|
|
298
|
+
var trackingConsentListeners = /* @__PURE__ */ new Set();
|
|
299
|
+
function readStoredTrackingConsent() {
|
|
300
|
+
if (typeof window === "undefined") {
|
|
164
301
|
return false;
|
|
165
302
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
earlyBeacon: true,
|
|
171
|
-
...utm
|
|
172
|
-
};
|
|
173
|
-
if (landingReferer) {
|
|
174
|
-
metadata.referer = landingReferer;
|
|
303
|
+
try {
|
|
304
|
+
return localStorage.getItem(JMP_TRACKING_CONSENT_STORAGE_KEY) === "granted";
|
|
305
|
+
} catch {
|
|
306
|
+
return false;
|
|
175
307
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
"Content-Type": "application/json",
|
|
185
|
-
"X-JMP-Tracking-Key": args.trackingKey
|
|
308
|
+
}
|
|
309
|
+
function isJmpTrackingConsentGranted() {
|
|
310
|
+
return moduleTrackingConsentGranted;
|
|
311
|
+
}
|
|
312
|
+
function subscribeJmpTrackingConsent(listener) {
|
|
313
|
+
trackingConsentListeners.add(listener);
|
|
314
|
+
return () => {
|
|
315
|
+
trackingConsentListeners.delete(listener);
|
|
186
316
|
};
|
|
187
|
-
|
|
188
|
-
|
|
317
|
+
}
|
|
318
|
+
function setJmpTrackingConsent(granted) {
|
|
319
|
+
if (moduleTrackingConsentGranted === granted) {
|
|
320
|
+
return;
|
|
189
321
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
method: "POST",
|
|
193
|
-
headers,
|
|
194
|
-
body: JSON.stringify(payload),
|
|
195
|
-
keepalive: true,
|
|
196
|
-
mode: "cors",
|
|
197
|
-
credentials: "omit"
|
|
198
|
-
}).catch(() => {
|
|
322
|
+
moduleTrackingConsentGranted = granted;
|
|
323
|
+
if (typeof window !== "undefined") {
|
|
199
324
|
try {
|
|
200
|
-
|
|
325
|
+
localStorage.setItem(
|
|
326
|
+
JMP_TRACKING_CONSENT_STORAGE_KEY,
|
|
327
|
+
granted ? "granted" : "denied"
|
|
328
|
+
);
|
|
201
329
|
} catch {
|
|
202
330
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const baseUrl = escapeInlineScript(args.baseUrl.replace(/\/$/, ""));
|
|
211
|
-
const trackingKey = escapeInlineScript(args.trackingKey);
|
|
212
|
-
const cookieMaxAge = String(LANDING_REFERER_COOKIE_MAX_AGE_SEC);
|
|
213
|
-
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)){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)}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){}})();`;
|
|
331
|
+
if (granted) {
|
|
332
|
+
captureLandingAttribution(window.location.hostname);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
for (const listener of trackingConsentListeners) {
|
|
336
|
+
listener();
|
|
337
|
+
}
|
|
214
338
|
}
|
|
215
|
-
|
|
216
|
-
// src/index.ts
|
|
217
339
|
function captureAndGetLandingReferer() {
|
|
218
340
|
if (typeof window === "undefined") {
|
|
219
341
|
return null;
|
|
@@ -288,9 +410,16 @@ function createJmpAnalytics(config) {
|
|
|
288
410
|
);
|
|
289
411
|
}
|
|
290
412
|
const fetchFn = resolvedFetch;
|
|
413
|
+
moduleTrackingConsentGranted = config.requireConsent ? readStoredTrackingConsent() : true;
|
|
414
|
+
function canSend() {
|
|
415
|
+
return moduleTrackingConsentGranted;
|
|
416
|
+
}
|
|
291
417
|
let lastDedupePath = "";
|
|
292
418
|
let lastDedupeAt = 0;
|
|
293
419
|
async function send(body) {
|
|
420
|
+
if (!canSend()) {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
294
423
|
const url = `${baseUrl}/api/events`;
|
|
295
424
|
const headers = {
|
|
296
425
|
"Content-Type": "application/json",
|
|
@@ -346,6 +475,9 @@ function createJmpAnalytics(config) {
|
|
|
346
475
|
}
|
|
347
476
|
}
|
|
348
477
|
async function sendLead(body) {
|
|
478
|
+
if (!canSend()) {
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
349
481
|
const url = `${baseUrl}/api/leads`;
|
|
350
482
|
const headers = {
|
|
351
483
|
"Content-Type": "application/json",
|
|
@@ -408,6 +540,9 @@ function createJmpAnalytics(config) {
|
|
|
408
540
|
return false;
|
|
409
541
|
}
|
|
410
542
|
function trackPageView(options) {
|
|
543
|
+
if (!canSend()) {
|
|
544
|
+
return Promise.resolve();
|
|
545
|
+
}
|
|
411
546
|
const pathFromOptions = options?.path?.trim();
|
|
412
547
|
let pathname = "/";
|
|
413
548
|
let search = "";
|
|
@@ -423,9 +558,6 @@ function createJmpAnalytics(config) {
|
|
|
423
558
|
search = window.location.search.replace(/^\?/, "");
|
|
424
559
|
}
|
|
425
560
|
const path = search ? `${pathname}?${search}` : pathname;
|
|
426
|
-
if (wasEarlyBeaconSentForPath(path)) {
|
|
427
|
-
return Promise.resolve();
|
|
428
|
-
}
|
|
429
561
|
if (shouldDedupePageView(pathname)) {
|
|
430
562
|
return Promise.resolve();
|
|
431
563
|
}
|
|
@@ -476,6 +608,20 @@ function createJmpAnalytics(config) {
|
|
|
476
608
|
metadata: metadata ?? {}
|
|
477
609
|
});
|
|
478
610
|
}
|
|
611
|
+
function trackContactClick(path, options) {
|
|
612
|
+
const metadata = {
|
|
613
|
+
...options.metadata && typeof options.metadata === "object" ? options.metadata : {},
|
|
614
|
+
channel: options.channel
|
|
615
|
+
};
|
|
616
|
+
if (options.linkHost) {
|
|
617
|
+
metadata.linkHost = options.linkHost;
|
|
618
|
+
}
|
|
619
|
+
return send({
|
|
620
|
+
type: "contact_click",
|
|
621
|
+
path: path || "/",
|
|
622
|
+
metadata
|
|
623
|
+
});
|
|
624
|
+
}
|
|
479
625
|
function trackLead(options) {
|
|
480
626
|
const path = options?.path ?? (typeof window !== "undefined" ? window.location.pathname : "/");
|
|
481
627
|
const body = { path: path || "/" };
|
|
@@ -508,7 +654,20 @@ function createJmpAnalytics(config) {
|
|
|
508
654
|
if (type === "call_click") {
|
|
509
655
|
return trackCallClick(payload.path, payload.metadata);
|
|
510
656
|
}
|
|
511
|
-
|
|
657
|
+
if (type === "email_click") {
|
|
658
|
+
return trackEmailClick(payload.path, payload.metadata);
|
|
659
|
+
}
|
|
660
|
+
const meta = payload.metadata ?? {};
|
|
661
|
+
const channel = typeof meta.channel === "string" ? meta.channel : "";
|
|
662
|
+
const linkHost = typeof meta.linkHost === "string" ? meta.linkHost : null;
|
|
663
|
+
if (channel !== "sms" && channel !== "whatsapp" && channel !== "maps" && channel !== "booking") {
|
|
664
|
+
return Promise.resolve();
|
|
665
|
+
}
|
|
666
|
+
return trackContactClick(payload.path, {
|
|
667
|
+
channel,
|
|
668
|
+
linkHost,
|
|
669
|
+
metadata: meta
|
|
670
|
+
});
|
|
512
671
|
}
|
|
513
672
|
function subscribeToSpaNavigation() {
|
|
514
673
|
if (typeof window === "undefined") {
|
|
@@ -542,12 +701,75 @@ function createJmpAnalytics(config) {
|
|
|
542
701
|
history.replaceState = replace;
|
|
543
702
|
};
|
|
544
703
|
}
|
|
545
|
-
|
|
704
|
+
let lastContactClickKey = "";
|
|
705
|
+
let lastContactClickAt = 0;
|
|
706
|
+
function subscribeContactLinkAutoTrack() {
|
|
707
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
708
|
+
return () => {
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
const handler = (event) => {
|
|
712
|
+
if (!canSend()) {
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
const target = event.target;
|
|
716
|
+
if (!(target instanceof Element)) {
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
const anchor = target.closest("a[href]");
|
|
720
|
+
if (!(anchor instanceof HTMLAnchorElement)) {
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
if (anchor.closest("[data-jmp-no-track]")) {
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
const href = anchor.getAttribute("href") ?? "";
|
|
727
|
+
const classified = classifyContactHref(href);
|
|
728
|
+
if (!classified) {
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
const path = window.location.pathname;
|
|
732
|
+
if (classified.kind === "call") {
|
|
733
|
+
if (dedupeContactClick(`call_click:${path}`)) {
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
void trackCallClick(path);
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
if (classified.kind === "email") {
|
|
740
|
+
if (dedupeContactClick(`email_click:${path}`)) {
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
void trackEmailClick(path);
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
if (dedupeContactClick(`contact_click:${classified.channel}:${path}`)) {
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
void trackContactClick(path, {
|
|
750
|
+
channel: classified.channel,
|
|
751
|
+
linkHost: classified.linkHost
|
|
752
|
+
});
|
|
753
|
+
};
|
|
754
|
+
document.addEventListener("click", handler, { capture: true });
|
|
755
|
+
return () => {
|
|
756
|
+
document.removeEventListener("click", handler, { capture: true });
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
function dedupeContactClick(key) {
|
|
760
|
+
const now = Date.now();
|
|
761
|
+
if (key === lastContactClickKey && now - lastContactClickAt < 1e3) {
|
|
762
|
+
return true;
|
|
763
|
+
}
|
|
764
|
+
lastContactClickKey = key;
|
|
765
|
+
lastContactClickAt = now;
|
|
766
|
+
return false;
|
|
767
|
+
}
|
|
768
|
+
if (typeof window !== "undefined" && canSend()) {
|
|
546
769
|
captureLandingAttribution(window.location.hostname);
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
});
|
|
770
|
+
}
|
|
771
|
+
if (typeof window !== "undefined" && config.autoTrackContactLinks && canSend()) {
|
|
772
|
+
subscribeContactLinkAutoTrack();
|
|
551
773
|
}
|
|
552
774
|
return {
|
|
553
775
|
track,
|
|
@@ -555,8 +777,12 @@ function createJmpAnalytics(config) {
|
|
|
555
777
|
trackFormSubmit,
|
|
556
778
|
trackCallClick,
|
|
557
779
|
trackEmailClick,
|
|
780
|
+
trackContactClick,
|
|
558
781
|
trackLead,
|
|
559
|
-
subscribeToSpaNavigation
|
|
782
|
+
subscribeToSpaNavigation,
|
|
783
|
+
subscribeContactLinkAutoTrack,
|
|
784
|
+
setTrackingConsent: setJmpTrackingConsent,
|
|
785
|
+
isTrackingConsentGranted: isJmpTrackingConsentGranted
|
|
560
786
|
};
|
|
561
787
|
}
|
|
562
788
|
async function trackJmpServerPageView(config, options = {}) {
|
|
@@ -646,11 +872,6 @@ function resolveContentWrappersFromConfig(config) {
|
|
|
646
872
|
if (config.contentWrappers?.length) {
|
|
647
873
|
return [...config.contentWrappers];
|
|
648
874
|
}
|
|
649
|
-
if (config.trustSlots?.length) {
|
|
650
|
-
return config.trustSlots.map(
|
|
651
|
-
(slot) => `trust.${slot}`
|
|
652
|
-
);
|
|
653
|
-
}
|
|
654
875
|
return [];
|
|
655
876
|
}
|
|
656
877
|
async function registerJmpContentWrappers(config) {
|
|
@@ -679,7 +900,10 @@ async function registerJmpContentWrappers(config) {
|
|
|
679
900
|
"Content-Type": "application/json",
|
|
680
901
|
"X-JMP-Tracking-Key": config.trackingKey
|
|
681
902
|
},
|
|
682
|
-
body: JSON.stringify({
|
|
903
|
+
body: JSON.stringify({
|
|
904
|
+
wrappers,
|
|
905
|
+
sdkVersion: JMP_ANALYTICS_SDK_VERSION
|
|
906
|
+
})
|
|
683
907
|
});
|
|
684
908
|
const text = await res.text();
|
|
685
909
|
if (!res.ok) {
|
|
@@ -697,18 +921,6 @@ async function registerJmpContentWrappers(config) {
|
|
|
697
921
|
const data = JSON.parse(text);
|
|
698
922
|
return { ok: true, wrappers: data.wrappers ?? wrappers };
|
|
699
923
|
}
|
|
700
|
-
async function registerJmpTrustCapabilities(config) {
|
|
701
|
-
const result = await registerJmpContentWrappers(config);
|
|
702
|
-
if (!result.ok) {
|
|
703
|
-
return result;
|
|
704
|
-
}
|
|
705
|
-
const slots = result.wrappers.filter((w) => w === "trust" || w.startsWith("trust.")).map((w) => w === "trust" ? "summary" : w.slice("trust.".length)).filter(
|
|
706
|
-
(s) => ["summary", "testimonials", "reviews", "stats", "logos", "credentials"].includes(
|
|
707
|
-
s
|
|
708
|
-
)
|
|
709
|
-
);
|
|
710
|
-
return { ok: true, slots };
|
|
711
|
-
}
|
|
712
924
|
function createJmpContentClient(config) {
|
|
713
925
|
return {
|
|
714
926
|
getBlogPosts: () => getJmpBlogPosts(config),
|
|
@@ -717,9 +929,7 @@ function createJmpContentClient(config) {
|
|
|
717
929
|
getTrustBundle: () => getJmpTrustBundle(config),
|
|
718
930
|
getSiteProfile: () => getJmpSiteProfile(config),
|
|
719
931
|
getRobotsTxt: () => getJmpRobotsTxt(config),
|
|
720
|
-
registerContentWrappers: () => registerJmpContentWrappers(config)
|
|
721
|
-
/** @deprecated Use registerContentWrappers */
|
|
722
|
-
registerTrustCapabilities: () => registerJmpTrustCapabilities(config)
|
|
932
|
+
registerContentWrappers: () => registerJmpContentWrappers(config)
|
|
723
933
|
};
|
|
724
934
|
}
|
|
725
935
|
async function getContent(config, path) {
|
|
@@ -768,31 +978,49 @@ async function getContentText(config, path) {
|
|
|
768
978
|
return text;
|
|
769
979
|
}
|
|
770
980
|
var DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS = 800;
|
|
771
|
-
function
|
|
981
|
+
function readNextPublicEnv2(key) {
|
|
772
982
|
if (typeof process === "undefined" || !process.env) return void 0;
|
|
773
|
-
|
|
983
|
+
let v;
|
|
984
|
+
switch (key) {
|
|
985
|
+
case "NEXT_PUBLIC_JMP_ANALYTICS_URL":
|
|
986
|
+
v = process.env.NEXT_PUBLIC_JMP_ANALYTICS_URL;
|
|
987
|
+
break;
|
|
988
|
+
case "NEXT_PUBLIC_JMP_TRACKING_KEY":
|
|
989
|
+
v = process.env.NEXT_PUBLIC_JMP_TRACKING_KEY;
|
|
990
|
+
break;
|
|
991
|
+
case "NEXT_PUBLIC_JMP_ANALYTICS_REQUIRE_CONSENT":
|
|
992
|
+
v = process.env.NEXT_PUBLIC_JMP_ANALYTICS_REQUIRE_CONSENT;
|
|
993
|
+
break;
|
|
994
|
+
case "NEXT_PUBLIC_JMP_AUTO_CONTACT_LINKS":
|
|
995
|
+
v = process.env.NEXT_PUBLIC_JMP_AUTO_CONTACT_LINKS;
|
|
996
|
+
break;
|
|
997
|
+
}
|
|
774
998
|
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
775
999
|
}
|
|
776
1000
|
function getJmpBrowserAnalyticsConfigFromEnv(overrides) {
|
|
777
|
-
const baseUrl =
|
|
778
|
-
const trackingKey =
|
|
1001
|
+
const baseUrl = readNextPublicEnv2("NEXT_PUBLIC_JMP_ANALYTICS_URL");
|
|
1002
|
+
const trackingKey = readNextPublicEnv2("NEXT_PUBLIC_JMP_TRACKING_KEY");
|
|
779
1003
|
if (!baseUrl || !trackingKey) return null;
|
|
1004
|
+
const requireConsentFromEnv = readNextPublicEnv2("NEXT_PUBLIC_JMP_ANALYTICS_REQUIRE_CONSENT") === "true";
|
|
1005
|
+
const autoTrackContactLinksFromEnv = readNextPublicEnv2("NEXT_PUBLIC_JMP_AUTO_CONTACT_LINKS") === "true";
|
|
780
1006
|
return {
|
|
781
1007
|
baseUrl,
|
|
782
1008
|
trackingKey,
|
|
783
1009
|
pageViewDedupeMs: DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS,
|
|
784
|
-
...overrides
|
|
1010
|
+
...overrides,
|
|
1011
|
+
requireConsent: overrides?.requireConsent ?? requireConsentFromEnv,
|
|
1012
|
+
autoTrackContactLinks: overrides?.autoTrackContactLinks ?? autoTrackContactLinksFromEnv
|
|
785
1013
|
};
|
|
786
1014
|
}
|
|
787
1015
|
function getJmpServerContentConfigFromEnv(overrides) {
|
|
788
|
-
const baseUrl =
|
|
789
|
-
const trackingKey =
|
|
1016
|
+
const baseUrl = readNextPublicEnv2("NEXT_PUBLIC_JMP_ANALYTICS_URL");
|
|
1017
|
+
const trackingKey = readNextPublicEnv2("NEXT_PUBLIC_JMP_TRACKING_KEY");
|
|
790
1018
|
if (!baseUrl || !trackingKey) return null;
|
|
791
1019
|
return { baseUrl, trackingKey, ...overrides };
|
|
792
1020
|
}
|
|
793
1021
|
function isJmpBrowserAnalyticsEnvConfigured() {
|
|
794
1022
|
return Boolean(
|
|
795
|
-
|
|
1023
|
+
readNextPublicEnv2("NEXT_PUBLIC_JMP_ANALYTICS_URL") && readNextPublicEnv2("NEXT_PUBLIC_JMP_TRACKING_KEY")
|
|
796
1024
|
);
|
|
797
1025
|
}
|
|
798
1026
|
function isJmpServerContentEnvConfigured() {
|
|
@@ -814,8 +1042,17 @@ function createJmpContentClientFromServerEnv(overrides) {
|
|
|
814
1042
|
}
|
|
815
1043
|
|
|
816
1044
|
export {
|
|
817
|
-
sendEarlyPageViewBeacon,
|
|
818
1045
|
buildLandingCaptureInlineScript,
|
|
1046
|
+
BOOKING_LINK_HOSTS,
|
|
1047
|
+
classifyContactHref,
|
|
1048
|
+
defineJmpSite,
|
|
1049
|
+
getJmpSiteDefinition,
|
|
1050
|
+
getJmpContentConfig,
|
|
1051
|
+
JMP_ANALYTICS_SDK_VERSION,
|
|
1052
|
+
JMP_TRACKING_CONSENT_STORAGE_KEY,
|
|
1053
|
+
isJmpTrackingConsentGranted,
|
|
1054
|
+
subscribeJmpTrackingConsent,
|
|
1055
|
+
setJmpTrackingConsent,
|
|
819
1056
|
createJmpAnalytics,
|
|
820
1057
|
trackJmpServerPageView,
|
|
821
1058
|
getJmpBlogPosts,
|
|
@@ -825,7 +1062,6 @@ export {
|
|
|
825
1062
|
getJmpSiteProfile,
|
|
826
1063
|
getJmpRobotsTxt,
|
|
827
1064
|
registerJmpContentWrappers,
|
|
828
|
-
registerJmpTrustCapabilities,
|
|
829
1065
|
createJmpContentClient,
|
|
830
1066
|
DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS,
|
|
831
1067
|
getJmpBrowserAnalyticsConfigFromEnv,
|
|
@@ -835,4 +1071,4 @@ export {
|
|
|
835
1071
|
getOrCreateJmpAnalyticsFromBrowserEnv,
|
|
836
1072
|
createJmpContentClientFromServerEnv
|
|
837
1073
|
};
|
|
838
|
-
//# sourceMappingURL=chunk-
|
|
1074
|
+
//# sourceMappingURL=chunk-GB37RVJJ.js.map
|