costway-tracking-v2 2.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/.cursor/README.md +150 -0
- package/.cursor/cursors-intellisense-rules.json +62 -0
- package/.cursor/rules.json +50 -0
- package/.cursor/settings.json +32 -0
- package/.cursor/snippets.json +186 -0
- package/README.md +223 -0
- package/index.cjs +494 -0
- package/index.html +42 -0
- package/package.json +20 -0
- package/tracking-dev.js +463 -0
- package/tracking.js +464 -0
- package/trackvue3.js +526 -0
package/tracking-dev.js
ADDED
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
//最原始的版本
|
|
2
|
+
(function () {
|
|
3
|
+
function getQueryParamFromScriptSrc(paramName, scriptName) {
|
|
4
|
+
var scripts = document.getElementsByTagName('script');
|
|
5
|
+
for (var i = 0; i < scripts.length; i++) {
|
|
6
|
+
if (scripts[i].src.indexOf(scriptName) !== -1) {
|
|
7
|
+
var params = scripts[i].src.split('?')[1];
|
|
8
|
+
var paramPairs = params.split('&');
|
|
9
|
+
for (var j = 0; j < paramPairs.length; j++) {
|
|
10
|
+
var pair = paramPairs[j].split('=');
|
|
11
|
+
if (pair[0] === paramName) {
|
|
12
|
+
return pair[1];
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
break;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// 使用该函数获取 'lang' 参数的值
|
|
22
|
+
const lang = getQueryParamFromScriptSrc('lang', 'tracking-dev.js');
|
|
23
|
+
let config = {
|
|
24
|
+
batchSize: 5,
|
|
25
|
+
debounceTime: 200,
|
|
26
|
+
targetContainerSelector: 'body',
|
|
27
|
+
targetClass: '.costway-track-class',
|
|
28
|
+
trackClickSelector: '[data-track]',
|
|
29
|
+
apiUrl: 'https://h5.kostway.com/goapi/v1/stat/track',
|
|
30
|
+
country: lang,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
function setConfig(globalConfig) {
|
|
35
|
+
config = { ...config, ...globalConfig };
|
|
36
|
+
}
|
|
37
|
+
let eventData = [];
|
|
38
|
+
let pageEnterTime;
|
|
39
|
+
let hasTrackedPageLeave = "";
|
|
40
|
+
let heartbeat;
|
|
41
|
+
let lastInterval;
|
|
42
|
+
|
|
43
|
+
config.sessionTimeout = 30 * 60 * 1000; // 30分钟
|
|
44
|
+
function getSession() {
|
|
45
|
+
let now = new Date().getTime();
|
|
46
|
+
let currentReferrer = document.referrer;
|
|
47
|
+
let currentUTM_S = getUrlParameter('utm_source');
|
|
48
|
+
let currentUTM_m = getUrlParameter('utm_medium');
|
|
49
|
+
let currentUTM = currentUTM_S + currentUTM_m;
|
|
50
|
+
const currentDomain = normalizeDomain(window.location.hostname);
|
|
51
|
+
const referrerTag = getReferrerTag(currentReferrer, currentDomain);
|
|
52
|
+
let previousSession = JSON.parse(localStorage.getItem("cgs_session"));
|
|
53
|
+
let sessionMap = JSON.parse(localStorage.getItem("cgs_sessionMap")) || {};
|
|
54
|
+
let isSessionTimedOut = previousSession && (now - previousSession.startTime > config.sessionTimeout);
|
|
55
|
+
if (!previousSession || (isSessionTimedOut)
|
|
56
|
+
|| (referrerTag !== "INTERNAL" && (referrerTag !== previousSession.referrerTag || currentUTM !== previousSession.initialUtmForSession))) {
|
|
57
|
+
let newSessionId = sessionMap[`${referrerTag}_${currentUTM}`];
|
|
58
|
+
if (!newSessionId) {
|
|
59
|
+
newSessionId = generateUUID();
|
|
60
|
+
sessionMap[`${referrerTag}_${currentUTM}`] = newSessionId;
|
|
61
|
+
localStorage.setItem("cgs_sessionMap", JSON.stringify(sessionMap));
|
|
62
|
+
}
|
|
63
|
+
if (isSessionTimedOut) {
|
|
64
|
+
newSessionId = generateUUID();
|
|
65
|
+
sessionMap[`${referrerTag}_${currentUTM}`] = newSessionId;
|
|
66
|
+
localStorage.setItem("cgs_sessionMap", JSON.stringify(sessionMap));
|
|
67
|
+
}
|
|
68
|
+
let newSession = {
|
|
69
|
+
id: newSessionId,
|
|
70
|
+
startTime: now,
|
|
71
|
+
lastActivityTime: now,
|
|
72
|
+
referrerTag: referrerTag,
|
|
73
|
+
referrerForSession: currentReferrer,
|
|
74
|
+
initialUtmForSession: currentUTM,
|
|
75
|
+
};
|
|
76
|
+
localStorage.setItem("cgs_session", JSON.stringify(newSession));
|
|
77
|
+
localStorage.setItem("cgs_sessionID", newSession.id);
|
|
78
|
+
setCookie("cgs_sessionID", newSession.id, 1); // Save for 1 day
|
|
79
|
+
return newSession;
|
|
80
|
+
} else {
|
|
81
|
+
previousSession.lastActivityTime = now;
|
|
82
|
+
localStorage.setItem("cgs_session", JSON.stringify(previousSession));
|
|
83
|
+
return previousSession;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function getReferrerTag(referrer) {
|
|
87
|
+
const currentDomain = normalizeDomain(window.location.hostname);
|
|
88
|
+
const referrerDomain = extractDomain(referrer)
|
|
89
|
+
if (referrerDomain === currentDomain || !referrer) {
|
|
90
|
+
return 'INTERNAL';
|
|
91
|
+
} else {
|
|
92
|
+
return referrerDomain;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function getUrlParameter(name) {
|
|
96
|
+
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
|
|
97
|
+
var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
|
|
98
|
+
var results = regex.exec(window.location.search);
|
|
99
|
+
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
|
|
100
|
+
};
|
|
101
|
+
function setCookie(name, value, days) {
|
|
102
|
+
let expires = "";
|
|
103
|
+
if (days) {
|
|
104
|
+
let date = new Date();
|
|
105
|
+
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
|
106
|
+
expires = "; expires=" + date.toUTCString();
|
|
107
|
+
}
|
|
108
|
+
document.cookie = name + "=" + (value || "") + expires + ";Domain=.costway.com; path=/";
|
|
109
|
+
}
|
|
110
|
+
function getCookie(name) {
|
|
111
|
+
let nameEQ = name + "=";
|
|
112
|
+
let ca = document.cookie.split(';');
|
|
113
|
+
for (let i = 0; i < ca.length; i++) {
|
|
114
|
+
let c = ca[i];
|
|
115
|
+
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
|
|
116
|
+
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
function setuserId(userId) {
|
|
121
|
+
localStorage.setItem('costway_t_userId', userId);
|
|
122
|
+
setCookie('costway_t_userId', userId, 365); // 保存365天
|
|
123
|
+
}
|
|
124
|
+
function normalizeDomain(domain) {
|
|
125
|
+
if (domain.startsWith("www.")) {
|
|
126
|
+
return domain.slice(4);
|
|
127
|
+
}
|
|
128
|
+
return domain;
|
|
129
|
+
}
|
|
130
|
+
function extractDomain(url) {
|
|
131
|
+
if (!url) {
|
|
132
|
+
return '';
|
|
133
|
+
}
|
|
134
|
+
// 从URL中提取域名
|
|
135
|
+
const domainRegex = /^(?:https?:\/\/)?(?:[^@\n]+@)?(?:www\.)?([^:\/\n]+)/im;
|
|
136
|
+
const match = url.match(domainRegex);
|
|
137
|
+
if (match && match.length > 1) {
|
|
138
|
+
return match[1];
|
|
139
|
+
} else {
|
|
140
|
+
return '';
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
function clearTimers() {
|
|
146
|
+
clearTimeout(heartbeat);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function getTime() {
|
|
150
|
+
return (new Date()).getTime();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function intervalHeartbeat() {
|
|
154
|
+
var now = getTime();
|
|
155
|
+
var diff = now - lastInterval - 200;
|
|
156
|
+
lastInterval = now;
|
|
157
|
+
if (diff > 1000) { // don't trigger on small stutters less than 1000ms
|
|
158
|
+
clearTimers();
|
|
159
|
+
trackPageLeave(); // call trackPageLeave when heartbeat stops
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
function debounce(func, wait) {
|
|
165
|
+
let timeout;
|
|
166
|
+
return function () {
|
|
167
|
+
const context = this;
|
|
168
|
+
const args = arguments;
|
|
169
|
+
clearTimeout(timeout);
|
|
170
|
+
timeout = setTimeout(function () {
|
|
171
|
+
func.apply(context, args);
|
|
172
|
+
}, wait);
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function getuserId() {
|
|
177
|
+
var userId = localStorage.getItem('costway_t_userId');
|
|
178
|
+
|
|
179
|
+
if (!userId) {
|
|
180
|
+
userId = getCookie('costway_t_userId');
|
|
181
|
+
if (!userId) {
|
|
182
|
+
userId = generateUUID();
|
|
183
|
+
localStorage.setItem('costway_t_userId', userId);
|
|
184
|
+
setCookie('costway_t_userId', userId, 365); // 将用户ID保存到cookie中,有效期为1年
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return userId;
|
|
188
|
+
}
|
|
189
|
+
function generateUUID() {
|
|
190
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
|
191
|
+
var r = Math.random() * 16 | 0,
|
|
192
|
+
v = c === 'x' ? r : (r & 0x3 | 0x8);
|
|
193
|
+
return v.toString(16);
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
function sendDataImmediately(data) {
|
|
197
|
+
if (data.length === 0) return;
|
|
198
|
+
navigator.sendBeacon(config.apiUrl, JSON.stringify(data));
|
|
199
|
+
}
|
|
200
|
+
function sendEventData() {
|
|
201
|
+
if (eventData.length === 0) return;
|
|
202
|
+
navigator.sendBeacon(config.apiUrl, JSON.stringify(eventData));
|
|
203
|
+
eventData = [];
|
|
204
|
+
}
|
|
205
|
+
function getGlobalParams() {
|
|
206
|
+
if (window.customGlobalParams) {
|
|
207
|
+
return window.customGlobalParams;
|
|
208
|
+
} else {
|
|
209
|
+
return {};
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function trackEvent(eventType, data) {
|
|
213
|
+
let session = getSession();
|
|
214
|
+
const event = {
|
|
215
|
+
userId: getuserId(),
|
|
216
|
+
timestamp: new Date().getTime(),
|
|
217
|
+
eventType: eventType,
|
|
218
|
+
data: data,
|
|
219
|
+
params: getGlobalParams(),
|
|
220
|
+
country: config.country,
|
|
221
|
+
sessionId: session.id,
|
|
222
|
+
};
|
|
223
|
+
if (eventType === "pageView" || eventType === "pageLeave" || eventType === "error" || eventType === "customEvent" || eventType === "pageLoadTime") {
|
|
224
|
+
sendDataImmediately([event]);
|
|
225
|
+
} else {
|
|
226
|
+
eventData.push(event);
|
|
227
|
+
if (eventData.length >= config.batchSize) {
|
|
228
|
+
sendEventData();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
}
|
|
233
|
+
function trackPageView() {
|
|
234
|
+
let session = getSession();
|
|
235
|
+
const pageViewData = {
|
|
236
|
+
url: window.location.href,
|
|
237
|
+
referrer: document.referrer,
|
|
238
|
+
sessionId: session.id,
|
|
239
|
+
};
|
|
240
|
+
trackEvent("pageView", pageViewData);
|
|
241
|
+
pageEnterTime = new Date().getTime();
|
|
242
|
+
}
|
|
243
|
+
function trackPageLeave() {
|
|
244
|
+
let session = getSession();
|
|
245
|
+
const pageLeaveTime = new Date().getTime();
|
|
246
|
+
const duration = pageLeaveTime - pageEnterTime;
|
|
247
|
+
const totalStayTime = window.performance.timing.loadEventEnd - window.performance.timing.navigationStart;
|
|
248
|
+
const scrollDistance = window.scrollY;
|
|
249
|
+
const pageLeaveData = {
|
|
250
|
+
url: window.location.href,
|
|
251
|
+
duration: duration,
|
|
252
|
+
totalStayTime: totalStayTime,
|
|
253
|
+
scrollDistance: scrollDistance,
|
|
254
|
+
sessionId: session.id,
|
|
255
|
+
};
|
|
256
|
+
trackEvent("pageLeave", pageLeaveData);
|
|
257
|
+
}
|
|
258
|
+
function trackClick(event) {
|
|
259
|
+
let target = event.target;
|
|
260
|
+
if (target !== document.body && target !== document.documentElement) {
|
|
261
|
+
target = target.parentNode;
|
|
262
|
+
}
|
|
263
|
+
//if (target.tagName == "HTML" || target.tagName == "BODY") return
|
|
264
|
+
console.log(target)
|
|
265
|
+
if (target.matches(config.trackClickSelector)) {
|
|
266
|
+
const clickData = {
|
|
267
|
+
element: target.tagName,
|
|
268
|
+
id: target.id,
|
|
269
|
+
classList: Array.from(target.classList),
|
|
270
|
+
dataset: target.dataset,
|
|
271
|
+
};
|
|
272
|
+
trackEvent("click", clickData);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
function trackNewElement(element) {
|
|
276
|
+
// if (element.matches(config.targetClass)) {
|
|
277
|
+
const newElementData = {
|
|
278
|
+
element: element.tagName,
|
|
279
|
+
id: element.id,
|
|
280
|
+
classList: Array.from(element.classList),
|
|
281
|
+
};
|
|
282
|
+
trackEvent("newElement", newElementData);
|
|
283
|
+
// }
|
|
284
|
+
}
|
|
285
|
+
config.hideShowSelector = '*'; // All elements
|
|
286
|
+
function isVisible(element) {
|
|
287
|
+
const style = window.getComputedStyle(element);
|
|
288
|
+
const isHidden = style.display === 'none' || style.visibility === 'hidden';
|
|
289
|
+
return !isHidden;
|
|
290
|
+
}
|
|
291
|
+
function observeClassChanges() {
|
|
292
|
+
const observerCallback = debounce(function (mutations) {
|
|
293
|
+
mutations.forEach(function (mutation) {
|
|
294
|
+
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
|
|
295
|
+
const target = mutation.target;
|
|
296
|
+
if (mutation.oldValue && mutation.oldValue.includes("showActive") && !target.classList.contains("showActive")) {
|
|
297
|
+
const removedElementData = {
|
|
298
|
+
element: target.tagName,
|
|
299
|
+
id: target.id,
|
|
300
|
+
classList: Array.from(target.classList),
|
|
301
|
+
};
|
|
302
|
+
trackEvent("closeElement", removedElementData);
|
|
303
|
+
} else if (!mutation.oldValue || (!mutation.oldValue.includes("showActive") && target.classList.contains("showActive"))) {
|
|
304
|
+
const addedElementData = {
|
|
305
|
+
element: target.tagName,
|
|
306
|
+
id: target.id,
|
|
307
|
+
classList: Array.from(target.classList),
|
|
308
|
+
};
|
|
309
|
+
trackEvent("showElement", addedElementData);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
}, config.debounceTime);
|
|
314
|
+
|
|
315
|
+
const observer = new MutationObserver(observerCallback);
|
|
316
|
+
const observerConfig = {
|
|
317
|
+
attributes: true,
|
|
318
|
+
attributeOldValue: true,
|
|
319
|
+
subtree: true,
|
|
320
|
+
};
|
|
321
|
+
const targetContainer = document.querySelector(config.targetContainerSelector);
|
|
322
|
+
observer.observe(targetContainer, observerConfig);
|
|
323
|
+
}
|
|
324
|
+
function observeDomChanges() {
|
|
325
|
+
// 使用“debounce”函数可以防止短时间内频繁触发事件
|
|
326
|
+
const observerCallback = debounce(function (mutations) {
|
|
327
|
+
mutations.forEach(function (mutation) {
|
|
328
|
+
mutation.addedNodes.forEach(function (node) {
|
|
329
|
+
// 判断新增的节点是否匹配目标类
|
|
330
|
+
if (
|
|
331
|
+
node.nodeType === Node.ELEMENT_NODE &&
|
|
332
|
+
node.matches(config.targetClass)
|
|
333
|
+
) {
|
|
334
|
+
trackNewElement(node);
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
}, config.debounceTime);
|
|
339
|
+
|
|
340
|
+
const observer = new MutationObserver(observerCallback);
|
|
341
|
+
const observerConfig = {
|
|
342
|
+
childList: true,
|
|
343
|
+
subtree: true,
|
|
344
|
+
};
|
|
345
|
+
// 使用querySelector获取目标元素,而不是直接使用document.body
|
|
346
|
+
const targetContainer = document.querySelector(config.targetContainerSelector);
|
|
347
|
+
observer.observe(targetContainer, observerConfig);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function registerErrorListener() {
|
|
351
|
+
window.addEventListener('error', function (event) {
|
|
352
|
+
trackError(event.message, event.filename, event.lineno, event.colno, event.error);
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
function trackError(message, source, lineno, colno, error) {
|
|
358
|
+
const errorData = {
|
|
359
|
+
message: message,
|
|
360
|
+
source: source,
|
|
361
|
+
lineno: lineno,
|
|
362
|
+
colno: colno,
|
|
363
|
+
error: error && error.stack,
|
|
364
|
+
};
|
|
365
|
+
trackEvent("error", errorData);
|
|
366
|
+
}
|
|
367
|
+
function trackPageLoadTime() {
|
|
368
|
+
if (window.performance) {
|
|
369
|
+
var performanceEntries = window.performance.getEntriesByType('navigation');
|
|
370
|
+
if (performanceEntries.length > 0) {
|
|
371
|
+
var navigationTiming = performanceEntries[0];
|
|
372
|
+
var pageLoadTime = navigationTiming.loadEventEnd - navigationTiming.startTime;
|
|
373
|
+
// ...
|
|
374
|
+
const data = {
|
|
375
|
+
pageLoadTime: pageLoadTime,
|
|
376
|
+
url: window.location.href,
|
|
377
|
+
};
|
|
378
|
+
trackEvent("pageLoadTime", data);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function trackResourceLoadTimes() {
|
|
384
|
+
if (window.performance) {
|
|
385
|
+
var resources = window.performance.getEntriesByType("resource");
|
|
386
|
+
resources.forEach(function (resource) {
|
|
387
|
+
var resourceName = resource.name;
|
|
388
|
+
var resourceLoadTime = resource.duration;
|
|
389
|
+
trackEvent("resourceLoadTime", { resourceName, resourceLoadTime });
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
function trackCustomEvent(eventName, eventData) {
|
|
396
|
+
const data = {
|
|
397
|
+
userId: getuserId(),
|
|
398
|
+
timestamp: new Date().getTime(),
|
|
399
|
+
customData: eventData,
|
|
400
|
+
eventName: eventName,
|
|
401
|
+
};
|
|
402
|
+
trackEvent("customEvent", data);
|
|
403
|
+
}
|
|
404
|
+
let lastVisibilityState = document.visibilityState;
|
|
405
|
+
let pageHiddenTime = null;
|
|
406
|
+
let pageVisibleTime = null;
|
|
407
|
+
function init() {
|
|
408
|
+
let userId = getuserId();
|
|
409
|
+
if (!userId) {
|
|
410
|
+
userId = generateUUID();
|
|
411
|
+
setuserId(userId);
|
|
412
|
+
}
|
|
413
|
+
trackPageView();
|
|
414
|
+
// trackPageLoadTime();
|
|
415
|
+
// trackResourceLoadTimes();
|
|
416
|
+
document.addEventListener('click', trackClick);
|
|
417
|
+
|
|
418
|
+
document.addEventListener('visibilitychange', function () {
|
|
419
|
+
if (lastVisibilityState) {
|
|
420
|
+
if (document.visibilityState === 'hidden' || document.hidden) {
|
|
421
|
+
console.log('hidden');
|
|
422
|
+
// 页面从可见变为不可见,记录时间
|
|
423
|
+
pageHiddenTime = new Date().getTime();
|
|
424
|
+
const visibleDuration = new Date().getTime() - pageVisibleTime;
|
|
425
|
+
if(hasTrackedPageLeave=="" || hasTrackedPageLeave=="hidden"){
|
|
426
|
+
hasTrackedPageLeave = "hidden";
|
|
427
|
+
trackPageLeave('hidden');
|
|
428
|
+
}
|
|
429
|
+
} else if (document.visibilityState === 'visible' && pageHiddenTime) {
|
|
430
|
+
// 页面从不可见变为可见,计算隐藏的时长
|
|
431
|
+
const hiddenDuration = new Date().getTime() - pageHiddenTime;
|
|
432
|
+
pageHiddenTime = null;
|
|
433
|
+
pageVisibleTime = new Date().getTime();
|
|
434
|
+
// 如果页面隐藏的时长超过一定阈值(例如30秒),则认为用户已离开页面
|
|
435
|
+
if (hiddenDuration > 30000) {
|
|
436
|
+
trackPageLeave("visible");
|
|
437
|
+
}
|
|
438
|
+
// 触发 pageView 事件
|
|
439
|
+
trackPageView();
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
lastVisibilityState = document.visibilityState;
|
|
443
|
+
});
|
|
444
|
+
window.addEventListener('pagehide', function () {
|
|
445
|
+
console.log('pagehide');
|
|
446
|
+
if (hasTrackedPageLeave == "pagehide") {
|
|
447
|
+
hasTrackedPageLeave = "pagehide";
|
|
448
|
+
trackPageLeave('pagehide');
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
});
|
|
452
|
+
window.addEventListener('load', function () { setTimeout(trackPageLoadTime, 0);});
|
|
453
|
+
window.addEventListener('DOMContentLoaded', function () {
|
|
454
|
+
observeDomChanges();
|
|
455
|
+
observeClassChanges();
|
|
456
|
+
});
|
|
457
|
+
registerErrorListener();
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
init();
|
|
461
|
+
window.trackCustomEvent = trackCustomEvent;
|
|
462
|
+
window.setConfig = setConfig;
|
|
463
|
+
})();
|