@metodokorexmk/tracking 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +200 -0
- package/dist/index.cjs +1779 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +869 -0
- package/dist/index.d.ts +869 -0
- package/dist/index.js +1719 -0
- package/dist/index.js.map +1 -0
- package/dist/react/index.cjs +870 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +201 -0
- package/dist/react/index.d.ts +201 -0
- package/dist/react/index.js +862 -0
- package/dist/react/index.js.map +1 -0
- package/package.json +92 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1779 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var ReactGA = require('react-ga4');
|
|
4
|
+
|
|
5
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
6
|
+
|
|
7
|
+
var ReactGA__default = /*#__PURE__*/_interopDefault(ReactGA);
|
|
8
|
+
|
|
9
|
+
// src/core/analytics.ts
|
|
10
|
+
var isInitialized = false;
|
|
11
|
+
var currentConfig = {};
|
|
12
|
+
var resolvedTrackingId = "";
|
|
13
|
+
var cachedUserId = null;
|
|
14
|
+
var cachedUserName = null;
|
|
15
|
+
var getGATrackingId = () => {
|
|
16
|
+
if (typeof window === "undefined") return resolvedTrackingId || "";
|
|
17
|
+
if (currentConfig.resolveTrackingId) {
|
|
18
|
+
try {
|
|
19
|
+
const path = window.location.pathname;
|
|
20
|
+
return currentConfig.resolveTrackingId(path);
|
|
21
|
+
} catch {
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return resolvedTrackingId;
|
|
25
|
+
};
|
|
26
|
+
var initGA = (config = {}) => {
|
|
27
|
+
if (typeof window === "undefined") return;
|
|
28
|
+
if (isInitialized) return;
|
|
29
|
+
currentConfig = config;
|
|
30
|
+
resolvedTrackingId = config.trackingId || "";
|
|
31
|
+
const trackingId = getGATrackingId();
|
|
32
|
+
if (!trackingId) {
|
|
33
|
+
if (config.debug) {
|
|
34
|
+
console.warn("[KorexTracking] No tracking ID provided. GA4 will not initialize.");
|
|
35
|
+
}
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
ReactGA__default.default.initialize(trackingId);
|
|
40
|
+
isInitialized = true;
|
|
41
|
+
if (config.anonymizeIp) {
|
|
42
|
+
window.gtag?.("config", trackingId, { anonymize_ip: true });
|
|
43
|
+
}
|
|
44
|
+
if (config.debug) {
|
|
45
|
+
console.log(`[KorexTracking] GA4 initialized with ID: ${trackingId}`);
|
|
46
|
+
}
|
|
47
|
+
} catch (error) {
|
|
48
|
+
if (config.debug) {
|
|
49
|
+
console.error("[KorexTracking] Failed to initialize GA4:", error);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
var reinitGA = (newTrackingId) => {
|
|
54
|
+
isInitialized = false;
|
|
55
|
+
resolvedTrackingId = newTrackingId;
|
|
56
|
+
initGA({ ...currentConfig, trackingId: newTrackingId });
|
|
57
|
+
};
|
|
58
|
+
var isGAInitialized = () => isInitialized;
|
|
59
|
+
var captureUTMParams = () => {
|
|
60
|
+
if (typeof window === "undefined") return null;
|
|
61
|
+
const params = new URLSearchParams(window.location.search);
|
|
62
|
+
const utmParams = {};
|
|
63
|
+
let hasParams = false;
|
|
64
|
+
const utmKeys = ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"];
|
|
65
|
+
for (const key of utmKeys) {
|
|
66
|
+
const value = params.get(key);
|
|
67
|
+
if (value) {
|
|
68
|
+
utmParams[key] = value;
|
|
69
|
+
hasParams = true;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return hasParams ? utmParams : null;
|
|
73
|
+
};
|
|
74
|
+
var captureUserIdFromURL = () => {
|
|
75
|
+
if (typeof window === "undefined") return null;
|
|
76
|
+
const params = new URLSearchParams(window.location.search);
|
|
77
|
+
const userId = params.get("user_id") || params.get("userId");
|
|
78
|
+
if (userId) {
|
|
79
|
+
cachedUserId = userId;
|
|
80
|
+
try {
|
|
81
|
+
localStorage.setItem("tracking_user_id", userId);
|
|
82
|
+
} catch {
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return userId;
|
|
86
|
+
};
|
|
87
|
+
var getUserName = () => {
|
|
88
|
+
if (typeof window === "undefined") return null;
|
|
89
|
+
const params = new URLSearchParams(window.location.search);
|
|
90
|
+
const fromUrl = params.get("user_name") || params.get("userName");
|
|
91
|
+
if (fromUrl) {
|
|
92
|
+
cachedUserName = fromUrl;
|
|
93
|
+
return fromUrl;
|
|
94
|
+
}
|
|
95
|
+
if (cachedUserName) return cachedUserName;
|
|
96
|
+
try {
|
|
97
|
+
const fromStorage = localStorage.getItem("tracking_user_name");
|
|
98
|
+
if (fromStorage) {
|
|
99
|
+
cachedUserName = fromStorage;
|
|
100
|
+
return fromStorage;
|
|
101
|
+
}
|
|
102
|
+
const userInfo = localStorage.getItem("userInfo");
|
|
103
|
+
if (userInfo) {
|
|
104
|
+
const parsed = JSON.parse(userInfo);
|
|
105
|
+
const name = parsed?.name || parsed?.userName || parsed?.fullName;
|
|
106
|
+
if (name) {
|
|
107
|
+
cachedUserName = name;
|
|
108
|
+
return name;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
} catch {
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
};
|
|
115
|
+
var getUserId = () => {
|
|
116
|
+
if (cachedUserId) return cachedUserId;
|
|
117
|
+
if (typeof window === "undefined") return null;
|
|
118
|
+
try {
|
|
119
|
+
return localStorage.getItem("tracking_user_id");
|
|
120
|
+
} catch {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
var setUserId = (userId) => {
|
|
125
|
+
cachedUserId = userId;
|
|
126
|
+
try {
|
|
127
|
+
localStorage.setItem("tracking_user_id", userId);
|
|
128
|
+
} catch {
|
|
129
|
+
}
|
|
130
|
+
window.gtag?.("set", { user_id: userId });
|
|
131
|
+
};
|
|
132
|
+
var setUserName = (userName) => {
|
|
133
|
+
cachedUserName = userName;
|
|
134
|
+
try {
|
|
135
|
+
localStorage.setItem("tracking_user_name", userName);
|
|
136
|
+
} catch {
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
var trackPageView = (path) => {
|
|
140
|
+
if (typeof window === "undefined") return;
|
|
141
|
+
const userId = getUserId();
|
|
142
|
+
const utmParams = captureUTMParams();
|
|
143
|
+
let pageWithParams = path;
|
|
144
|
+
if (utmParams) {
|
|
145
|
+
const searchParams = new URLSearchParams();
|
|
146
|
+
Object.entries(utmParams).forEach(([key, value]) => {
|
|
147
|
+
if (value) searchParams.set(key, value);
|
|
148
|
+
});
|
|
149
|
+
const paramString = searchParams.toString();
|
|
150
|
+
if (paramString) {
|
|
151
|
+
pageWithParams = `${path}${path.includes("?") ? "&" : "?"}${paramString}`;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
ReactGA__default.default.send({
|
|
156
|
+
hitType: "pageview",
|
|
157
|
+
page: pageWithParams,
|
|
158
|
+
...userId && { user_id: userId }
|
|
159
|
+
});
|
|
160
|
+
} catch {
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
window.gtag?.("event", "page_view", {
|
|
164
|
+
page_path: pageWithParams,
|
|
165
|
+
page_title: document.title,
|
|
166
|
+
...userId && { user_id: userId }
|
|
167
|
+
});
|
|
168
|
+
} catch {
|
|
169
|
+
}
|
|
170
|
+
if (currentConfig.debug) {
|
|
171
|
+
console.log(`[KorexTracking] PageView: ${pageWithParams}`, { userId });
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
var trackEvent = (category, action, label, value, additionalData) => {
|
|
175
|
+
if (typeof window === "undefined") return;
|
|
176
|
+
const userId = getUserId();
|
|
177
|
+
const userName = getUserName();
|
|
178
|
+
const eventData = {
|
|
179
|
+
event_category: category,
|
|
180
|
+
event_label: label || "",
|
|
181
|
+
...value !== void 0 && { value },
|
|
182
|
+
...userId && { user_id: userId },
|
|
183
|
+
...userName && { user_name: userName },
|
|
184
|
+
timestamp: Date.now(),
|
|
185
|
+
...additionalData
|
|
186
|
+
};
|
|
187
|
+
try {
|
|
188
|
+
window.gtag?.("event", action, eventData);
|
|
189
|
+
} catch {
|
|
190
|
+
}
|
|
191
|
+
try {
|
|
192
|
+
ReactGA__default.default.event({
|
|
193
|
+
category,
|
|
194
|
+
action,
|
|
195
|
+
label: label || void 0,
|
|
196
|
+
value: value || void 0,
|
|
197
|
+
...userId && { user_id: userId }
|
|
198
|
+
});
|
|
199
|
+
} catch {
|
|
200
|
+
}
|
|
201
|
+
if (currentConfig.debug) {
|
|
202
|
+
console.log(`[KorexTracking] Event: ${category}/${action}`, eventData);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
var resetAnalytics = () => {
|
|
206
|
+
isInitialized = false;
|
|
207
|
+
currentConfig = {};
|
|
208
|
+
resolvedTrackingId = "";
|
|
209
|
+
cachedUserId = null;
|
|
210
|
+
cachedUserName = null;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// src/core/gtm.ts
|
|
214
|
+
var injectGTMScript = (gtmId) => {
|
|
215
|
+
if (typeof window === "undefined") return;
|
|
216
|
+
if (!gtmId) return;
|
|
217
|
+
const existingScript = document.querySelector(`script[data-gtm-id="${gtmId}"]`);
|
|
218
|
+
if (existingScript) return;
|
|
219
|
+
window.dataLayer = window.dataLayer || [];
|
|
220
|
+
window.dataLayer.push({
|
|
221
|
+
"gtm.start": (/* @__PURE__ */ new Date()).getTime(),
|
|
222
|
+
event: "gtm.js"
|
|
223
|
+
});
|
|
224
|
+
const script = document.createElement("script");
|
|
225
|
+
script.async = true;
|
|
226
|
+
script.src = `https://www.googletagmanager.com/gtm.js?id=${gtmId}`;
|
|
227
|
+
script.setAttribute("data-gtm-id", gtmId);
|
|
228
|
+
const firstScript = document.getElementsByTagName("script")[0];
|
|
229
|
+
if (firstScript?.parentNode) {
|
|
230
|
+
firstScript.parentNode.insertBefore(script, firstScript);
|
|
231
|
+
} else {
|
|
232
|
+
document.head.appendChild(script);
|
|
233
|
+
}
|
|
234
|
+
if (document.body) {
|
|
235
|
+
const noscript = document.createElement("noscript");
|
|
236
|
+
const iframe = document.createElement("iframe");
|
|
237
|
+
iframe.src = `https://www.googletagmanager.com/ns.html?id=${gtmId}`;
|
|
238
|
+
iframe.height = "0";
|
|
239
|
+
iframe.width = "0";
|
|
240
|
+
iframe.style.display = "none";
|
|
241
|
+
iframe.style.visibility = "hidden";
|
|
242
|
+
noscript.appendChild(iframe);
|
|
243
|
+
document.body.insertBefore(noscript, document.body.firstChild);
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
var pushToDataLayer = (eventName, data) => {
|
|
247
|
+
if (typeof window === "undefined") return;
|
|
248
|
+
window.dataLayer = window.dataLayer || [];
|
|
249
|
+
window.dataLayer.push({
|
|
250
|
+
event: eventName,
|
|
251
|
+
...data
|
|
252
|
+
});
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
// src/trackers/video-tracker.ts
|
|
256
|
+
var VideoTracker = class {
|
|
257
|
+
constructor() {
|
|
258
|
+
this.videos = /* @__PURE__ */ new Map();
|
|
259
|
+
this.watchTimeIntervals = /* @__PURE__ */ new Map();
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Inicializa el tracking para un video.
|
|
263
|
+
*/
|
|
264
|
+
initVideo(videoId) {
|
|
265
|
+
if (this.videos.has(videoId)) return;
|
|
266
|
+
const state = {
|
|
267
|
+
videoId,
|
|
268
|
+
startTime: Date.now(),
|
|
269
|
+
totalWatchTime: 0,
|
|
270
|
+
playCount: 0,
|
|
271
|
+
pauseCount: 0,
|
|
272
|
+
seekCount: 0,
|
|
273
|
+
completionPercentage: 0,
|
|
274
|
+
lastPlayTimestamp: 0,
|
|
275
|
+
isPlaying: false,
|
|
276
|
+
currentTime: 0,
|
|
277
|
+
duration: 0,
|
|
278
|
+
pauseTimes: [],
|
|
279
|
+
seekEvents: [],
|
|
280
|
+
progressMilestones: /* @__PURE__ */ new Set(),
|
|
281
|
+
playbackSpeed: 1
|
|
282
|
+
};
|
|
283
|
+
this.videos.set(videoId, state);
|
|
284
|
+
trackEvent("Video", "init", videoId, void 0, {
|
|
285
|
+
video_id: videoId,
|
|
286
|
+
timestamp: Date.now()
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Tracking de evento play.
|
|
291
|
+
*/
|
|
292
|
+
trackPlay(videoId, currentTime = 0) {
|
|
293
|
+
const state = this.getOrCreateState(videoId);
|
|
294
|
+
state.playCount++;
|
|
295
|
+
state.isPlaying = true;
|
|
296
|
+
state.lastPlayTimestamp = Date.now();
|
|
297
|
+
state.currentTime = currentTime;
|
|
298
|
+
this.startWatchTimeTracking(videoId);
|
|
299
|
+
trackEvent("Video", "play", videoId, void 0, {
|
|
300
|
+
video_id: videoId,
|
|
301
|
+
play_count: state.playCount,
|
|
302
|
+
current_time: Math.round(currentTime)
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Tracking de evento pause.
|
|
307
|
+
*/
|
|
308
|
+
trackPause(videoId, currentTime, duration) {
|
|
309
|
+
const state = this.getOrCreateState(videoId);
|
|
310
|
+
state.pauseCount++;
|
|
311
|
+
state.isPlaying = false;
|
|
312
|
+
state.currentTime = currentTime;
|
|
313
|
+
state.pauseTimes.push(currentTime);
|
|
314
|
+
if (duration) {
|
|
315
|
+
state.duration = duration;
|
|
316
|
+
state.completionPercentage = Math.round(currentTime / duration * 100);
|
|
317
|
+
}
|
|
318
|
+
this.stopWatchTimeTracking(videoId);
|
|
319
|
+
trackEvent("Video", "pause", videoId, void 0, {
|
|
320
|
+
video_id: videoId,
|
|
321
|
+
pause_count: state.pauseCount,
|
|
322
|
+
current_time: Math.round(currentTime),
|
|
323
|
+
completion_percentage: state.completionPercentage,
|
|
324
|
+
total_watch_time: Math.round(state.totalWatchTime)
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Tracking de evento seek (saltar en el timeline).
|
|
329
|
+
*/
|
|
330
|
+
trackSeek(videoId, fromTime, toTime) {
|
|
331
|
+
const state = this.getOrCreateState(videoId);
|
|
332
|
+
state.seekCount++;
|
|
333
|
+
state.seekEvents.push({ from: fromTime, to: toTime });
|
|
334
|
+
trackEvent("Video", "seek", videoId, void 0, {
|
|
335
|
+
video_id: videoId,
|
|
336
|
+
seek_count: state.seekCount,
|
|
337
|
+
from_time: Math.round(fromTime),
|
|
338
|
+
to_time: Math.round(toTime),
|
|
339
|
+
skip_duration: Math.round(toTime - fromTime)
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Tracking de progreso (milestones: 25%, 50%, 75%).
|
|
344
|
+
*/
|
|
345
|
+
trackProgress(videoId, percentage, currentTime) {
|
|
346
|
+
const state = this.getOrCreateState(videoId);
|
|
347
|
+
const milestone = Math.floor(percentage / 25) * 25;
|
|
348
|
+
if (milestone > 0 && milestone < 100 && !state.progressMilestones.has(milestone)) {
|
|
349
|
+
state.progressMilestones.add(milestone);
|
|
350
|
+
trackEvent("Video", "progress", videoId, milestone, {
|
|
351
|
+
video_id: videoId,
|
|
352
|
+
progress_percentage: milestone,
|
|
353
|
+
current_time: Math.round(currentTime),
|
|
354
|
+
total_watch_time: Math.round(state.totalWatchTime)
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Tracking de completación del video (≥95%).
|
|
360
|
+
*/
|
|
361
|
+
trackComplete(videoId, totalDuration) {
|
|
362
|
+
const state = this.getOrCreateState(videoId);
|
|
363
|
+
state.completionPercentage = 100;
|
|
364
|
+
state.duration = totalDuration;
|
|
365
|
+
this.stopWatchTimeTracking(videoId);
|
|
366
|
+
trackEvent("Video", "complete", videoId, void 0, {
|
|
367
|
+
video_id: videoId,
|
|
368
|
+
total_duration: Math.round(totalDuration),
|
|
369
|
+
total_watch_time: Math.round(state.totalWatchTime),
|
|
370
|
+
play_count: state.playCount,
|
|
371
|
+
pause_count: state.pauseCount,
|
|
372
|
+
seek_count: state.seekCount
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Tracking de fin del video (evento ended nativo).
|
|
377
|
+
*/
|
|
378
|
+
trackEnd(videoId) {
|
|
379
|
+
const state = this.getOrCreateState(videoId);
|
|
380
|
+
state.isPlaying = false;
|
|
381
|
+
this.stopWatchTimeTracking(videoId);
|
|
382
|
+
trackEvent("Video", "end", videoId, void 0, {
|
|
383
|
+
video_id: videoId,
|
|
384
|
+
total_watch_time: Math.round(state.totalWatchTime),
|
|
385
|
+
play_count: state.playCount
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Tracking de cambio de velocidad de reproducción.
|
|
390
|
+
*/
|
|
391
|
+
trackSpeedChange(videoId, speed) {
|
|
392
|
+
const state = this.getOrCreateState(videoId);
|
|
393
|
+
state.playbackSpeed = speed;
|
|
394
|
+
trackEvent("Video", "speed_change", videoId, void 0, {
|
|
395
|
+
video_id: videoId,
|
|
396
|
+
playback_speed: speed
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Tracking de pantalla completa.
|
|
401
|
+
*/
|
|
402
|
+
trackFullscreen(videoId, isFullscreen) {
|
|
403
|
+
trackEvent("Video", isFullscreen ? "fullscreen_enter" : "fullscreen_exit", videoId, void 0, {
|
|
404
|
+
video_id: videoId,
|
|
405
|
+
is_fullscreen: isFullscreen
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Tracking de no interacción (el usuario está en la página pero no interactúa con el video).
|
|
410
|
+
*/
|
|
411
|
+
trackNoInteraction(videoId, timeOnPage) {
|
|
412
|
+
trackEvent("Video", "no_interaction", videoId, void 0, {
|
|
413
|
+
video_id: videoId,
|
|
414
|
+
time_on_page: Math.round(timeOnPage)
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Obtiene el estado actual de un video.
|
|
419
|
+
*/
|
|
420
|
+
getState(videoId) {
|
|
421
|
+
return this.videos.get(videoId);
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Limpia el tracking de un video específico.
|
|
425
|
+
*/
|
|
426
|
+
cleanup(videoId) {
|
|
427
|
+
this.stopWatchTimeTracking(videoId);
|
|
428
|
+
this.videos.delete(videoId);
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Limpia todo el tracking.
|
|
432
|
+
*/
|
|
433
|
+
cleanupAll() {
|
|
434
|
+
for (const videoId of this.videos.keys()) {
|
|
435
|
+
this.stopWatchTimeTracking(videoId);
|
|
436
|
+
}
|
|
437
|
+
this.videos.clear();
|
|
438
|
+
}
|
|
439
|
+
// --- Métodos privados ---
|
|
440
|
+
getOrCreateState(videoId) {
|
|
441
|
+
if (!this.videos.has(videoId)) {
|
|
442
|
+
this.initVideo(videoId);
|
|
443
|
+
}
|
|
444
|
+
return this.videos.get(videoId);
|
|
445
|
+
}
|
|
446
|
+
startWatchTimeTracking(videoId) {
|
|
447
|
+
this.stopWatchTimeTracking(videoId);
|
|
448
|
+
const interval = setInterval(() => {
|
|
449
|
+
const state = this.videos.get(videoId);
|
|
450
|
+
if (state?.isPlaying) {
|
|
451
|
+
state.totalWatchTime += 1;
|
|
452
|
+
}
|
|
453
|
+
}, 1e3);
|
|
454
|
+
this.watchTimeIntervals.set(videoId, interval);
|
|
455
|
+
}
|
|
456
|
+
stopWatchTimeTracking(videoId) {
|
|
457
|
+
const interval = this.watchTimeIntervals.get(videoId);
|
|
458
|
+
if (interval) {
|
|
459
|
+
clearInterval(interval);
|
|
460
|
+
this.watchTimeIntervals.delete(videoId);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
var videoTracker = new VideoTracker();
|
|
465
|
+
|
|
466
|
+
// src/trackers/events.ts
|
|
467
|
+
var trackCTAClick = (buttonName, section, additionalData) => {
|
|
468
|
+
trackEvent("CTA", "click", buttonName, void 0, {
|
|
469
|
+
button_name: buttonName,
|
|
470
|
+
section,
|
|
471
|
+
...additionalData
|
|
472
|
+
});
|
|
473
|
+
};
|
|
474
|
+
var trackFormStart = (formName) => {
|
|
475
|
+
trackEvent("Form", "started", formName, void 0, {
|
|
476
|
+
form_name: formName,
|
|
477
|
+
timestamp: Date.now()
|
|
478
|
+
});
|
|
479
|
+
};
|
|
480
|
+
var trackFormFieldComplete = (formName, fieldName) => {
|
|
481
|
+
trackEvent("Form", "field_completed", formName, void 0, {
|
|
482
|
+
form_name: formName,
|
|
483
|
+
field_name: fieldName
|
|
484
|
+
});
|
|
485
|
+
};
|
|
486
|
+
var trackFormSubmit = (formName, success, additionalData) => {
|
|
487
|
+
trackEvent("Form", success ? "submit_success" : "submit_error", formName, void 0, {
|
|
488
|
+
form_name: formName,
|
|
489
|
+
success,
|
|
490
|
+
...additionalData
|
|
491
|
+
});
|
|
492
|
+
};
|
|
493
|
+
var trackFormValidationError = (formName, fieldName, errorMessage) => {
|
|
494
|
+
trackEvent("Error", "validation", formName, void 0, {
|
|
495
|
+
form_name: formName,
|
|
496
|
+
field_name: fieldName,
|
|
497
|
+
error_message: errorMessage
|
|
498
|
+
});
|
|
499
|
+
};
|
|
500
|
+
var trackConversion = (conversionType, value, additionalData) => {
|
|
501
|
+
trackEvent("Conversion", conversionType, void 0, value, {
|
|
502
|
+
conversion_type: conversionType,
|
|
503
|
+
...additionalData
|
|
504
|
+
});
|
|
505
|
+
};
|
|
506
|
+
var trackSocialClick = (platform, action, additionalData) => {
|
|
507
|
+
trackEvent("Social", "click", platform, void 0, {
|
|
508
|
+
social_platform: platform,
|
|
509
|
+
social_action: action,
|
|
510
|
+
...additionalData
|
|
511
|
+
});
|
|
512
|
+
};
|
|
513
|
+
var trackFAQExpand = (question, index) => {
|
|
514
|
+
trackEvent("FAQ", "expand", question, index, {
|
|
515
|
+
question,
|
|
516
|
+
question_index: index
|
|
517
|
+
});
|
|
518
|
+
};
|
|
519
|
+
var trackImageClick = (imageName, section) => {
|
|
520
|
+
trackEvent("Engagement", "image_click", imageName, void 0, {
|
|
521
|
+
image_name: imageName,
|
|
522
|
+
section
|
|
523
|
+
});
|
|
524
|
+
};
|
|
525
|
+
var trackTimeInSection = (sectionName, seconds) => {
|
|
526
|
+
if (seconds < 3) return;
|
|
527
|
+
trackEvent("Engagement", "time_in_section", sectionName, Math.round(seconds), {
|
|
528
|
+
section_name: sectionName,
|
|
529
|
+
time_seconds: Math.round(seconds)
|
|
530
|
+
});
|
|
531
|
+
};
|
|
532
|
+
var trackSectionClick = (section) => {
|
|
533
|
+
trackEvent("Navigation", "section_click", section);
|
|
534
|
+
};
|
|
535
|
+
var trackScrollTo = (section) => {
|
|
536
|
+
trackEvent("Navigation", "scroll_to", section);
|
|
537
|
+
};
|
|
538
|
+
var trackPricingCardClick = (productName, price, additionalData) => {
|
|
539
|
+
trackEvent("Pricing", "card_click", productName, void 0, {
|
|
540
|
+
product_name: productName,
|
|
541
|
+
price: String(price),
|
|
542
|
+
...additionalData
|
|
543
|
+
});
|
|
544
|
+
};
|
|
545
|
+
var trackContactClick = (method) => {
|
|
546
|
+
trackEvent("Contact", "click", method, void 0, {
|
|
547
|
+
contact_method: method
|
|
548
|
+
});
|
|
549
|
+
};
|
|
550
|
+
var trackShare = (platform, content) => {
|
|
551
|
+
trackEvent("Share", "click", platform, void 0, {
|
|
552
|
+
share_platform: platform,
|
|
553
|
+
share_content: content
|
|
554
|
+
});
|
|
555
|
+
};
|
|
556
|
+
var trackDownload = (fileName, fileType) => {
|
|
557
|
+
trackEvent("Download", "click", fileName, void 0, {
|
|
558
|
+
file_name: fileName,
|
|
559
|
+
file_type: fileType
|
|
560
|
+
});
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
// src/trackers/wistia-adapter.ts
|
|
564
|
+
var createStats = (timeWatched, completed) => ({
|
|
565
|
+
timeWatched: Math.max(0, Math.round(timeWatched)),
|
|
566
|
+
completed,
|
|
567
|
+
lastUpdate: Date.now()
|
|
568
|
+
});
|
|
569
|
+
var trackWistiaByMediaId = async (mediaId) => {
|
|
570
|
+
if (typeof window === "undefined") return null;
|
|
571
|
+
let stats = createStats(0, false);
|
|
572
|
+
let updateCallbacks = [];
|
|
573
|
+
let stopped = false;
|
|
574
|
+
const callUpdate = () => {
|
|
575
|
+
updateCallbacks.forEach((cb) => cb(stats));
|
|
576
|
+
};
|
|
577
|
+
const bindWistiaSDK = () => {
|
|
578
|
+
if (!window._wq) return false;
|
|
579
|
+
window._wq.push({
|
|
580
|
+
id: mediaId,
|
|
581
|
+
onReady: (rawVideo) => {
|
|
582
|
+
if (stopped) return;
|
|
583
|
+
const video = rawVideo;
|
|
584
|
+
const updateFromWistia = () => {
|
|
585
|
+
const t = video.time();
|
|
586
|
+
const d = video.duration();
|
|
587
|
+
const pct = d > 0 ? Math.round(t / d * 100) : 0;
|
|
588
|
+
const isCompleted = stats.completed || pct >= 95;
|
|
589
|
+
const newTime = Math.max(stats.timeWatched, Math.round(t));
|
|
590
|
+
if (newTime !== stats.timeWatched || isCompleted !== stats.completed) {
|
|
591
|
+
stats = createStats(newTime, isCompleted);
|
|
592
|
+
callUpdate();
|
|
593
|
+
}
|
|
594
|
+
};
|
|
595
|
+
video.bind("play", updateFromWistia);
|
|
596
|
+
video.bind("pause", updateFromWistia);
|
|
597
|
+
video.bind("secondchange", updateFromWistia);
|
|
598
|
+
video.bind("end", () => {
|
|
599
|
+
stats = createStats(Math.round(video.duration()), true);
|
|
600
|
+
callUpdate();
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
return true;
|
|
605
|
+
};
|
|
606
|
+
const bindHTML5Fallback = () => {
|
|
607
|
+
const selector = `[data-wistia-id="${mediaId}"], .wistia_embed[data-media-id="${mediaId}"]`;
|
|
608
|
+
const container = document.querySelector(selector);
|
|
609
|
+
if (!container) return;
|
|
610
|
+
const video = container.querySelector("video");
|
|
611
|
+
if (!video) return;
|
|
612
|
+
const onTimeUpdate = () => {
|
|
613
|
+
if (stopped) return;
|
|
614
|
+
const ct = Math.round(video.currentTime || 0);
|
|
615
|
+
const dur = video.duration || 0;
|
|
616
|
+
const pct = dur > 0 ? Math.round(ct / dur * 100) : 0;
|
|
617
|
+
const isCompleted = stats.completed || pct >= 95 || video.ended;
|
|
618
|
+
const newTime = Math.max(stats.timeWatched, ct);
|
|
619
|
+
if (newTime !== stats.timeWatched || isCompleted !== stats.completed) {
|
|
620
|
+
stats = createStats(newTime, isCompleted);
|
|
621
|
+
callUpdate();
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
const onEnded = () => {
|
|
625
|
+
if (stopped) return;
|
|
626
|
+
stats = createStats(Math.max(stats.timeWatched, Math.round(video.duration || 0)), true);
|
|
627
|
+
callUpdate();
|
|
628
|
+
};
|
|
629
|
+
video.addEventListener("timeupdate", onTimeUpdate);
|
|
630
|
+
video.addEventListener("ended", onEnded);
|
|
631
|
+
};
|
|
632
|
+
const wistiaReady = bindWistiaSDK();
|
|
633
|
+
if (!wistiaReady) {
|
|
634
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
635
|
+
bindHTML5Fallback();
|
|
636
|
+
}
|
|
637
|
+
return {
|
|
638
|
+
getStats: () => stats,
|
|
639
|
+
onUpdate: (cb) => {
|
|
640
|
+
updateCallbacks.push(cb);
|
|
641
|
+
return () => {
|
|
642
|
+
updateCallbacks = updateCallbacks.filter((x) => x !== cb);
|
|
643
|
+
};
|
|
644
|
+
},
|
|
645
|
+
stop: () => {
|
|
646
|
+
stopped = true;
|
|
647
|
+
updateCallbacks = [];
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
};
|
|
651
|
+
|
|
652
|
+
// src/trackers/voomly-adapter.ts
|
|
653
|
+
var createStats2 = (timeWatched, completed) => ({
|
|
654
|
+
timeWatched: Math.max(0, Math.round(timeWatched)),
|
|
655
|
+
completed,
|
|
656
|
+
lastUpdate: Date.now()
|
|
657
|
+
});
|
|
658
|
+
var findAllVideosInElement = (root) => {
|
|
659
|
+
const results = [];
|
|
660
|
+
const videos = Array.from(root.querySelectorAll("video"));
|
|
661
|
+
results.push(...videos);
|
|
662
|
+
const allElements = root.querySelectorAll("*");
|
|
663
|
+
for (const el of Array.from(allElements)) {
|
|
664
|
+
if (el.shadowRoot) {
|
|
665
|
+
results.push(...findAllVideosInElement(el.shadowRoot));
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
const iframes = root.querySelectorAll("iframe");
|
|
669
|
+
for (const iframe of Array.from(iframes)) {
|
|
670
|
+
try {
|
|
671
|
+
const doc = iframe.contentDocument || iframe.contentWindow?.document;
|
|
672
|
+
if (doc) {
|
|
673
|
+
results.push(...findAllVideosInElement(doc));
|
|
674
|
+
}
|
|
675
|
+
} catch {
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
return results;
|
|
679
|
+
};
|
|
680
|
+
var trackVoomlyByEmbedId = async (embedId) => {
|
|
681
|
+
if (typeof window === "undefined") return null;
|
|
682
|
+
let stats = createStats2(0, false);
|
|
683
|
+
let updateCallbacks = [];
|
|
684
|
+
const trackedVideos = /* @__PURE__ */ new Set();
|
|
685
|
+
const callUpdate = () => {
|
|
686
|
+
updateCallbacks.forEach((cb) => cb(stats));
|
|
687
|
+
};
|
|
688
|
+
const updateStatsFromVideo = (video) => {
|
|
689
|
+
const ct = Math.round(video.currentTime || 0);
|
|
690
|
+
const dur = video.duration || 0;
|
|
691
|
+
const pct = dur ? Math.round(ct / dur * 100) : 0;
|
|
692
|
+
const isCompleted = stats.completed || pct >= 90 || video.ended;
|
|
693
|
+
const newTimeWatched = Math.max(stats.timeWatched, ct);
|
|
694
|
+
if (newTimeWatched !== stats.timeWatched || isCompleted !== stats.completed) {
|
|
695
|
+
stats = createStats2(newTimeWatched, isCompleted);
|
|
696
|
+
callUpdate();
|
|
697
|
+
}
|
|
698
|
+
};
|
|
699
|
+
const onPlay = (e) => updateStatsFromVideo(e.target);
|
|
700
|
+
const onPause = (e) => updateStatsFromVideo(e.target);
|
|
701
|
+
const onTime = (e) => updateStatsFromVideo(e.target);
|
|
702
|
+
const onEnd = (e) => updateStatsFromVideo(e.target);
|
|
703
|
+
const bindVideo = (video) => {
|
|
704
|
+
if (trackedVideos.has(video)) return;
|
|
705
|
+
trackedVideos.add(video);
|
|
706
|
+
video.addEventListener("play", onPlay);
|
|
707
|
+
video.addEventListener("pause", onPause);
|
|
708
|
+
video.addEventListener("timeupdate", onTime);
|
|
709
|
+
video.addEventListener("ended", onEnd);
|
|
710
|
+
};
|
|
711
|
+
const scanForVideos = () => {
|
|
712
|
+
const selector = `.voomly-embed[data-id="${embedId}"]`;
|
|
713
|
+
const container = document.querySelector(selector);
|
|
714
|
+
let foundVideos = [];
|
|
715
|
+
if (container) {
|
|
716
|
+
foundVideos = findAllVideosInElement(container);
|
|
717
|
+
}
|
|
718
|
+
if (foundVideos.length === 0) {
|
|
719
|
+
foundVideos = findAllVideosInElement(document);
|
|
720
|
+
}
|
|
721
|
+
foundVideos.forEach((v) => bindVideo(v));
|
|
722
|
+
};
|
|
723
|
+
scanForVideos();
|
|
724
|
+
const scanInterval = setInterval(scanForVideos, 2e3);
|
|
725
|
+
const pollingInterval = setInterval(() => {
|
|
726
|
+
trackedVideos.forEach((video) => {
|
|
727
|
+
const ct = Math.round(video.currentTime || 0);
|
|
728
|
+
if (!video.paused || ct > 0) {
|
|
729
|
+
updateStatsFromVideo(video);
|
|
730
|
+
}
|
|
731
|
+
});
|
|
732
|
+
}, 1e3);
|
|
733
|
+
const handleVoomlyWindowEvents = (e) => {
|
|
734
|
+
try {
|
|
735
|
+
const customEvent = e;
|
|
736
|
+
const payload = customEvent.detail || customEvent.payload;
|
|
737
|
+
const playerInfo = payload?.playerInfo || payload;
|
|
738
|
+
const id = playerInfo?.id || playerInfo?.videoId;
|
|
739
|
+
if (id && id !== embedId) return;
|
|
740
|
+
const currentTime = payload?.currentTime || payload?.time || 0;
|
|
741
|
+
const duration = payload?.duration || payload?.totalTime || 0;
|
|
742
|
+
let completed = false;
|
|
743
|
+
if (e.type === "voomly:video:ended" || e.type === "voomly:video:complete") {
|
|
744
|
+
completed = true;
|
|
745
|
+
}
|
|
746
|
+
if (currentTime > 0 || completed) {
|
|
747
|
+
const pct = duration ? Math.round(currentTime / duration * 100) : 0;
|
|
748
|
+
const isCompleted = stats.completed || completed || pct >= 90;
|
|
749
|
+
const newTimeWatched = Math.max(stats.timeWatched, Math.round(currentTime));
|
|
750
|
+
if (newTimeWatched !== stats.timeWatched || isCompleted !== stats.completed) {
|
|
751
|
+
stats = createStats2(newTimeWatched, isCompleted);
|
|
752
|
+
callUpdate();
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
} catch {
|
|
756
|
+
}
|
|
757
|
+
};
|
|
758
|
+
const windowEvents = ["voomly:video:play", "voomly:video:timeupdate", "voomly:video:ended", "voomly:video:progress"];
|
|
759
|
+
windowEvents.forEach((evt) => window.addEventListener(evt, handleVoomlyWindowEvents));
|
|
760
|
+
const stop = () => {
|
|
761
|
+
clearInterval(scanInterval);
|
|
762
|
+
clearInterval(pollingInterval);
|
|
763
|
+
trackedVideos.forEach((video) => {
|
|
764
|
+
video.removeEventListener("play", onPlay);
|
|
765
|
+
video.removeEventListener("pause", onPause);
|
|
766
|
+
video.removeEventListener("timeupdate", onTime);
|
|
767
|
+
video.removeEventListener("ended", onEnd);
|
|
768
|
+
});
|
|
769
|
+
windowEvents.forEach((evt) => window.removeEventListener(evt, handleVoomlyWindowEvents));
|
|
770
|
+
updateCallbacks = [];
|
|
771
|
+
};
|
|
772
|
+
return {
|
|
773
|
+
getStats: () => stats,
|
|
774
|
+
onUpdate: (cb) => {
|
|
775
|
+
updateCallbacks.push(cb);
|
|
776
|
+
return () => {
|
|
777
|
+
updateCallbacks = updateCallbacks.filter((x) => x !== cb);
|
|
778
|
+
};
|
|
779
|
+
},
|
|
780
|
+
stop
|
|
781
|
+
};
|
|
782
|
+
};
|
|
783
|
+
|
|
784
|
+
// src/trackers/html5-adapter.ts
|
|
785
|
+
var createStats3 = (timeWatched, completed) => ({
|
|
786
|
+
timeWatched: Math.max(0, Math.round(timeWatched)),
|
|
787
|
+
completed,
|
|
788
|
+
lastUpdate: Date.now()
|
|
789
|
+
});
|
|
790
|
+
var trackHTML5Video = (videoId, videoElement) => {
|
|
791
|
+
let stats = createStats3(0, false);
|
|
792
|
+
let updateCallbacks = [];
|
|
793
|
+
const trackedMilestones = /* @__PURE__ */ new Set();
|
|
794
|
+
const callUpdate = () => {
|
|
795
|
+
updateCallbacks.forEach((cb) => cb(stats));
|
|
796
|
+
};
|
|
797
|
+
const onTimeUpdate = () => {
|
|
798
|
+
const ct = Math.round(videoElement.currentTime || 0);
|
|
799
|
+
const dur = videoElement.duration || 0;
|
|
800
|
+
const pct = dur > 0 ? Math.round(ct / dur * 100) : 0;
|
|
801
|
+
const newTime = Math.max(stats.timeWatched, ct);
|
|
802
|
+
const isCompleted = stats.completed || pct >= 95 || videoElement.ended;
|
|
803
|
+
if (newTime !== stats.timeWatched || isCompleted !== stats.completed) {
|
|
804
|
+
stats = createStats3(newTime, isCompleted);
|
|
805
|
+
callUpdate();
|
|
806
|
+
}
|
|
807
|
+
for (const milestone of [25, 50, 75]) {
|
|
808
|
+
if (pct >= milestone && !trackedMilestones.has(milestone)) {
|
|
809
|
+
trackedMilestones.add(milestone);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
const onEnded = () => {
|
|
814
|
+
const dur = Math.round(videoElement.duration || 0);
|
|
815
|
+
stats = createStats3(Math.max(stats.timeWatched, dur), true);
|
|
816
|
+
callUpdate();
|
|
817
|
+
};
|
|
818
|
+
const onPlay = () => {
|
|
819
|
+
onTimeUpdate();
|
|
820
|
+
};
|
|
821
|
+
const onPause = () => {
|
|
822
|
+
onTimeUpdate();
|
|
823
|
+
};
|
|
824
|
+
videoElement.addEventListener("timeupdate", onTimeUpdate);
|
|
825
|
+
videoElement.addEventListener("ended", onEnded);
|
|
826
|
+
videoElement.addEventListener("play", onPlay);
|
|
827
|
+
videoElement.addEventListener("pause", onPause);
|
|
828
|
+
return {
|
|
829
|
+
getStats: () => stats,
|
|
830
|
+
onUpdate: (cb) => {
|
|
831
|
+
updateCallbacks.push(cb);
|
|
832
|
+
return () => {
|
|
833
|
+
updateCallbacks = updateCallbacks.filter((x) => x !== cb);
|
|
834
|
+
};
|
|
835
|
+
},
|
|
836
|
+
stop: () => {
|
|
837
|
+
videoElement.removeEventListener("timeupdate", onTimeUpdate);
|
|
838
|
+
videoElement.removeEventListener("ended", onEnded);
|
|
839
|
+
videoElement.removeEventListener("play", onPlay);
|
|
840
|
+
videoElement.removeEventListener("pause", onPause);
|
|
841
|
+
updateCallbacks = [];
|
|
842
|
+
}
|
|
843
|
+
};
|
|
844
|
+
};
|
|
845
|
+
|
|
846
|
+
// src/orchestrator/landing-tracker.ts
|
|
847
|
+
var LandingTracker = class {
|
|
848
|
+
constructor() {
|
|
849
|
+
this.config = null;
|
|
850
|
+
this.isInitialized = false;
|
|
851
|
+
this.sessionStartTime = 0;
|
|
852
|
+
this.scrollMilestonesReached = /* @__PURE__ */ new Set();
|
|
853
|
+
this.clicks = [];
|
|
854
|
+
this.sectionTimers = /* @__PURE__ */ new Map();
|
|
855
|
+
this.observers = [];
|
|
856
|
+
this.listeners = [];
|
|
857
|
+
}
|
|
858
|
+
/**
|
|
859
|
+
* Inicializa el tracking de la landing con la configuración dada.
|
|
860
|
+
*/
|
|
861
|
+
init(config) {
|
|
862
|
+
if (typeof window === "undefined") return;
|
|
863
|
+
if (this.isInitialized) return;
|
|
864
|
+
this.config = {
|
|
865
|
+
enableScrollTracking: true,
|
|
866
|
+
enableCTATracking: true,
|
|
867
|
+
enableTimeTracking: true,
|
|
868
|
+
enableSectionTracking: true,
|
|
869
|
+
enableFormTracking: true,
|
|
870
|
+
enableHeatmap: false,
|
|
871
|
+
eventSuffix: "",
|
|
872
|
+
...config
|
|
873
|
+
};
|
|
874
|
+
this.isInitialized = true;
|
|
875
|
+
this.sessionStartTime = Date.now();
|
|
876
|
+
this.captureInitialData();
|
|
877
|
+
trackPageView(this.config.pagePath);
|
|
878
|
+
if (this.config.gtmId) {
|
|
879
|
+
injectGTMScript(this.config.gtmId);
|
|
880
|
+
pushToDataLayer(this.getEventName("page_load"), {
|
|
881
|
+
page_path: this.config.pagePath,
|
|
882
|
+
page_title: this.config.pageName,
|
|
883
|
+
timestamp: Date.now()
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
if (this.config.enableScrollTracking) this.initScrollTracking();
|
|
887
|
+
if (this.config.enableCTATracking) this.initCTATracking();
|
|
888
|
+
if (this.config.enableTimeTracking) this.initTimeTracking();
|
|
889
|
+
if (this.config.enableSectionTracking) this.initSectionTracking();
|
|
890
|
+
if (this.config.enableFormTracking) this.initFormTracking();
|
|
891
|
+
if (this.config.enableHeatmap) this.initClickHeatmap();
|
|
892
|
+
this.trackSessionStart();
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* Destruye el tracker y limpia todos los observers y listeners.
|
|
896
|
+
*/
|
|
897
|
+
destroy() {
|
|
898
|
+
this.observers.forEach((obs) => obs.disconnect());
|
|
899
|
+
this.observers = [];
|
|
900
|
+
this.listeners.forEach(({ target, event, handler }) => {
|
|
901
|
+
target.removeEventListener(event, handler);
|
|
902
|
+
});
|
|
903
|
+
this.listeners = [];
|
|
904
|
+
this.isInitialized = false;
|
|
905
|
+
this.config = null;
|
|
906
|
+
this.scrollMilestonesReached.clear();
|
|
907
|
+
this.clicks = [];
|
|
908
|
+
this.sectionTimers.clear();
|
|
909
|
+
}
|
|
910
|
+
// ====================================
|
|
911
|
+
// MÉTODOS PÚBLICOS DE TRACKING
|
|
912
|
+
// ====================================
|
|
913
|
+
/** Trackear click en un CTA */
|
|
914
|
+
trackCTAClick(buttonName, section, additionalData) {
|
|
915
|
+
trackCTAClick(buttonName, section, additionalData);
|
|
916
|
+
this.pushGTMEvent("cta_click", { button_text: buttonName, section });
|
|
917
|
+
}
|
|
918
|
+
/** Trackear conversión */
|
|
919
|
+
trackConversion(type, value, additionalData) {
|
|
920
|
+
trackEvent("Conversion", type, void 0, value, additionalData);
|
|
921
|
+
this.pushGTMEvent("conversion", { conversion_type: type, value });
|
|
922
|
+
}
|
|
923
|
+
/** Trackear click en FAQs */
|
|
924
|
+
trackFAQExpand(question, index) {
|
|
925
|
+
trackEvent("FAQ", "expand", question, index);
|
|
926
|
+
}
|
|
927
|
+
/** Trackear click social */
|
|
928
|
+
trackSocialClick(platform, action) {
|
|
929
|
+
trackEvent("Social", "click", platform, void 0, { social_action: action });
|
|
930
|
+
}
|
|
931
|
+
/** Trackear click en imagen */
|
|
932
|
+
trackImageClick(imageName, section) {
|
|
933
|
+
trackEvent("Engagement", "image_click", imageName, void 0, { section });
|
|
934
|
+
}
|
|
935
|
+
/** Trackear scroll a sección */
|
|
936
|
+
trackScrollTo(section) {
|
|
937
|
+
trackEvent("Navigation", "scroll_to", section);
|
|
938
|
+
}
|
|
939
|
+
/** Trackear share */
|
|
940
|
+
trackShare(platform, content) {
|
|
941
|
+
trackEvent("Share", "click", platform, void 0, { share_content: content });
|
|
942
|
+
}
|
|
943
|
+
/** Trackear descarga */
|
|
944
|
+
trackDownload(fileName, fileType) {
|
|
945
|
+
trackEvent("Download", "click", fileName, void 0, { file_type: fileType });
|
|
946
|
+
}
|
|
947
|
+
/** Obtener datos de la sesión actual */
|
|
948
|
+
getSessionData() {
|
|
949
|
+
return {
|
|
950
|
+
duration: Math.round((Date.now() - this.sessionStartTime) / 1e3),
|
|
951
|
+
clicks: this.clicks.length,
|
|
952
|
+
scrollMilestones: Array.from(this.scrollMilestonesReached)
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
// ====================================
|
|
956
|
+
// MÉTODOS PRIVADOS
|
|
957
|
+
// ====================================
|
|
958
|
+
captureInitialData() {
|
|
959
|
+
const utmParams = captureUTMParams();
|
|
960
|
+
const userId = captureUserIdFromURL();
|
|
961
|
+
const userName = getUserName();
|
|
962
|
+
const prefix = this.config.pagePath.replace("/", "");
|
|
963
|
+
try {
|
|
964
|
+
localStorage.setItem(`${prefix}_entry_time`, (/* @__PURE__ */ new Date()).toISOString());
|
|
965
|
+
if (utmParams) {
|
|
966
|
+
localStorage.setItem(`${prefix}_utm_params`, JSON.stringify(utmParams));
|
|
967
|
+
}
|
|
968
|
+
if (userId) {
|
|
969
|
+
localStorage.setItem(`${prefix}_user_id`, userId);
|
|
970
|
+
}
|
|
971
|
+
} catch {
|
|
972
|
+
}
|
|
973
|
+
trackEvent("Landing", "page_load", this.config.pageName, void 0, {
|
|
974
|
+
page_path: this.config.pagePath,
|
|
975
|
+
user_id: userId,
|
|
976
|
+
user_name: userName,
|
|
977
|
+
utm_params: utmParams,
|
|
978
|
+
timestamp: Date.now()
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
trackSessionStart() {
|
|
982
|
+
trackEvent("Session", "start", this.config.pageName, void 0, {
|
|
983
|
+
page_path: this.config.pagePath,
|
|
984
|
+
timestamp: Date.now()
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
getEventName(baseName) {
|
|
988
|
+
const suffix = this.config?.eventSuffix || "";
|
|
989
|
+
return suffix ? `${baseName}${suffix}` : baseName;
|
|
990
|
+
}
|
|
991
|
+
pushGTMEvent(eventName, data) {
|
|
992
|
+
if (this.config?.gtmId) {
|
|
993
|
+
pushToDataLayer(this.getEventName(eventName), {
|
|
994
|
+
page_path: this.config.pagePath,
|
|
995
|
+
...data
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
addListener(target, event, handler) {
|
|
1000
|
+
target.addEventListener(event, handler);
|
|
1001
|
+
this.listeners.push({ target, event, handler });
|
|
1002
|
+
}
|
|
1003
|
+
// --- Scroll Tracking ---
|
|
1004
|
+
initScrollTracking() {
|
|
1005
|
+
let scrollTimeout;
|
|
1006
|
+
const handler = () => {
|
|
1007
|
+
clearTimeout(scrollTimeout);
|
|
1008
|
+
scrollTimeout = setTimeout(() => {
|
|
1009
|
+
const scrollHeight = document.documentElement.scrollHeight - window.innerHeight;
|
|
1010
|
+
if (scrollHeight <= 0) return;
|
|
1011
|
+
const pct = Math.round(window.scrollY / scrollHeight * 100);
|
|
1012
|
+
for (const milestone of [25, 50, 75, 100]) {
|
|
1013
|
+
if (pct >= milestone && !this.scrollMilestonesReached.has(milestone)) {
|
|
1014
|
+
this.scrollMilestonesReached.add(milestone);
|
|
1015
|
+
trackEvent("Scroll", "milestone", `${milestone}%`, milestone, {
|
|
1016
|
+
page_path: this.config.pagePath,
|
|
1017
|
+
scroll_percentage: milestone
|
|
1018
|
+
});
|
|
1019
|
+
this.pushGTMEvent("scroll_milestone", { scroll_percentage: milestone });
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
}, 100);
|
|
1023
|
+
};
|
|
1024
|
+
this.addListener(window, "scroll", handler);
|
|
1025
|
+
}
|
|
1026
|
+
// --- CTA Tracking ---
|
|
1027
|
+
initCTATracking() {
|
|
1028
|
+
setTimeout(() => {
|
|
1029
|
+
const ctaButtons = document.querySelectorAll('button, a[href*="#"], [data-cta]');
|
|
1030
|
+
ctaButtons.forEach((button) => {
|
|
1031
|
+
const handler = () => {
|
|
1032
|
+
const el = button;
|
|
1033
|
+
const text = el.textContent?.trim() || el.getAttribute("data-cta") || "Unknown CTA";
|
|
1034
|
+
const section = el.closest("section")?.id || el.closest("[data-section]")?.getAttribute("data-section") || "unknown";
|
|
1035
|
+
this.trackCTAClick(text, section);
|
|
1036
|
+
};
|
|
1037
|
+
this.addListener(button, "click", handler);
|
|
1038
|
+
});
|
|
1039
|
+
}, 2e3);
|
|
1040
|
+
}
|
|
1041
|
+
// --- Time Tracking (page exit) ---
|
|
1042
|
+
initTimeTracking() {
|
|
1043
|
+
const handler = () => {
|
|
1044
|
+
const duration = Math.round((Date.now() - this.sessionStartTime) / 1e3);
|
|
1045
|
+
trackEvent("Session", "end", this.config.pageName, duration, {
|
|
1046
|
+
session_duration: duration,
|
|
1047
|
+
page_path: this.config.pagePath,
|
|
1048
|
+
exit_timestamp: Date.now()
|
|
1049
|
+
});
|
|
1050
|
+
this.pushGTMEvent("page_exit", {
|
|
1051
|
+
session_duration: duration,
|
|
1052
|
+
exit_timestamp: Date.now()
|
|
1053
|
+
});
|
|
1054
|
+
};
|
|
1055
|
+
this.addListener(window, "beforeunload", handler);
|
|
1056
|
+
}
|
|
1057
|
+
// --- Section Dwell Time (IntersectionObserver) ---
|
|
1058
|
+
initSectionTracking() {
|
|
1059
|
+
const sectionEntryTimes = /* @__PURE__ */ new Map();
|
|
1060
|
+
const observer = new IntersectionObserver(
|
|
1061
|
+
(entries) => {
|
|
1062
|
+
entries.forEach((entry) => {
|
|
1063
|
+
const sectionId = entry.target.id || entry.target.getAttribute("data-section") || "unknown";
|
|
1064
|
+
if (entry.isIntersecting) {
|
|
1065
|
+
sectionEntryTimes.set(sectionId, Date.now());
|
|
1066
|
+
} else {
|
|
1067
|
+
const entryTime = sectionEntryTimes.get(sectionId);
|
|
1068
|
+
if (entryTime) {
|
|
1069
|
+
const seconds = (Date.now() - entryTime) / 1e3;
|
|
1070
|
+
trackTimeInSection(sectionId, seconds);
|
|
1071
|
+
sectionEntryTimes.delete(sectionId);
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
});
|
|
1075
|
+
},
|
|
1076
|
+
{ threshold: 0.5 }
|
|
1077
|
+
);
|
|
1078
|
+
setTimeout(() => {
|
|
1079
|
+
const sections = document.querySelectorAll("section, [data-section]");
|
|
1080
|
+
sections.forEach((section) => observer.observe(section));
|
|
1081
|
+
}, 1e3);
|
|
1082
|
+
this.observers.push(observer);
|
|
1083
|
+
}
|
|
1084
|
+
// --- Form Auto-Tracking ---
|
|
1085
|
+
initFormTracking() {
|
|
1086
|
+
setTimeout(() => {
|
|
1087
|
+
const forms = document.querySelectorAll("form");
|
|
1088
|
+
forms.forEach((form) => {
|
|
1089
|
+
const formName = form.getAttribute("name") || form.id || "unknown-form";
|
|
1090
|
+
let started = false;
|
|
1091
|
+
const inputs = form.querySelectorAll("input, textarea, select");
|
|
1092
|
+
inputs.forEach((input) => {
|
|
1093
|
+
this.addListener(input, "focus", (() => {
|
|
1094
|
+
if (!started) {
|
|
1095
|
+
started = true;
|
|
1096
|
+
trackFormStart(formName);
|
|
1097
|
+
}
|
|
1098
|
+
}));
|
|
1099
|
+
});
|
|
1100
|
+
this.addListener(form, "submit", ((_e) => {
|
|
1101
|
+
trackFormSubmit(formName, true, {
|
|
1102
|
+
page_path: this.config.pagePath
|
|
1103
|
+
});
|
|
1104
|
+
this.pushGTMEvent("form_submit", { form_name: formName });
|
|
1105
|
+
}));
|
|
1106
|
+
});
|
|
1107
|
+
}, 2e3);
|
|
1108
|
+
}
|
|
1109
|
+
// --- Click Heatmap ---
|
|
1110
|
+
initClickHeatmap() {
|
|
1111
|
+
const handler = (e) => {
|
|
1112
|
+
const mouseEvent = e;
|
|
1113
|
+
const target = mouseEvent.target;
|
|
1114
|
+
const clickData = {
|
|
1115
|
+
x: mouseEvent.clientX,
|
|
1116
|
+
y: mouseEvent.clientY,
|
|
1117
|
+
element: target.tagName.toLowerCase(),
|
|
1118
|
+
section: target.closest("section")?.id || "unknown",
|
|
1119
|
+
timestamp: Date.now(),
|
|
1120
|
+
viewportWidth: window.innerWidth,
|
|
1121
|
+
viewportHeight: window.innerHeight
|
|
1122
|
+
};
|
|
1123
|
+
this.clicks.push(clickData);
|
|
1124
|
+
};
|
|
1125
|
+
this.addListener(document, "click", handler);
|
|
1126
|
+
}
|
|
1127
|
+
};
|
|
1128
|
+
var createLandingTracker = (config) => {
|
|
1129
|
+
const tracker = new LandingTracker();
|
|
1130
|
+
tracker.init(config);
|
|
1131
|
+
return tracker;
|
|
1132
|
+
};
|
|
1133
|
+
|
|
1134
|
+
// src/persistence/api-client.ts
|
|
1135
|
+
var apiConfig = null;
|
|
1136
|
+
var initTrackingAPI = (config) => {
|
|
1137
|
+
apiConfig = {
|
|
1138
|
+
...config,
|
|
1139
|
+
baseUrl: config.baseUrl.replace(/\/+$/, "")
|
|
1140
|
+
};
|
|
1141
|
+
};
|
|
1142
|
+
var getAPIConfig = () => {
|
|
1143
|
+
if (!apiConfig) {
|
|
1144
|
+
throw new Error("[@metodokorexmk/tracking] initTrackingAPI() debe llamarse antes de usar funciones de persistencia.");
|
|
1145
|
+
}
|
|
1146
|
+
return apiConfig;
|
|
1147
|
+
};
|
|
1148
|
+
var isTrackingAPIInitialized = () => apiConfig !== null;
|
|
1149
|
+
var buildHeaders = () => {
|
|
1150
|
+
const config = getAPIConfig();
|
|
1151
|
+
const headers = {
|
|
1152
|
+
"Content-Type": "application/json"
|
|
1153
|
+
};
|
|
1154
|
+
if (config.getAuthToken) {
|
|
1155
|
+
const token = config.getAuthToken();
|
|
1156
|
+
if (token) {
|
|
1157
|
+
headers.Authorization = `Bearer ${token}`;
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
if (config.getCustomHeaders) {
|
|
1161
|
+
Object.assign(headers, config.getCustomHeaders());
|
|
1162
|
+
}
|
|
1163
|
+
return headers;
|
|
1164
|
+
};
|
|
1165
|
+
var apiGet = async (path) => {
|
|
1166
|
+
const config = getAPIConfig();
|
|
1167
|
+
const url = `${config.baseUrl}${path}`;
|
|
1168
|
+
const response = await fetch(url, {
|
|
1169
|
+
method: "GET",
|
|
1170
|
+
headers: buildHeaders(),
|
|
1171
|
+
credentials: "include"
|
|
1172
|
+
});
|
|
1173
|
+
if (!response.ok) {
|
|
1174
|
+
const errorText = await response.text().catch(() => "");
|
|
1175
|
+
throw new Error(`API GET ${path} failed (${response.status}): ${errorText}`);
|
|
1176
|
+
}
|
|
1177
|
+
return response.json();
|
|
1178
|
+
};
|
|
1179
|
+
var apiPost = async (path, body) => {
|
|
1180
|
+
const config = getAPIConfig();
|
|
1181
|
+
const url = `${config.baseUrl}${path}`;
|
|
1182
|
+
const response = await fetch(url, {
|
|
1183
|
+
method: "POST",
|
|
1184
|
+
headers: buildHeaders(),
|
|
1185
|
+
body: JSON.stringify(body)
|
|
1186
|
+
});
|
|
1187
|
+
if (!response.ok) {
|
|
1188
|
+
const errorText = await response.text().catch(() => "");
|
|
1189
|
+
throw new Error(`API POST ${path} failed (${response.status}): ${errorText}`);
|
|
1190
|
+
}
|
|
1191
|
+
return response.json();
|
|
1192
|
+
};
|
|
1193
|
+
|
|
1194
|
+
// src/persistence/lead-submission.ts
|
|
1195
|
+
var extractUrlParams = (url) => {
|
|
1196
|
+
if (typeof window === "undefined" && !url) {
|
|
1197
|
+
return {
|
|
1198
|
+
userId: "",
|
|
1199
|
+
idCampana: "",
|
|
1200
|
+
utmContent: "",
|
|
1201
|
+
fbclid: "",
|
|
1202
|
+
nombreNetworker: "",
|
|
1203
|
+
whatsapp: "",
|
|
1204
|
+
pipelineId: "",
|
|
1205
|
+
leadCampaing: "Funnel Organico",
|
|
1206
|
+
isOrganico: true
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
let searchParams;
|
|
1210
|
+
if (url) {
|
|
1211
|
+
try {
|
|
1212
|
+
const urlObj = new URL(url);
|
|
1213
|
+
searchParams = new URLSearchParams(urlObj.search);
|
|
1214
|
+
} catch {
|
|
1215
|
+
searchParams = new URLSearchParams();
|
|
1216
|
+
}
|
|
1217
|
+
} else {
|
|
1218
|
+
searchParams = new URLSearchParams(window.location.search);
|
|
1219
|
+
}
|
|
1220
|
+
const userId = searchParams.get("user_id") || "";
|
|
1221
|
+
const idCampana = searchParams.get("campaign_id") || searchParams.get("utm_id") || "";
|
|
1222
|
+
const utmContent = searchParams.get("utm_content") || "";
|
|
1223
|
+
const fbclid = searchParams.get("fbclid") || "";
|
|
1224
|
+
const whatsapp = searchParams.get("whatsapp") || "";
|
|
1225
|
+
const nombreNetworker = searchParams.get("user_name") || searchParams.get("networker") || searchParams.get("name") || "";
|
|
1226
|
+
const pipelineId = searchParams.get("pipeline_id") || "";
|
|
1227
|
+
const funnelId = searchParams.get("funnel_id") || "";
|
|
1228
|
+
const metricId = searchParams.get("metric_id") || "";
|
|
1229
|
+
const utmSource = searchParams.get("utm_source") || "";
|
|
1230
|
+
const utmMedium = searchParams.get("utm_medium") || "";
|
|
1231
|
+
const utmCampaign = searchParams.get("utm_campaign") || "";
|
|
1232
|
+
const isOrganico = !!(whatsapp && !idCampana && !fbclid);
|
|
1233
|
+
const leadCampaing = fbclid ? "Funnel FB" : "Funnel Organico";
|
|
1234
|
+
return {
|
|
1235
|
+
userId,
|
|
1236
|
+
idCampana,
|
|
1237
|
+
utmContent,
|
|
1238
|
+
fbclid,
|
|
1239
|
+
nombreNetworker,
|
|
1240
|
+
whatsapp,
|
|
1241
|
+
pipelineId,
|
|
1242
|
+
funnelId,
|
|
1243
|
+
metricId,
|
|
1244
|
+
utmSource,
|
|
1245
|
+
utmMedium,
|
|
1246
|
+
utmCampaign,
|
|
1247
|
+
leadCampaing,
|
|
1248
|
+
isOrganico
|
|
1249
|
+
};
|
|
1250
|
+
};
|
|
1251
|
+
var detectLeadOrigin = (url) => {
|
|
1252
|
+
const params = extractUrlParams(url);
|
|
1253
|
+
const hasCampaign = Boolean(params.idCampana || params.fbclid);
|
|
1254
|
+
const type = hasCampaign ? "facebook" : "organic";
|
|
1255
|
+
const endpoint = hasCampaign ? "/tracking/create-with-lead-and-distribution" : "/tracking/create-organic-lead";
|
|
1256
|
+
return { type, endpoint, params, hasCampaign };
|
|
1257
|
+
};
|
|
1258
|
+
var buildTrackingRequestBody = (params, formData, origin, options) => {
|
|
1259
|
+
const collaboratorId = params.userId ? parseInt(params.userId, 10) : null;
|
|
1260
|
+
const telefonoLimpio = formData.telefono.replace(/\s+/g, "");
|
|
1261
|
+
const body = {
|
|
1262
|
+
userName: formData.nombre.trim(),
|
|
1263
|
+
userEmail: formData.correo.trim(),
|
|
1264
|
+
entryDate: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1265
|
+
accessUrl: typeof window !== "undefined" ? window.location.href : "",
|
|
1266
|
+
leadName: formData.nombre.trim(),
|
|
1267
|
+
leadEmail: formData.correo.trim(),
|
|
1268
|
+
leadPhone: formData.countryLada ? `${formData.countryLada}${telefonoLimpio}` : telefonoLimpio
|
|
1269
|
+
};
|
|
1270
|
+
if (options?.nameForm) body.nameForm = options.nameForm;
|
|
1271
|
+
if (options?.msgForm) body.msgForm = options.msgForm;
|
|
1272
|
+
if (options?.status) body.status = options.status;
|
|
1273
|
+
if (options?.whatsappSent !== void 0) body.whatsapp_sent = options.whatsappSent;
|
|
1274
|
+
if (options?.dwellTime !== void 0) body.dwellTime = options.dwellTime;
|
|
1275
|
+
if (options?.videoTimeWatched !== void 0) body.videoTimeWatched = options.videoTimeWatched;
|
|
1276
|
+
if (options?.videoCompleted !== void 0) {
|
|
1277
|
+
body.videoCompleted = options.videoCompleted;
|
|
1278
|
+
body.video_completed = options.videoCompleted;
|
|
1279
|
+
}
|
|
1280
|
+
if (origin.hasCampaign) {
|
|
1281
|
+
if (collaboratorId) body.userId = collaboratorId;
|
|
1282
|
+
if (params.idCampana) body.idCampana = params.idCampana;
|
|
1283
|
+
if (params.utmContent) body.utmContent = params.utmContent;
|
|
1284
|
+
if (params.fbclid) body.fbclid = params.fbclid;
|
|
1285
|
+
} else if (collaboratorId) {
|
|
1286
|
+
body.referringUserId = collaboratorId;
|
|
1287
|
+
body.referring_user_id = collaboratorId;
|
|
1288
|
+
}
|
|
1289
|
+
if (params.pipelineId) {
|
|
1290
|
+
const parsed = parseInt(params.pipelineId, 10);
|
|
1291
|
+
if (!isNaN(parsed)) body.pipeline_id = parsed;
|
|
1292
|
+
}
|
|
1293
|
+
if (params.funnelId) {
|
|
1294
|
+
const parsed = parseInt(params.funnelId, 10);
|
|
1295
|
+
if (!isNaN(parsed)) body.funnel_id = parsed;
|
|
1296
|
+
}
|
|
1297
|
+
if (params.metricId) {
|
|
1298
|
+
const parsed = parseInt(params.metricId, 10);
|
|
1299
|
+
if (!isNaN(parsed)) body.metric_id = parsed;
|
|
1300
|
+
}
|
|
1301
|
+
if (options?.additionalFields) {
|
|
1302
|
+
Object.assign(body, options.additionalFields);
|
|
1303
|
+
}
|
|
1304
|
+
return body;
|
|
1305
|
+
};
|
|
1306
|
+
var submitTrackingLead = async (body, origin) => {
|
|
1307
|
+
const config = getAPIConfig();
|
|
1308
|
+
const fullUrl = `${config.baseUrl}${origin.endpoint}`;
|
|
1309
|
+
try {
|
|
1310
|
+
const response = await fetch(fullUrl, {
|
|
1311
|
+
method: "POST",
|
|
1312
|
+
headers: { "Content-Type": "application/json" },
|
|
1313
|
+
body: JSON.stringify(body)
|
|
1314
|
+
});
|
|
1315
|
+
let data = null;
|
|
1316
|
+
try {
|
|
1317
|
+
const text = await response.text();
|
|
1318
|
+
if (text) {
|
|
1319
|
+
const parsed = JSON.parse(text);
|
|
1320
|
+
if (parsed && typeof parsed === "object") {
|
|
1321
|
+
data = parsed;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
} catch {
|
|
1325
|
+
}
|
|
1326
|
+
return { ok: response.ok, status: response.status, data };
|
|
1327
|
+
} catch {
|
|
1328
|
+
return { ok: false, status: 0, data: null };
|
|
1329
|
+
}
|
|
1330
|
+
};
|
|
1331
|
+
var submitLead = async (formData, options, url) => {
|
|
1332
|
+
const origin = detectLeadOrigin(url);
|
|
1333
|
+
const body = buildTrackingRequestBody(origin.params, formData, origin, options);
|
|
1334
|
+
return submitTrackingLead(body, origin);
|
|
1335
|
+
};
|
|
1336
|
+
var updateTrackingLead = async (trackingId, updates) => {
|
|
1337
|
+
const config = getAPIConfig();
|
|
1338
|
+
const url = `${config.baseUrl}/tracking/${trackingId}`;
|
|
1339
|
+
try {
|
|
1340
|
+
const response = await fetch(url, {
|
|
1341
|
+
method: "PATCH",
|
|
1342
|
+
headers: { "Content-Type": "application/json" },
|
|
1343
|
+
body: JSON.stringify(updates)
|
|
1344
|
+
});
|
|
1345
|
+
return response.ok;
|
|
1346
|
+
} catch {
|
|
1347
|
+
return false;
|
|
1348
|
+
}
|
|
1349
|
+
};
|
|
1350
|
+
var markWhatsAppSent = async (leadId) => {
|
|
1351
|
+
const config = getAPIConfig();
|
|
1352
|
+
const url = `${config.baseUrl}/tracking/lead/${leadId}/whatsapp-sent`;
|
|
1353
|
+
try {
|
|
1354
|
+
const response = await fetch(url, {
|
|
1355
|
+
method: "PATCH",
|
|
1356
|
+
headers: { "Content-Type": "application/json" },
|
|
1357
|
+
body: JSON.stringify({ whatsapp_sent: true })
|
|
1358
|
+
});
|
|
1359
|
+
return response.ok;
|
|
1360
|
+
} catch {
|
|
1361
|
+
return false;
|
|
1362
|
+
}
|
|
1363
|
+
};
|
|
1364
|
+
var buildMsgFormJSON = (mensaje, datosAdicionales) => {
|
|
1365
|
+
return JSON.stringify({ mensaje: mensaje || "", ...datosAdicionales });
|
|
1366
|
+
};
|
|
1367
|
+
|
|
1368
|
+
// src/persistence/dwell-time-tracker.ts
|
|
1369
|
+
var DwellTimeTracker = class {
|
|
1370
|
+
constructor() {
|
|
1371
|
+
this.startTime = null;
|
|
1372
|
+
this.trackingDataId = null;
|
|
1373
|
+
this.enabled = false;
|
|
1374
|
+
this.intervalId = null;
|
|
1375
|
+
this.isSending = false;
|
|
1376
|
+
this.videoTimeWatched = 0;
|
|
1377
|
+
this.videoCompleted = false;
|
|
1378
|
+
this.videoTrackingStopped = false;
|
|
1379
|
+
this.lastDwellTimeSent = 0;
|
|
1380
|
+
this.lastVideoTimeSent = 0;
|
|
1381
|
+
this.pausedTime = 0;
|
|
1382
|
+
this.pauseStartTime = null;
|
|
1383
|
+
this.boundBeforeUnload = this.handleBeforeUnload.bind(this);
|
|
1384
|
+
this.boundVisibilityChange = this.handleVisibilityChange.bind(this);
|
|
1385
|
+
}
|
|
1386
|
+
/**
|
|
1387
|
+
* Inicia el tracking del tiempo de permanencia.
|
|
1388
|
+
*/
|
|
1389
|
+
start(config) {
|
|
1390
|
+
if (typeof window === "undefined") return;
|
|
1391
|
+
const wasAlreadyTracking = this.enabled;
|
|
1392
|
+
this.enabled = config.enabled !== false;
|
|
1393
|
+
this.trackingDataId = config.trackingDataId;
|
|
1394
|
+
this.onSuccess = config.onSuccess;
|
|
1395
|
+
this.onError = config.onError;
|
|
1396
|
+
if (!this.enabled) return;
|
|
1397
|
+
this.cleanupListeners();
|
|
1398
|
+
window.addEventListener("beforeunload", this.boundBeforeUnload);
|
|
1399
|
+
document.addEventListener("visibilitychange", this.boundVisibilityChange);
|
|
1400
|
+
if (!wasAlreadyTracking) {
|
|
1401
|
+
this.startTime = Date.now();
|
|
1402
|
+
}
|
|
1403
|
+
if (!this.intervalId) {
|
|
1404
|
+
this.intervalId = setInterval(() => {
|
|
1405
|
+
if (this.videoCompleted && this.videoTrackingStopped) {
|
|
1406
|
+
if (document.visibilityState === "visible" && this.startTime) {
|
|
1407
|
+
this.sendDwellTime(false);
|
|
1408
|
+
}
|
|
1409
|
+
return;
|
|
1410
|
+
}
|
|
1411
|
+
if (this.videoCompleted && !this.videoTrackingStopped) {
|
|
1412
|
+
this.sendDwellTime(false);
|
|
1413
|
+
this.videoTrackingStopped = true;
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
1416
|
+
if (document.visibilityState === "visible" && this.startTime) {
|
|
1417
|
+
this.sendDwellTime(false);
|
|
1418
|
+
}
|
|
1419
|
+
}, 3e4);
|
|
1420
|
+
}
|
|
1421
|
+
this.retryPendingUpdates();
|
|
1422
|
+
}
|
|
1423
|
+
/**
|
|
1424
|
+
* Detiene el tracking y envía el tiempo acumulado.
|
|
1425
|
+
*/
|
|
1426
|
+
stop() {
|
|
1427
|
+
if (this.startTime) {
|
|
1428
|
+
this.sendDwellTime(true);
|
|
1429
|
+
}
|
|
1430
|
+
this.startTime = null;
|
|
1431
|
+
this.lastVideoTimeSent = 0;
|
|
1432
|
+
this.lastDwellTimeSent = 0;
|
|
1433
|
+
this.videoTrackingStopped = false;
|
|
1434
|
+
this.pausedTime = 0;
|
|
1435
|
+
this.pauseStartTime = null;
|
|
1436
|
+
this.cleanup();
|
|
1437
|
+
}
|
|
1438
|
+
/**
|
|
1439
|
+
* Obtiene el tiempo transcurrido en segundos (restando tiempo pausado).
|
|
1440
|
+
*/
|
|
1441
|
+
getElapsedTime() {
|
|
1442
|
+
if (!this.startTime) return 0;
|
|
1443
|
+
const totalTime = Math.floor((Date.now() - this.startTime) / 1e3);
|
|
1444
|
+
let currentPauseTime = this.pausedTime;
|
|
1445
|
+
if (this.pauseStartTime !== null) {
|
|
1446
|
+
currentPauseTime += Math.floor((Date.now() - this.pauseStartTime) / 1e3);
|
|
1447
|
+
}
|
|
1448
|
+
return Math.max(0, totalTime - currentPauseTime);
|
|
1449
|
+
}
|
|
1450
|
+
/**
|
|
1451
|
+
* Actualiza manualmente el tiempo del video.
|
|
1452
|
+
* Llamar desde el VideoTracker u otros adaptadores.
|
|
1453
|
+
*/
|
|
1454
|
+
updateVideoTime(videoTimeWatched, videoCompleted = false) {
|
|
1455
|
+
const hasNewTime = videoTimeWatched > this.videoTimeWatched;
|
|
1456
|
+
const hasNewCompletion = videoCompleted && !this.videoCompleted;
|
|
1457
|
+
if (hasNewTime || hasNewCompletion) {
|
|
1458
|
+
this.videoTimeWatched = Math.max(this.videoTimeWatched, videoTimeWatched);
|
|
1459
|
+
this.videoCompleted = videoCompleted || this.videoCompleted;
|
|
1460
|
+
if (this.trackingDataId && this.trackingDataId !== "pending" && this.startTime && hasNewCompletion) {
|
|
1461
|
+
this.sendDwellTime(false);
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
/**
|
|
1466
|
+
* Actualiza el trackingDataId (útil cuando se obtiene después del registro).
|
|
1467
|
+
*/
|
|
1468
|
+
updateTrackingId(trackingDataId) {
|
|
1469
|
+
if (!trackingDataId || typeof trackingDataId === "string" && trackingDataId.trim() === "") return;
|
|
1470
|
+
const oldTrackingId = this.trackingDataId;
|
|
1471
|
+
this.trackingDataId = trackingDataId;
|
|
1472
|
+
if (oldTrackingId === "pending" && this.trackingDataId !== "pending") {
|
|
1473
|
+
this.lastDwellTimeSent = 0;
|
|
1474
|
+
this.lastVideoTimeSent = 0;
|
|
1475
|
+
this.videoTrackingStopped = false;
|
|
1476
|
+
}
|
|
1477
|
+
if (this.startTime && oldTrackingId === "pending") {
|
|
1478
|
+
this.sendDwellTime(false);
|
|
1479
|
+
}
|
|
1480
|
+
if (!this.intervalId && this.enabled && this.startTime) {
|
|
1481
|
+
this.intervalId = setInterval(() => {
|
|
1482
|
+
if (document.visibilityState === "visible" && this.startTime) {
|
|
1483
|
+
this.sendDwellTime(false);
|
|
1484
|
+
}
|
|
1485
|
+
}, 3e4);
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
/**
|
|
1489
|
+
* Limpia todos los listeners y timers.
|
|
1490
|
+
*/
|
|
1491
|
+
destroy() {
|
|
1492
|
+
this.stop();
|
|
1493
|
+
}
|
|
1494
|
+
// ====================================
|
|
1495
|
+
// PRIVATE
|
|
1496
|
+
// ====================================
|
|
1497
|
+
checkLocalStorageForId() {
|
|
1498
|
+
if (typeof window === "undefined") return;
|
|
1499
|
+
if (!this.trackingDataId || this.trackingDataId === "pending") {
|
|
1500
|
+
const tid = localStorage.getItem("tracking_id") || localStorage.getItem("landing_tracking_id");
|
|
1501
|
+
if (tid) this.updateTrackingId(tid);
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
async sendDwellTime(isFinal = false) {
|
|
1505
|
+
if (!isTrackingAPIInitialized()) return;
|
|
1506
|
+
this.checkLocalStorageForId();
|
|
1507
|
+
if (!this.startTime || this.isSending) return;
|
|
1508
|
+
if (!this.trackingDataId || this.trackingDataId === "pending") return;
|
|
1509
|
+
const trackingIdNum = typeof this.trackingDataId === "string" ? parseInt(this.trackingDataId, 10) : this.trackingDataId;
|
|
1510
|
+
if (isNaN(trackingIdNum) || trackingIdNum <= 0) return;
|
|
1511
|
+
const elapsedTime = this.getElapsedTime();
|
|
1512
|
+
if (elapsedTime < 1 && !isFinal) return;
|
|
1513
|
+
this.isSending = true;
|
|
1514
|
+
const config = getAPIConfig();
|
|
1515
|
+
const token = config.getAuthToken?.() ?? null;
|
|
1516
|
+
try {
|
|
1517
|
+
const url = `${config.baseUrl}/tracking/${trackingIdNum}`;
|
|
1518
|
+
const headers = { "Content-Type": "application/json" };
|
|
1519
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
1520
|
+
const body = { dwellTime: elapsedTime };
|
|
1521
|
+
if (this.videoTimeWatched > 0 && !this.videoTrackingStopped) {
|
|
1522
|
+
body.videoTimeWatched = this.videoTimeWatched;
|
|
1523
|
+
body.videoCompleted = this.videoCompleted;
|
|
1524
|
+
}
|
|
1525
|
+
this.lastDwellTimeSent = elapsedTime;
|
|
1526
|
+
if (this.videoTimeWatched > 0 && !this.videoTrackingStopped) {
|
|
1527
|
+
this.lastVideoTimeSent = this.videoTimeWatched;
|
|
1528
|
+
}
|
|
1529
|
+
const controller = new AbortController();
|
|
1530
|
+
const timeoutId = setTimeout(() => controller.abort(), 1e4);
|
|
1531
|
+
const response = await fetch(url, {
|
|
1532
|
+
method: "PATCH",
|
|
1533
|
+
headers,
|
|
1534
|
+
body: JSON.stringify(body),
|
|
1535
|
+
keepalive: isFinal,
|
|
1536
|
+
signal: controller.signal
|
|
1537
|
+
});
|
|
1538
|
+
clearTimeout(timeoutId);
|
|
1539
|
+
if (!response.ok) {
|
|
1540
|
+
throw new Error(`Error ${response.status}: ${response.statusText}`);
|
|
1541
|
+
}
|
|
1542
|
+
this.onSuccess?.();
|
|
1543
|
+
this.clearPendingUpdate();
|
|
1544
|
+
} catch (error) {
|
|
1545
|
+
const err = error;
|
|
1546
|
+
const isNetworkError = err.name === "AbortError" || err.name === "TimeoutError" || err.name === "TypeError" && err.message?.includes("Failed to fetch");
|
|
1547
|
+
if (token) this.savePendingUpdate(elapsedTime);
|
|
1548
|
+
if (this.onError && !isNetworkError) {
|
|
1549
|
+
this.onError(error instanceof Error ? error : new Error("Error desconocido"));
|
|
1550
|
+
}
|
|
1551
|
+
} finally {
|
|
1552
|
+
this.isSending = false;
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
handleBeforeUnload(_event) {
|
|
1556
|
+
this.sendDwellTimeWithBeacon();
|
|
1557
|
+
}
|
|
1558
|
+
handleVisibilityChange() {
|
|
1559
|
+
if (document.visibilityState === "hidden") {
|
|
1560
|
+
if (this.pauseStartTime === null && this.startTime) {
|
|
1561
|
+
this.pauseStartTime = Date.now();
|
|
1562
|
+
this.sendDwellTime(false);
|
|
1563
|
+
}
|
|
1564
|
+
} else if (document.visibilityState === "visible") {
|
|
1565
|
+
if (this.pauseStartTime !== null) {
|
|
1566
|
+
this.pausedTime += Math.floor((Date.now() - this.pauseStartTime) / 1e3);
|
|
1567
|
+
this.pauseStartTime = null;
|
|
1568
|
+
} else if (!this.startTime) {
|
|
1569
|
+
this.startTime = Date.now();
|
|
1570
|
+
this.pausedTime = 0;
|
|
1571
|
+
this.pauseStartTime = null;
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
sendDwellTimeWithBeacon() {
|
|
1576
|
+
if (!isTrackingAPIInitialized()) return;
|
|
1577
|
+
this.checkLocalStorageForId();
|
|
1578
|
+
if (!this.startTime || !this.trackingDataId || this.trackingDataId === "pending") return;
|
|
1579
|
+
const trackingIdNum = typeof this.trackingDataId === "string" ? parseInt(this.trackingDataId, 10) : this.trackingDataId;
|
|
1580
|
+
if (isNaN(trackingIdNum) || trackingIdNum <= 0) return;
|
|
1581
|
+
const config = getAPIConfig();
|
|
1582
|
+
const elapsedTime = this.getElapsedTime();
|
|
1583
|
+
const token = config.getAuthToken?.() ?? null;
|
|
1584
|
+
const body = { dwellTime: elapsedTime };
|
|
1585
|
+
if (this.videoTimeWatched > 0 && !this.videoTrackingStopped) {
|
|
1586
|
+
body.videoTimeWatched = this.videoTimeWatched;
|
|
1587
|
+
body.videoCompleted = this.videoCompleted;
|
|
1588
|
+
}
|
|
1589
|
+
if (token) this.savePendingUpdate(elapsedTime);
|
|
1590
|
+
const url = `${config.baseUrl}/tracking/${trackingIdNum}`;
|
|
1591
|
+
const headers = { "Content-Type": "application/json" };
|
|
1592
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
1593
|
+
fetch(url, {
|
|
1594
|
+
method: "PATCH",
|
|
1595
|
+
headers,
|
|
1596
|
+
body: JSON.stringify(body),
|
|
1597
|
+
keepalive: true
|
|
1598
|
+
}).then((response) => {
|
|
1599
|
+
if (response.ok) this.clearPendingUpdate();
|
|
1600
|
+
}).catch(() => {
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1603
|
+
savePendingUpdate(time) {
|
|
1604
|
+
if (typeof window === "undefined" || !this.trackingDataId) return;
|
|
1605
|
+
try {
|
|
1606
|
+
sessionStorage.setItem("dwellTime_pending", JSON.stringify({ trackingDataId: this.trackingDataId, time, timestamp: Date.now() }));
|
|
1607
|
+
} catch {
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
async retryPendingUpdates() {
|
|
1611
|
+
if (typeof window === "undefined" || !isTrackingAPIInitialized()) return;
|
|
1612
|
+
try {
|
|
1613
|
+
const pendingData = sessionStorage.getItem("dwellTime_pending");
|
|
1614
|
+
if (!pendingData) return;
|
|
1615
|
+
const { trackingDataId, time } = JSON.parse(pendingData);
|
|
1616
|
+
if (trackingDataId !== this.trackingDataId) {
|
|
1617
|
+
this.clearPendingUpdate();
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
const config = getAPIConfig();
|
|
1621
|
+
const token = config.getAuthToken?.() ?? null;
|
|
1622
|
+
if (!token) {
|
|
1623
|
+
this.clearPendingUpdate();
|
|
1624
|
+
return;
|
|
1625
|
+
}
|
|
1626
|
+
const url = `${config.baseUrl}/tracking/${trackingDataId}`;
|
|
1627
|
+
const response = await fetch(url, {
|
|
1628
|
+
method: "PATCH",
|
|
1629
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
|
1630
|
+
body: JSON.stringify({ dwellTime: time })
|
|
1631
|
+
});
|
|
1632
|
+
if (response.ok) this.clearPendingUpdate();
|
|
1633
|
+
} catch {
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
clearPendingUpdate() {
|
|
1637
|
+
if (typeof window === "undefined") return;
|
|
1638
|
+
try {
|
|
1639
|
+
sessionStorage.removeItem("dwellTime_pending");
|
|
1640
|
+
} catch {
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
cleanupListeners() {
|
|
1644
|
+
if (typeof window === "undefined") return;
|
|
1645
|
+
window.removeEventListener("beforeunload", this.boundBeforeUnload);
|
|
1646
|
+
document.removeEventListener("visibilitychange", this.boundVisibilityChange);
|
|
1647
|
+
}
|
|
1648
|
+
cleanup() {
|
|
1649
|
+
if (typeof window === "undefined") return;
|
|
1650
|
+
this.cleanupListeners();
|
|
1651
|
+
if (this.intervalId) {
|
|
1652
|
+
clearInterval(this.intervalId);
|
|
1653
|
+
this.intervalId = null;
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
};
|
|
1657
|
+
var dwellTimeTracker = new DwellTimeTracker();
|
|
1658
|
+
var startDwellTimeTracking = (config) => {
|
|
1659
|
+
dwellTimeTracker.start(config);
|
|
1660
|
+
};
|
|
1661
|
+
var stopDwellTimeTracking = () => {
|
|
1662
|
+
dwellTimeTracker.stop();
|
|
1663
|
+
};
|
|
1664
|
+
|
|
1665
|
+
// src/persistence/tracking-queries.ts
|
|
1666
|
+
var getTrackingById = async (trackingId) => {
|
|
1667
|
+
return apiGet(`/tracking/${trackingId}`);
|
|
1668
|
+
};
|
|
1669
|
+
var getLeadWithTracking = async (leadId) => {
|
|
1670
|
+
const defaultData = {
|
|
1671
|
+
videoVisto: false,
|
|
1672
|
+
tiempoReproducido: "00:00",
|
|
1673
|
+
envioWhatsapp: false,
|
|
1674
|
+
videoTimeWatched: 0,
|
|
1675
|
+
dwellTime: 0
|
|
1676
|
+
};
|
|
1677
|
+
try {
|
|
1678
|
+
const data = await apiGet(`/leads/${leadId}/with-tracking`);
|
|
1679
|
+
const trackingDataArray = data.trackingData;
|
|
1680
|
+
const trackingInfo = trackingDataArray && trackingDataArray.length > 0 ? trackingDataArray[0] : null;
|
|
1681
|
+
const leadData = data;
|
|
1682
|
+
if (!trackingInfo) {
|
|
1683
|
+
return {
|
|
1684
|
+
...defaultData,
|
|
1685
|
+
accessUrl: leadData.accessUrl,
|
|
1686
|
+
nameForm: leadData.nameForm,
|
|
1687
|
+
msgForm: leadData.msgForm,
|
|
1688
|
+
platform: leadData.plataforma || leadData.platform,
|
|
1689
|
+
aliasName: leadData.aliasName || leadData.campaignName
|
|
1690
|
+
};
|
|
1691
|
+
}
|
|
1692
|
+
const formatTime = (seconds) => {
|
|
1693
|
+
const minutes = Math.floor(seconds / 60);
|
|
1694
|
+
const remainingSeconds = seconds % 60;
|
|
1695
|
+
return `${minutes.toString().padStart(2, "0")}:${remainingSeconds.toString().padStart(2, "0")}`;
|
|
1696
|
+
};
|
|
1697
|
+
const videoTimeWatched = trackingInfo.videoTimeWatched || 0;
|
|
1698
|
+
const dwellTime = trackingInfo.dwellTime || trackingInfo.dwell_time || 0;
|
|
1699
|
+
return {
|
|
1700
|
+
videoVisto: trackingInfo.videoCompleted || false,
|
|
1701
|
+
tiempoReproducido: videoTimeWatched ? formatTime(videoTimeWatched) : "00:00",
|
|
1702
|
+
envioWhatsapp: trackingInfo.whatsappSent || false,
|
|
1703
|
+
accessUrl: trackingInfo.accessUrl || leadData.accessUrl,
|
|
1704
|
+
nameForm: trackingInfo.nameForm || leadData.nameForm,
|
|
1705
|
+
msgForm: trackingInfo.msgForm || leadData.msgForm,
|
|
1706
|
+
platform: leadData.plataforma || leadData.platform || trackingInfo.platform,
|
|
1707
|
+
aliasName: leadData.aliasName || leadData.campaignName || trackingInfo.aliasName,
|
|
1708
|
+
trackingId: trackingInfo.id,
|
|
1709
|
+
videoTimeWatched,
|
|
1710
|
+
dwellTime
|
|
1711
|
+
};
|
|
1712
|
+
} catch {
|
|
1713
|
+
return defaultData;
|
|
1714
|
+
}
|
|
1715
|
+
};
|
|
1716
|
+
var generateTrackingLink = async (funnelId, customDomain) => {
|
|
1717
|
+
return apiPost("/tracking/generate-link", {
|
|
1718
|
+
funnel_id: funnelId,
|
|
1719
|
+
custom_domain: customDomain
|
|
1720
|
+
});
|
|
1721
|
+
};
|
|
1722
|
+
|
|
1723
|
+
exports.DwellTimeTracker = DwellTimeTracker;
|
|
1724
|
+
exports.LandingTracker = LandingTracker;
|
|
1725
|
+
exports.VideoTracker = VideoTracker;
|
|
1726
|
+
exports.buildMsgFormJSON = buildMsgFormJSON;
|
|
1727
|
+
exports.buildTrackingRequestBody = buildTrackingRequestBody;
|
|
1728
|
+
exports.captureUTMParams = captureUTMParams;
|
|
1729
|
+
exports.captureUserIdFromURL = captureUserIdFromURL;
|
|
1730
|
+
exports.createLandingTracker = createLandingTracker;
|
|
1731
|
+
exports.detectLeadOrigin = detectLeadOrigin;
|
|
1732
|
+
exports.dwellTimeTracker = dwellTimeTracker;
|
|
1733
|
+
exports.extractUrlParams = extractUrlParams;
|
|
1734
|
+
exports.generateTrackingLink = generateTrackingLink;
|
|
1735
|
+
exports.getGATrackingId = getGATrackingId;
|
|
1736
|
+
exports.getLeadWithTracking = getLeadWithTracking;
|
|
1737
|
+
exports.getTrackingById = getTrackingById;
|
|
1738
|
+
exports.getUserId = getUserId;
|
|
1739
|
+
exports.getUserName = getUserName;
|
|
1740
|
+
exports.initGA = initGA;
|
|
1741
|
+
exports.initTrackingAPI = initTrackingAPI;
|
|
1742
|
+
exports.injectGTMScript = injectGTMScript;
|
|
1743
|
+
exports.isGAInitialized = isGAInitialized;
|
|
1744
|
+
exports.isTrackingAPIInitialized = isTrackingAPIInitialized;
|
|
1745
|
+
exports.markWhatsAppSent = markWhatsAppSent;
|
|
1746
|
+
exports.pushToDataLayer = pushToDataLayer;
|
|
1747
|
+
exports.reinitGA = reinitGA;
|
|
1748
|
+
exports.resetAnalytics = resetAnalytics;
|
|
1749
|
+
exports.setUserId = setUserId;
|
|
1750
|
+
exports.setUserName = setUserName;
|
|
1751
|
+
exports.startDwellTimeTracking = startDwellTimeTracking;
|
|
1752
|
+
exports.stopDwellTimeTracking = stopDwellTimeTracking;
|
|
1753
|
+
exports.submitLead = submitLead;
|
|
1754
|
+
exports.submitTrackingLead = submitTrackingLead;
|
|
1755
|
+
exports.trackCTAClick = trackCTAClick;
|
|
1756
|
+
exports.trackContactClick = trackContactClick;
|
|
1757
|
+
exports.trackConversion = trackConversion;
|
|
1758
|
+
exports.trackDownload = trackDownload;
|
|
1759
|
+
exports.trackEvent = trackEvent;
|
|
1760
|
+
exports.trackFAQExpand = trackFAQExpand;
|
|
1761
|
+
exports.trackFormFieldComplete = trackFormFieldComplete;
|
|
1762
|
+
exports.trackFormStart = trackFormStart;
|
|
1763
|
+
exports.trackFormSubmit = trackFormSubmit;
|
|
1764
|
+
exports.trackFormValidationError = trackFormValidationError;
|
|
1765
|
+
exports.trackHTML5Video = trackHTML5Video;
|
|
1766
|
+
exports.trackImageClick = trackImageClick;
|
|
1767
|
+
exports.trackPageView = trackPageView;
|
|
1768
|
+
exports.trackPricingCardClick = trackPricingCardClick;
|
|
1769
|
+
exports.trackScrollTo = trackScrollTo;
|
|
1770
|
+
exports.trackSectionClick = trackSectionClick;
|
|
1771
|
+
exports.trackShare = trackShare;
|
|
1772
|
+
exports.trackSocialClick = trackSocialClick;
|
|
1773
|
+
exports.trackTimeInSection = trackTimeInSection;
|
|
1774
|
+
exports.trackVoomlyByEmbedId = trackVoomlyByEmbedId;
|
|
1775
|
+
exports.trackWistiaByMediaId = trackWistiaByMediaId;
|
|
1776
|
+
exports.updateTrackingLead = updateTrackingLead;
|
|
1777
|
+
exports.videoTracker = videoTracker;
|
|
1778
|
+
//# sourceMappingURL=index.cjs.map
|
|
1779
|
+
//# sourceMappingURL=index.cjs.map
|