mixpanel-browser 2.70.0 → 2.71.1
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/.claude/settings.local.json +9 -0
- package/CHANGELOG.md +11 -0
- package/build.sh +1 -0
- package/dist/mixpanel-core.cjs.d.ts +440 -0
- package/dist/mixpanel-core.cjs.js +849 -50
- package/dist/mixpanel-recorder.js +5 -3
- package/dist/mixpanel-recorder.min.js +1 -1
- package/dist/mixpanel-recorder.min.js.map +1 -1
- package/dist/mixpanel-with-async-recorder.cjs.d.ts +440 -0
- package/dist/mixpanel-with-async-recorder.cjs.js +849 -50
- package/dist/mixpanel-with-recorder.d.ts +440 -0
- package/dist/mixpanel-with-recorder.js +853 -52
- package/dist/mixpanel-with-recorder.min.d.ts +440 -0
- package/dist/mixpanel-with-recorder.min.js +1 -1
- package/dist/mixpanel.amd.d.ts +440 -0
- package/dist/mixpanel.amd.js +853 -52
- package/dist/mixpanel.cjs.d.ts +440 -0
- package/dist/mixpanel.cjs.js +853 -52
- package/dist/mixpanel.globals.js +849 -50
- package/dist/mixpanel.min.js +170 -153
- package/dist/mixpanel.module.d.ts +440 -0
- package/dist/mixpanel.module.js +853 -52
- package/dist/mixpanel.umd.d.ts +440 -0
- package/dist/mixpanel.umd.js +853 -52
- package/dist/rrweb-compiled.js +4 -2
- package/package.json +2 -19
- package/rollup.config.mjs +28 -4
- package/src/autocapture/deadclick.js +254 -0
- package/src/autocapture/index.js +239 -41
- package/src/autocapture/shadow-dom-observer.js +100 -0
- package/src/autocapture/utils.js +230 -3
- package/src/config.js +1 -1
- package/src/flags/index.js +32 -12
- package/src/index.d.ts +16 -3
- package/src/loaders/loader-module-core.d.ts +1 -0
- package/src/loaders/loader-module-with-async-recorder.d.ts +1 -0
- package/src/loaders/loader-module.d.ts +1 -0
- package/src/utils.js +15 -0
package/src/autocapture/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { _, document, safewrap, safewrapClass } from '../utils';
|
|
2
2
|
import { window } from '../window';
|
|
3
3
|
import {
|
|
4
|
-
getPropsForDOMEvent, logger, minDOMApisSupported,
|
|
5
|
-
EV_CHANGE, EV_CLICK, EV_HASHCHANGE, EV_MP_LOCATION_CHANGE,
|
|
6
|
-
EV_SCROLLEND, EV_SUBMIT
|
|
4
|
+
getPolyfillScrollEndFunction, getPropsForDOMEvent, logger, minDOMApisSupported,
|
|
5
|
+
EV_CHANGE, EV_CLICK, EV_HASHCHANGE, EV_MP_LOCATION_CHANGE, EV_LOAD,
|
|
6
|
+
EV_POPSTATE, EV_SCROLL, EV_SCROLLEND, EV_SUBMIT, EV_VISIBILITYCHANGE
|
|
7
7
|
} from './utils';
|
|
8
8
|
import { RageClickTracker } from './rageclick';
|
|
9
|
+
import { DeadClickTracker, DEFAULT_DEAD_CLICK_TIMEOUT_MS } from './deadclick';
|
|
9
10
|
|
|
10
11
|
var AUTOCAPTURE_CONFIG_KEY = 'autocapture';
|
|
11
12
|
var LEGACY_PAGEVIEW_CONFIG_KEY = 'track_pageview';
|
|
@@ -26,10 +27,12 @@ var CONFIG_CAPTURE_TEXT_CONTENT = 'capture_text_content';
|
|
|
26
27
|
var CONFIG_SCROLL_CAPTURE_ALL = 'scroll_capture_all';
|
|
27
28
|
var CONFIG_SCROLL_CHECKPOINTS = 'scroll_depth_percent_checkpoints';
|
|
28
29
|
var CONFIG_TRACK_CLICK = 'click';
|
|
30
|
+
var CONFIG_TRACK_DEAD_CLICK = 'dead_click';
|
|
29
31
|
var CONFIG_TRACK_INPUT = 'input';
|
|
30
32
|
var CONFIG_TRACK_PAGEVIEW = 'pageview';
|
|
31
33
|
var CONFIG_TRACK_RAGE_CLICK = 'rage_click';
|
|
32
34
|
var CONFIG_TRACK_SCROLL = 'scroll';
|
|
35
|
+
var CONFIG_TRACK_PAGE_LEAVE = 'page_leave';
|
|
33
36
|
var CONFIG_TRACK_SUBMIT = 'submit';
|
|
34
37
|
|
|
35
38
|
var CONFIG_DEFAULTS = {};
|
|
@@ -44,10 +47,12 @@ CONFIG_DEFAULTS[CONFIG_CAPTURE_TEXT_CONTENT] = false;
|
|
|
44
47
|
CONFIG_DEFAULTS[CONFIG_SCROLL_CAPTURE_ALL] = false;
|
|
45
48
|
CONFIG_DEFAULTS[CONFIG_SCROLL_CHECKPOINTS] = [25, 50, 75, 100];
|
|
46
49
|
CONFIG_DEFAULTS[CONFIG_TRACK_CLICK] = true;
|
|
50
|
+
CONFIG_DEFAULTS[CONFIG_TRACK_DEAD_CLICK] = true;
|
|
47
51
|
CONFIG_DEFAULTS[CONFIG_TRACK_INPUT] = true;
|
|
48
52
|
CONFIG_DEFAULTS[CONFIG_TRACK_PAGEVIEW] = PAGEVIEW_OPTION_FULL_URL;
|
|
49
53
|
CONFIG_DEFAULTS[CONFIG_TRACK_RAGE_CLICK] = true;
|
|
50
54
|
CONFIG_DEFAULTS[CONFIG_TRACK_SCROLL] = true;
|
|
55
|
+
CONFIG_DEFAULTS[CONFIG_TRACK_PAGE_LEAVE] = false;
|
|
51
56
|
CONFIG_DEFAULTS[CONFIG_TRACK_SUBMIT] = true;
|
|
52
57
|
|
|
53
58
|
var DEFAULT_PROPS = {
|
|
@@ -55,10 +60,12 @@ var DEFAULT_PROPS = {
|
|
|
55
60
|
};
|
|
56
61
|
|
|
57
62
|
var MP_EV_CLICK = '$mp_click';
|
|
63
|
+
var MP_EV_DEAD_CLICK = '$mp_dead_click';
|
|
58
64
|
var MP_EV_INPUT = '$mp_input_change';
|
|
59
65
|
var MP_EV_RAGE_CLICK = '$mp_rage_click';
|
|
60
66
|
var MP_EV_SCROLL = '$mp_scroll';
|
|
61
67
|
var MP_EV_SUBMIT = '$mp_submit';
|
|
68
|
+
var MP_EV_PAGE_LEAVE = '$mp_page_leave';
|
|
62
69
|
|
|
63
70
|
/**
|
|
64
71
|
* Autocapture: manages automatic event tracking
|
|
@@ -66,6 +73,9 @@ var MP_EV_SUBMIT = '$mp_submit';
|
|
|
66
73
|
*/
|
|
67
74
|
var Autocapture = function(mp) {
|
|
68
75
|
this.mp = mp;
|
|
76
|
+
this.maxScrollViewDepth = 0;
|
|
77
|
+
this.hasTrackedScrollSession = false;
|
|
78
|
+
this.previousScrollHeight = 0;
|
|
69
79
|
};
|
|
70
80
|
|
|
71
81
|
Autocapture.prototype.init = function() {
|
|
@@ -73,13 +83,15 @@ Autocapture.prototype.init = function() {
|
|
|
73
83
|
logger.critical('Autocapture unavailable: missing required DOM APIs');
|
|
74
84
|
return;
|
|
75
85
|
}
|
|
76
|
-
|
|
86
|
+
this.initPageListeners();
|
|
77
87
|
this.initPageviewTracking();
|
|
78
88
|
this.initClickTracking();
|
|
89
|
+
this.initDeadClickTracking();
|
|
79
90
|
this.initInputTracking();
|
|
80
91
|
this.initScrollTracking();
|
|
81
92
|
this.initSubmitTracking();
|
|
82
93
|
this.initRageClickTracking();
|
|
94
|
+
this.initPageLeaveTracking();
|
|
83
95
|
};
|
|
84
96
|
|
|
85
97
|
Autocapture.prototype.getFullConfig = function() {
|
|
@@ -160,7 +172,8 @@ Autocapture.prototype.trackDomEvent = function(ev, mpEventName) {
|
|
|
160
172
|
|
|
161
173
|
var isCapturedForHeatMap = this.mp.is_recording_heatmap_data() && (
|
|
162
174
|
(mpEventName === MP_EV_CLICK && !this.getConfig(CONFIG_TRACK_CLICK)) ||
|
|
163
|
-
(mpEventName === MP_EV_RAGE_CLICK && !this.
|
|
175
|
+
(mpEventName === MP_EV_RAGE_CLICK && !this._getClickTrackingConfig(CONFIG_TRACK_RAGE_CLICK)) ||
|
|
176
|
+
(mpEventName === MP_EV_DEAD_CLICK && !this._getClickTrackingConfig(CONFIG_TRACK_DEAD_CLICK))
|
|
164
177
|
);
|
|
165
178
|
|
|
166
179
|
var props = getPropsForDOMEvent(ev, {
|
|
@@ -179,11 +192,45 @@ Autocapture.prototype.trackDomEvent = function(ev, mpEventName) {
|
|
|
179
192
|
}
|
|
180
193
|
};
|
|
181
194
|
|
|
182
|
-
Autocapture.prototype.
|
|
183
|
-
|
|
195
|
+
Autocapture.prototype.initPageListeners = function() {
|
|
196
|
+
window.removeEventListener(EV_POPSTATE, this.listenerPopstate);
|
|
197
|
+
window.removeEventListener(EV_HASHCHANGE, this.listenerHashchange);
|
|
198
|
+
|
|
199
|
+
if (!this.pageviewTrackingConfig() && !this.getConfig(CONFIG_TRACK_PAGE_LEAVE) && !this.mp.get_config('record_heatmap_data')) {
|
|
200
|
+
// These are all the configs that use these listeners
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
this.listenerPopstate = function() {
|
|
205
|
+
window.dispatchEvent(new Event(EV_MP_LOCATION_CHANGE));
|
|
206
|
+
};
|
|
207
|
+
this.listenerHashchange = function() {
|
|
208
|
+
window.dispatchEvent(new Event(EV_MP_LOCATION_CHANGE));
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
window.addEventListener(EV_POPSTATE, this.listenerPopstate);
|
|
212
|
+
window.addEventListener(EV_HASHCHANGE, this.listenerHashchange);
|
|
213
|
+
var nativePushState = window.history.pushState;
|
|
214
|
+
if (typeof nativePushState === 'function') {
|
|
215
|
+
window.history.pushState = function(state, unused, url) {
|
|
216
|
+
nativePushState.call(window.history, state, unused, url);
|
|
217
|
+
window.dispatchEvent(new Event(EV_MP_LOCATION_CHANGE));
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
var nativeReplaceState = window.history.replaceState;
|
|
221
|
+
if (typeof nativeReplaceState === 'function') {
|
|
222
|
+
window.history.replaceState = function(state, unused, url) {
|
|
223
|
+
nativeReplaceState.call(window.history, state, unused, url);
|
|
224
|
+
window.dispatchEvent(new Event(EV_MP_LOCATION_CHANGE));
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
Autocapture.prototype._getClickTrackingConfig = function(configKey) {
|
|
230
|
+
var config = this.getConfig(configKey);
|
|
184
231
|
|
|
185
232
|
if (!config) {
|
|
186
|
-
return null; //
|
|
233
|
+
return null; // click tracking disabled
|
|
187
234
|
}
|
|
188
235
|
|
|
189
236
|
if (config === true) {
|
|
@@ -197,6 +244,70 @@ Autocapture.prototype._getRageClickConfig = function() {
|
|
|
197
244
|
return {}; // fallback to defaults for any other truthy value
|
|
198
245
|
};
|
|
199
246
|
|
|
247
|
+
Autocapture.prototype._trackPageLeave = function(ev, currentUrl, currentScrollHeight) {
|
|
248
|
+
if (this.hasTrackedScrollSession) {
|
|
249
|
+
// User has navigated away already ending their impression.
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (!this.getConfig(CONFIG_TRACK_PAGE_LEAVE) && !this.mp.is_recording_heatmap_data()) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
this.hasTrackedScrollSession = true;
|
|
258
|
+
var viewportHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
|
|
259
|
+
var scrollPercentage = Math.round(Math.max(this.maxScrollViewDepth - viewportHeight, 0) / (currentScrollHeight - viewportHeight) * 100);
|
|
260
|
+
var foldLinePercentage = Math.round((viewportHeight / currentScrollHeight) * 100);
|
|
261
|
+
if (currentScrollHeight <= viewportHeight) {
|
|
262
|
+
// If the content fits within the viewport, consider it fully scrolled
|
|
263
|
+
scrollPercentage = 100;
|
|
264
|
+
foldLinePercentage = 100;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
var props = _.extend({
|
|
268
|
+
'$max_scroll_view_depth': this.maxScrollViewDepth,
|
|
269
|
+
'$max_scroll_percentage': scrollPercentage,
|
|
270
|
+
'$fold_line_percentage': foldLinePercentage,
|
|
271
|
+
'$scroll_height': currentScrollHeight,
|
|
272
|
+
'$event_type': ev.type,
|
|
273
|
+
'$current_url': currentUrl || _.info.currentUrl(),
|
|
274
|
+
'$viewportHeight': viewportHeight, // This is the fold line
|
|
275
|
+
'$viewportWidth': Math.max(document.documentElement.clientWidth, window.innerWidth || 0),
|
|
276
|
+
'$captured_for_heatmap': this.mp.is_recording_heatmap_data()
|
|
277
|
+
}, DEFAULT_PROPS);
|
|
278
|
+
|
|
279
|
+
// Send with beacon transport to ensure event is sent before unload
|
|
280
|
+
this.mp.track(MP_EV_PAGE_LEAVE, props, {transport: 'sendBeacon'});
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
Autocapture.prototype._initScrollDepthTracking = function() {
|
|
284
|
+
window.removeEventListener(EV_SCROLL, this.listenerScrollDepth);
|
|
285
|
+
window.removeEventListener(EV_SCROLLEND, this.listenerScrollDepth);
|
|
286
|
+
|
|
287
|
+
if (!this.mp.get_config('record_heatmap_data')) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
logger.log('Initializing scroll depth tracking');
|
|
292
|
+
|
|
293
|
+
this.maxScrollViewDepth = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
|
|
294
|
+
|
|
295
|
+
var updateScrollDepth = function() {
|
|
296
|
+
if (this.currentUrlBlocked()) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
var scrollViewHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0) + window.scrollY;
|
|
300
|
+
if (scrollViewHeight > this.maxScrollViewDepth) {
|
|
301
|
+
this.maxScrollViewDepth = scrollViewHeight;
|
|
302
|
+
}
|
|
303
|
+
this.previousScrollHeight = document.body.scrollHeight;
|
|
304
|
+
}.bind(this);
|
|
305
|
+
|
|
306
|
+
var scrollEndPolyfill = getPolyfillScrollEndFunction(updateScrollDepth);
|
|
307
|
+
this.listenerScrollDepth = scrollEndPolyfill.listener;
|
|
308
|
+
window.addEventListener(scrollEndPolyfill.eventType, this.listenerScrollDepth);
|
|
309
|
+
};
|
|
310
|
+
|
|
200
311
|
Autocapture.prototype.initClickTracking = function() {
|
|
201
312
|
window.removeEventListener(EV_CLICK, this.listenerClick);
|
|
202
313
|
|
|
@@ -205,12 +316,49 @@ Autocapture.prototype.initClickTracking = function() {
|
|
|
205
316
|
}
|
|
206
317
|
logger.log('Initializing click tracking');
|
|
207
318
|
|
|
208
|
-
this.listenerClick =
|
|
319
|
+
this.listenerClick = function(ev) {
|
|
209
320
|
if (!this.getConfig(CONFIG_TRACK_CLICK) && !this.mp.is_recording_heatmap_data()) {
|
|
210
321
|
return;
|
|
211
322
|
}
|
|
212
323
|
this.trackDomEvent(ev, MP_EV_CLICK);
|
|
213
|
-
}.bind(this)
|
|
324
|
+
}.bind(this);
|
|
325
|
+
window.addEventListener(EV_CLICK, this.listenerClick);
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
Autocapture.prototype.initDeadClickTracking = function() {
|
|
329
|
+
var deadClickConfig = this._getClickTrackingConfig(CONFIG_TRACK_DEAD_CLICK);
|
|
330
|
+
|
|
331
|
+
if (!deadClickConfig && !this.mp.get_config('record_heatmap_data')) {
|
|
332
|
+
this.stopDeadClickTracking();
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
logger.log('Initializing dead click tracking');
|
|
337
|
+
if (!this._deadClickTracker) {
|
|
338
|
+
this._deadClickTracker = new DeadClickTracker(function(deadClickEvent) {
|
|
339
|
+
this.trackDomEvent(deadClickEvent, MP_EV_DEAD_CLICK);
|
|
340
|
+
}.bind(this));
|
|
341
|
+
this._deadClickTracker.startTracking();
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (!this.listenerDeadClick) {
|
|
345
|
+
this.listenerDeadClick = function(ev) {
|
|
346
|
+
var currentDeadClickConfig = this._getClickTrackingConfig(CONFIG_TRACK_DEAD_CLICK);
|
|
347
|
+
if (!currentDeadClickConfig && !this.mp.is_recording_heatmap_data()) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
if (this.currentUrlBlocked()) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
// Normalize config to ensure timeout_ms is always set
|
|
354
|
+
var normalizedConfig = currentDeadClickConfig || {};
|
|
355
|
+
if (!normalizedConfig['timeout_ms']) {
|
|
356
|
+
normalizedConfig['timeout_ms'] = DEFAULT_DEAD_CLICK_TIMEOUT_MS;
|
|
357
|
+
}
|
|
358
|
+
this._deadClickTracker.trackClick(ev, normalizedConfig);
|
|
359
|
+
}.bind(this);
|
|
360
|
+
window.addEventListener(EV_CLICK, this.listenerDeadClick);
|
|
361
|
+
}
|
|
214
362
|
};
|
|
215
363
|
|
|
216
364
|
Autocapture.prototype.initInputTracking = function() {
|
|
@@ -221,17 +369,16 @@ Autocapture.prototype.initInputTracking = function() {
|
|
|
221
369
|
}
|
|
222
370
|
logger.log('Initializing input tracking');
|
|
223
371
|
|
|
224
|
-
this.listenerChange =
|
|
372
|
+
this.listenerChange = function(ev) {
|
|
225
373
|
if (!this.getConfig(CONFIG_TRACK_INPUT)) {
|
|
226
374
|
return;
|
|
227
375
|
}
|
|
228
376
|
this.trackDomEvent(ev, MP_EV_INPUT);
|
|
229
|
-
}.bind(this)
|
|
377
|
+
}.bind(this);
|
|
378
|
+
window.addEventListener(EV_CHANGE, this.listenerChange);
|
|
230
379
|
};
|
|
231
380
|
|
|
232
381
|
Autocapture.prototype.initPageviewTracking = function() {
|
|
233
|
-
window.removeEventListener(EV_POPSTATE, this.listenerPopstate);
|
|
234
|
-
window.removeEventListener(EV_HASHCHANGE, this.listenerHashchange);
|
|
235
382
|
window.removeEventListener(EV_MP_LOCATION_CHANGE, this.listenerLocationchange);
|
|
236
383
|
|
|
237
384
|
if (!this.pageviewTrackingConfig()) {
|
|
@@ -248,27 +395,7 @@ Autocapture.prototype.initPageviewTracking = function() {
|
|
|
248
395
|
previousTrackedUrl = _.info.currentUrl();
|
|
249
396
|
}
|
|
250
397
|
|
|
251
|
-
this.
|
|
252
|
-
window.dispatchEvent(new Event(EV_MP_LOCATION_CHANGE));
|
|
253
|
-
});
|
|
254
|
-
this.listenerHashchange = window.addEventListener(EV_HASHCHANGE, function() {
|
|
255
|
-
window.dispatchEvent(new Event(EV_MP_LOCATION_CHANGE));
|
|
256
|
-
});
|
|
257
|
-
var nativePushState = window.history.pushState;
|
|
258
|
-
if (typeof nativePushState === 'function') {
|
|
259
|
-
window.history.pushState = function(state, unused, url) {
|
|
260
|
-
nativePushState.call(window.history, state, unused, url);
|
|
261
|
-
window.dispatchEvent(new Event(EV_MP_LOCATION_CHANGE));
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
var nativeReplaceState = window.history.replaceState;
|
|
265
|
-
if (typeof nativeReplaceState === 'function') {
|
|
266
|
-
window.history.replaceState = function(state, unused, url) {
|
|
267
|
-
nativeReplaceState.call(window.history, state, unused, url);
|
|
268
|
-
window.dispatchEvent(new Event(EV_MP_LOCATION_CHANGE));
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
this.listenerLocationchange = window.addEventListener(EV_MP_LOCATION_CHANGE, safewrap(function() {
|
|
398
|
+
this.listenerLocationchange = safewrap(function() {
|
|
272
399
|
if (this.currentUrlBlocked()) {
|
|
273
400
|
return;
|
|
274
401
|
}
|
|
@@ -295,13 +422,14 @@ Autocapture.prototype.initPageviewTracking = function() {
|
|
|
295
422
|
logger.log('Path change: re-initializing scroll depth checkpoints');
|
|
296
423
|
}
|
|
297
424
|
}
|
|
298
|
-
}.bind(this))
|
|
425
|
+
}.bind(this));
|
|
426
|
+
window.addEventListener(EV_MP_LOCATION_CHANGE, this.listenerLocationchange);
|
|
299
427
|
};
|
|
300
428
|
|
|
301
429
|
Autocapture.prototype.initRageClickTracking = function() {
|
|
302
430
|
window.removeEventListener(EV_CLICK, this.listenerRageClick);
|
|
303
431
|
|
|
304
|
-
var rageClickConfig = this.
|
|
432
|
+
var rageClickConfig = this._getClickTrackingConfig(CONFIG_TRACK_RAGE_CLICK);
|
|
305
433
|
if (!rageClickConfig && !this.mp.get_config('record_heatmap_data')) {
|
|
306
434
|
return;
|
|
307
435
|
}
|
|
@@ -312,7 +440,7 @@ Autocapture.prototype.initRageClickTracking = function() {
|
|
|
312
440
|
}
|
|
313
441
|
|
|
314
442
|
this.listenerRageClick = function(ev) {
|
|
315
|
-
var currentRageClickConfig = this.
|
|
443
|
+
var currentRageClickConfig = this._getClickTrackingConfig(CONFIG_TRACK_RAGE_CLICK);
|
|
316
444
|
if (!currentRageClickConfig && !this.mp.is_recording_heatmap_data()) {
|
|
317
445
|
return;
|
|
318
446
|
}
|
|
@@ -330,6 +458,8 @@ Autocapture.prototype.initRageClickTracking = function() {
|
|
|
330
458
|
|
|
331
459
|
Autocapture.prototype.initScrollTracking = function() {
|
|
332
460
|
window.removeEventListener(EV_SCROLLEND, this.listenerScroll);
|
|
461
|
+
window.removeEventListener(EV_SCROLL, this.listenerScroll);
|
|
462
|
+
|
|
333
463
|
|
|
334
464
|
if (!this.getConfig(CONFIG_TRACK_SCROLL)) {
|
|
335
465
|
return;
|
|
@@ -337,7 +467,7 @@ Autocapture.prototype.initScrollTracking = function() {
|
|
|
337
467
|
logger.log('Initializing scroll tracking');
|
|
338
468
|
this.lastScrollCheckpoint = 0;
|
|
339
469
|
|
|
340
|
-
|
|
470
|
+
var scrollTrackFunction = function() {
|
|
341
471
|
if (!this.getConfig(CONFIG_TRACK_SCROLL)) {
|
|
342
472
|
return;
|
|
343
473
|
}
|
|
@@ -376,7 +506,11 @@ Autocapture.prototype.initScrollTracking = function() {
|
|
|
376
506
|
if (shouldTrack) {
|
|
377
507
|
this.mp.track(MP_EV_SCROLL, props);
|
|
378
508
|
}
|
|
379
|
-
}.bind(this)
|
|
509
|
+
}.bind(this);
|
|
510
|
+
|
|
511
|
+
var scrollEndPolyfill = getPolyfillScrollEndFunction(scrollTrackFunction);
|
|
512
|
+
this.listenerScroll = scrollEndPolyfill.listener;
|
|
513
|
+
window.addEventListener(scrollEndPolyfill.eventType, this.listenerScroll);
|
|
380
514
|
};
|
|
381
515
|
|
|
382
516
|
Autocapture.prototype.initSubmitTracking = function() {
|
|
@@ -387,12 +521,76 @@ Autocapture.prototype.initSubmitTracking = function() {
|
|
|
387
521
|
}
|
|
388
522
|
logger.log('Initializing submit tracking');
|
|
389
523
|
|
|
390
|
-
this.listenerSubmit =
|
|
524
|
+
this.listenerSubmit = function(ev) {
|
|
391
525
|
if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
|
|
392
526
|
return;
|
|
393
527
|
}
|
|
394
528
|
this.trackDomEvent(ev, MP_EV_SUBMIT);
|
|
529
|
+
}.bind(this);
|
|
530
|
+
window.addEventListener(EV_SUBMIT, this.listenerSubmit);
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
Autocapture.prototype.initPageLeaveTracking = function() {
|
|
534
|
+
// Capture page_leave both when the user navigates away from the page (visibilitychange) as well
|
|
535
|
+
// as when they navigate to a different page within the SPA (popstate/pushstate/hashchange).
|
|
536
|
+
document.removeEventListener(EV_VISIBILITYCHANGE, this.listenerPageLeaveVisibilitychange);
|
|
537
|
+
window.removeEventListener(EV_MP_LOCATION_CHANGE, this.listenerPageLeaveLocationchange);
|
|
538
|
+
window.removeEventListener(EV_LOAD, this.listenerPageLoad);
|
|
539
|
+
|
|
540
|
+
if (!this.getConfig(CONFIG_TRACK_PAGE_LEAVE) && !this.mp.get_config('record_heatmap_data')) {
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
logger.log('Initializing page visibility tracking.');
|
|
545
|
+
this._initScrollDepthTracking();
|
|
546
|
+
var previousTrackedUrl = _.info.currentUrl();
|
|
547
|
+
|
|
548
|
+
// Initialize previousScrollHeight on `load` which handles async loading
|
|
549
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event
|
|
550
|
+
this.listenerPageLoad = function() {
|
|
551
|
+
this.previousScrollHeight = document.body.scrollHeight;
|
|
552
|
+
}.bind(this);
|
|
553
|
+
window.addEventListener(EV_LOAD, this.listenerPageLoad);
|
|
554
|
+
|
|
555
|
+
// Track page navigation events similar to how initPageviewTracking does it
|
|
556
|
+
this.listenerPageLeaveLocationchange = safewrap(function(ev) {
|
|
557
|
+
if (this.currentUrlBlocked()) {
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
var currentUrl = _.info.currentUrl();
|
|
562
|
+
// Track all URL changes including query string or fragment changes as separate scroll sessions
|
|
563
|
+
var shouldTrack = currentUrl !== previousTrackedUrl;
|
|
564
|
+
|
|
565
|
+
if (shouldTrack) {
|
|
566
|
+
this._trackPageLeave(ev, previousTrackedUrl, this.previousScrollHeight);
|
|
567
|
+
previousTrackedUrl = currentUrl;
|
|
568
|
+
// Fragment navigation should call scroll(end) and trigger listener, don't add window.scrollY here.
|
|
569
|
+
this.maxScrollViewDepth = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
|
|
570
|
+
this.previousScrollHeight = document.body.scrollHeight;
|
|
571
|
+
this.hasTrackedScrollSession = false;
|
|
572
|
+
}
|
|
395
573
|
}.bind(this));
|
|
574
|
+
window.addEventListener(EV_MP_LOCATION_CHANGE, this.listenerPageLeaveLocationchange);
|
|
575
|
+
|
|
576
|
+
this.listenerPageLeaveVisibilitychange = function(ev) {
|
|
577
|
+
if (document.hidden) {
|
|
578
|
+
this._trackPageLeave(ev, previousTrackedUrl, this.previousScrollHeight);
|
|
579
|
+
}
|
|
580
|
+
}.bind(this);
|
|
581
|
+
document.addEventListener(EV_VISIBILITYCHANGE, this.listenerPageLeaveVisibilitychange);
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
Autocapture.prototype.stopDeadClickTracking = function() {
|
|
585
|
+
if (this.listenerDeadClick) {
|
|
586
|
+
window.removeEventListener(EV_CLICK, this.listenerDeadClick);
|
|
587
|
+
this.listenerDeadClick = null;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if (this._deadClickTracker) {
|
|
591
|
+
this._deadClickTracker.stopTracking();
|
|
592
|
+
this._deadClickTracker = null;
|
|
593
|
+
}
|
|
396
594
|
};
|
|
397
595
|
|
|
398
596
|
// TODO integrate error_reporter from mixpanel instance
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { logger, weakSetSupported } from './utils';
|
|
2
|
+
|
|
3
|
+
function ShadowDOMObserver(changeCallback, observerConfig) {
|
|
4
|
+
this.changeCallback = changeCallback || function() {};
|
|
5
|
+
this.observerConfig = observerConfig;
|
|
6
|
+
|
|
7
|
+
this.observedShadowRoots = null;
|
|
8
|
+
this.shadowObservers = [];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
ShadowDOMObserver.prototype.getEventTarget = function(event) {
|
|
12
|
+
if (!this.observedShadowRoots) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
var path = this.getComposedPath(event);
|
|
16
|
+
if (path && path.length) {
|
|
17
|
+
return path[0];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return event['target'] || event['srcElement'];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
ShadowDOMObserver.prototype.getComposedPath = function(event) {
|
|
25
|
+
if ('composedPath' in event) {
|
|
26
|
+
return event['composedPath']();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return [];
|
|
30
|
+
};
|
|
31
|
+
ShadowDOMObserver.prototype.observeFromEvent = function(event) {
|
|
32
|
+
if (!this.observedShadowRoots) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
var path = this.getComposedPath(event);
|
|
37
|
+
|
|
38
|
+
// Check each element in path for shadow roots
|
|
39
|
+
for (var i = 0; i < path.length; i++) {
|
|
40
|
+
var element = path[i];
|
|
41
|
+
|
|
42
|
+
if (element && element.shadowRoot) {
|
|
43
|
+
this.observeShadowRoot(element.shadowRoot);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
ShadowDOMObserver.prototype.observeShadowRoot = function(shadowRoot) {
|
|
50
|
+
if (!this.observedShadowRoots || this.observedShadowRoots.has(shadowRoot)) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
var self = this;
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
this.observedShadowRoots.add(shadowRoot);
|
|
58
|
+
|
|
59
|
+
var observer = new window.MutationObserver(function() {
|
|
60
|
+
self.changeCallback();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
observer.observe(shadowRoot, this.observerConfig);
|
|
64
|
+
this.shadowObservers.push(observer);
|
|
65
|
+
} catch (e) {
|
|
66
|
+
logger.critical('Error while observing shadow root', e);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
ShadowDOMObserver.prototype.start = function() {
|
|
72
|
+
if (this.observedShadowRoots) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!weakSetSupported()) {
|
|
77
|
+
logger.critical('Shadow DOM observation unavailable: WeakSet not supported');
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
this.observedShadowRoots = new WeakSet();
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
ShadowDOMObserver.prototype.stop = function() {
|
|
85
|
+
if (!this.observedShadowRoots) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
for (var i = 0; i < this.shadowObservers.length; i++) {
|
|
90
|
+
try {
|
|
91
|
+
this.shadowObservers[i].disconnect();
|
|
92
|
+
} catch (e) {
|
|
93
|
+
logger.critical('Error while disconnecting shadow DOM observer', e);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
this.shadowObservers = [];
|
|
97
|
+
this.observedShadowRoots = null;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export { ShadowDOMObserver };
|