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/dist/mixpanel.module.js
CHANGED
|
@@ -1593,7 +1593,8 @@ function snapshot(n2, options) {
|
|
|
1593
1593
|
week: true,
|
|
1594
1594
|
textarea: true,
|
|
1595
1595
|
select: true,
|
|
1596
|
-
password: true
|
|
1596
|
+
password: true,
|
|
1597
|
+
hidden: true
|
|
1597
1598
|
} : maskAllInputs === false ? {
|
|
1598
1599
|
password: true
|
|
1599
1600
|
} : maskAllInputs;
|
|
@@ -13235,7 +13236,8 @@ function record(options) {
|
|
|
13235
13236
|
week: true,
|
|
13236
13237
|
textarea: true,
|
|
13237
13238
|
select: true,
|
|
13238
|
-
password: true
|
|
13239
|
+
password: true,
|
|
13240
|
+
hidden: true
|
|
13239
13241
|
} : _maskInputOptions !== void 0 ? _maskInputOptions : {
|
|
13240
13242
|
password: true
|
|
13241
13243
|
};
|
|
@@ -14039,7 +14041,7 @@ if (typeof Promise !== 'undefined' && Promise.toString().indexOf('[native code]'
|
|
|
14039
14041
|
|
|
14040
14042
|
var Config = {
|
|
14041
14043
|
DEBUG: false,
|
|
14042
|
-
LIB_VERSION: '2.
|
|
14044
|
+
LIB_VERSION: '2.71.1'
|
|
14043
14045
|
};
|
|
14044
14046
|
|
|
14045
14047
|
/* eslint camelcase: "off", eqeqeq: "off" */
|
|
@@ -15718,6 +15720,20 @@ var cheap_guid = function(maxlen) {
|
|
|
15718
15720
|
return maxlen ? guid.substring(0, maxlen) : guid;
|
|
15719
15721
|
};
|
|
15720
15722
|
|
|
15723
|
+
/**
|
|
15724
|
+
* Generates a W3C traceparent header for easy interop with distributed tracing systems i.e Open Telemetry
|
|
15725
|
+
* https://www.w3.org/TR/trace-context/#traceparent-header
|
|
15726
|
+
*/
|
|
15727
|
+
var generateTraceparent = function() {
|
|
15728
|
+
var traceID = _.UUID().replace(/-/g, '');
|
|
15729
|
+
var parentID = _.UUID().replace(/-/g, '').substring(0, 16);
|
|
15730
|
+
|
|
15731
|
+
// Sampled trace
|
|
15732
|
+
var traceFlags = '01';
|
|
15733
|
+
|
|
15734
|
+
return '00-' + traceID + '-' + parentID + '-' + traceFlags;
|
|
15735
|
+
};
|
|
15736
|
+
|
|
15721
15737
|
// naive way to extract domain name (example.com) from full hostname (my.sub.example.com)
|
|
15722
15738
|
var SIMPLE_DOMAIN_MATCH_REGEX = /[a-z0-9][a-z0-9-]*\.[a-z]+$/i;
|
|
15723
15739
|
// this next one attempts to account for some ccSLDs, e.g. extracting oxford.ac.uk from www.oxford.ac.uk
|
|
@@ -17853,11 +17869,17 @@ win['__mp_recorder'] = MixpanelRecorder;
|
|
|
17853
17869
|
var EV_CHANGE = 'change';
|
|
17854
17870
|
var EV_CLICK = 'click';
|
|
17855
17871
|
var EV_HASHCHANGE = 'hashchange';
|
|
17872
|
+
var EV_INPUT = 'input';
|
|
17873
|
+
var EV_LOAD = 'load';
|
|
17856
17874
|
var EV_MP_LOCATION_CHANGE = 'mp_locationchange';
|
|
17857
17875
|
var EV_POPSTATE = 'popstate';
|
|
17858
17876
|
// TODO scrollend isn't available in Safari: document or polyfill?
|
|
17859
17877
|
var EV_SCROLLEND = 'scrollend';
|
|
17878
|
+
var EV_SCROLL = 'scroll';
|
|
17879
|
+
var EV_SELECT = 'select';
|
|
17860
17880
|
var EV_SUBMIT = 'submit';
|
|
17881
|
+
var EV_TOGGLE = 'toggle';
|
|
17882
|
+
var EV_VISIBILITYCHANGE = 'visibilitychange';
|
|
17861
17883
|
|
|
17862
17884
|
var CLICK_EVENT_PROPS = [
|
|
17863
17885
|
'clientX', 'clientY',
|
|
@@ -17874,6 +17896,77 @@ var TRACKED_ATTRS = [
|
|
|
17874
17896
|
'href', 'name', 'role', 'title', 'type'
|
|
17875
17897
|
];
|
|
17876
17898
|
|
|
17899
|
+
var INTERACTIVE_ARIA_ROLES = {
|
|
17900
|
+
'button': true,
|
|
17901
|
+
'checkbox': true,
|
|
17902
|
+
'combobox': true,
|
|
17903
|
+
'grid': true,
|
|
17904
|
+
'link': true,
|
|
17905
|
+
'listbox': true,
|
|
17906
|
+
'menu': true,
|
|
17907
|
+
'menubar': true,
|
|
17908
|
+
'menuitem': true,
|
|
17909
|
+
'menuitemcheckbox': true,
|
|
17910
|
+
'menuitemradio': true,
|
|
17911
|
+
'navigation': true,
|
|
17912
|
+
'option': true,
|
|
17913
|
+
'radio': true,
|
|
17914
|
+
'radiogroup': true,
|
|
17915
|
+
'searchbox': true,
|
|
17916
|
+
'slider': true,
|
|
17917
|
+
'spinbutton': true,
|
|
17918
|
+
'switch': true,
|
|
17919
|
+
'tab': true,
|
|
17920
|
+
'tablist': true,
|
|
17921
|
+
'textbox': true,
|
|
17922
|
+
'tree': true,
|
|
17923
|
+
'treegrid': true,
|
|
17924
|
+
'treeitem': true
|
|
17925
|
+
};
|
|
17926
|
+
|
|
17927
|
+
var ALWAYS_NON_INTERACTIVE_TAGS = {
|
|
17928
|
+
// Document metadata
|
|
17929
|
+
'base': true,
|
|
17930
|
+
'head': true,
|
|
17931
|
+
'html': true,
|
|
17932
|
+
'link': true,
|
|
17933
|
+
'meta': true,
|
|
17934
|
+
'script': true,
|
|
17935
|
+
'style': true,
|
|
17936
|
+
'title': true,
|
|
17937
|
+
// Text formatting
|
|
17938
|
+
'br': true,
|
|
17939
|
+
'hr': true,
|
|
17940
|
+
'wbr': true,
|
|
17941
|
+
// Other
|
|
17942
|
+
'noscript': true,
|
|
17943
|
+
'picture': true,
|
|
17944
|
+
'source': true,
|
|
17945
|
+
'template': true,
|
|
17946
|
+
'track': true
|
|
17947
|
+
};
|
|
17948
|
+
|
|
17949
|
+
// Common container tags that need additional checks
|
|
17950
|
+
var TEXT_CONTAINER_TAGS = {
|
|
17951
|
+
'article': true,
|
|
17952
|
+
'div': true,
|
|
17953
|
+
'h1': true,
|
|
17954
|
+
'h2': true,
|
|
17955
|
+
'h3': true,
|
|
17956
|
+
'h4': true,
|
|
17957
|
+
'h5': true,
|
|
17958
|
+
'h6': true,
|
|
17959
|
+
'p': true,
|
|
17960
|
+
'section': true,
|
|
17961
|
+
'span': true
|
|
17962
|
+
};
|
|
17963
|
+
|
|
17964
|
+
var EVENT_HANDLER_ATTRIBUTES = [
|
|
17965
|
+
'onclick', 'onmousedown', 'onmouseup', 'onpointerdown', 'onpointerup', 'ontouchend', 'ontouchstart'
|
|
17966
|
+
];
|
|
17967
|
+
|
|
17968
|
+
var MAX_DEPTH = 5;
|
|
17969
|
+
|
|
17877
17970
|
var logger$1 = console_with_prefix('autocapture');
|
|
17878
17971
|
|
|
17879
17972
|
|
|
@@ -18241,6 +18334,10 @@ function minDOMApisSupported() {
|
|
|
18241
18334
|
}
|
|
18242
18335
|
}
|
|
18243
18336
|
|
|
18337
|
+
function weakSetSupported() {
|
|
18338
|
+
return typeof WeakSet !== 'undefined';
|
|
18339
|
+
}
|
|
18340
|
+
|
|
18244
18341
|
/*
|
|
18245
18342
|
* Check whether a DOM event should be "tracked" or if it may contain sensitive data
|
|
18246
18343
|
* using a variety of heuristics.
|
|
@@ -18366,6 +18463,149 @@ function shouldTrackValue(value) {
|
|
|
18366
18463
|
return true;
|
|
18367
18464
|
}
|
|
18368
18465
|
|
|
18466
|
+
/**
|
|
18467
|
+
* Creates a cross-browser compatible scroll end function with appropriate event listener.
|
|
18468
|
+
* For browsers that support scrollend, returns the original function with scrollend event.
|
|
18469
|
+
* For browsers without scrollend support, returns a debounced function that triggers
|
|
18470
|
+
* 100ms after the last scroll event to simulate scrollend behavior.
|
|
18471
|
+
* @param {Function} originalFunction - The function to call when scrolling ends
|
|
18472
|
+
* @returns {Object} Object containing listener function and eventType string
|
|
18473
|
+
* @returns {Function} returns.listener - The wrapped function to use as event listener
|
|
18474
|
+
* @returns {string} returns.eventType - The event type to listen for ('scrollend' or 'scroll')
|
|
18475
|
+
*/
|
|
18476
|
+
function getPolyfillScrollEndFunction(originalFunction) {
|
|
18477
|
+
var supportsScrollEnd = 'onscrollend' in win;
|
|
18478
|
+
var polyfillFunction = safewrap(originalFunction);
|
|
18479
|
+
var polyfillEvent = EV_SCROLLEND;
|
|
18480
|
+
if (!supportsScrollEnd) {
|
|
18481
|
+
// Polyfill for browsers without scrollend support: wait 100ms after the last scroll event
|
|
18482
|
+
// https://developer.chrome.com/blog/scrollend-a-new-javascript-event
|
|
18483
|
+
var scrollTimer = null;
|
|
18484
|
+
var scrollDelayMs = 100;
|
|
18485
|
+
|
|
18486
|
+
polyfillFunction = safewrap(function() {
|
|
18487
|
+
clearTimeout(scrollTimer);
|
|
18488
|
+
scrollTimer = setTimeout(originalFunction, scrollDelayMs);
|
|
18489
|
+
});
|
|
18490
|
+
|
|
18491
|
+
polyfillEvent = EV_SCROLL;
|
|
18492
|
+
}
|
|
18493
|
+
|
|
18494
|
+
return {
|
|
18495
|
+
listener: polyfillFunction,
|
|
18496
|
+
eventType: polyfillEvent
|
|
18497
|
+
};
|
|
18498
|
+
}
|
|
18499
|
+
|
|
18500
|
+
function hasInlineEventHandlers(element) {
|
|
18501
|
+
for (var i = 0; i < EVENT_HANDLER_ATTRIBUTES.length; i++) {
|
|
18502
|
+
if (element.hasAttribute(EVENT_HANDLER_ATTRIBUTES[i])) {
|
|
18503
|
+
return true;
|
|
18504
|
+
}
|
|
18505
|
+
}
|
|
18506
|
+
return false;
|
|
18507
|
+
}
|
|
18508
|
+
|
|
18509
|
+
function hasInteractiveAriaRole(element) {
|
|
18510
|
+
var role = element.getAttribute('role');
|
|
18511
|
+
if (!role) return false;
|
|
18512
|
+
|
|
18513
|
+
// Handle invalid markup where multiple roles might be specified
|
|
18514
|
+
// Only the first token is recognized per ARIA spec
|
|
18515
|
+
var primaryRole = role.trim().split(/\s+/)[0].toLowerCase();
|
|
18516
|
+
|
|
18517
|
+
return INTERACTIVE_ARIA_ROLES[primaryRole];
|
|
18518
|
+
}
|
|
18519
|
+
|
|
18520
|
+
function hasAnyInteractivityIndicators(element) {
|
|
18521
|
+
var tagName = element.tagName.toLowerCase();
|
|
18522
|
+
|
|
18523
|
+
// Check for interactive HTML elements
|
|
18524
|
+
if (tagName === 'button' ||
|
|
18525
|
+
tagName === 'input' ||
|
|
18526
|
+
tagName === 'select' ||
|
|
18527
|
+
tagName === 'textarea' ||
|
|
18528
|
+
tagName === 'details' ||
|
|
18529
|
+
tagName === 'dialog') {
|
|
18530
|
+
return true;
|
|
18531
|
+
}
|
|
18532
|
+
|
|
18533
|
+
if (element.isContentEditable) {
|
|
18534
|
+
return true;
|
|
18535
|
+
}
|
|
18536
|
+
|
|
18537
|
+
if (element.onclick || element.onmousedown || element.onmouseup || element.ontouchstart || element.ontouchend) {
|
|
18538
|
+
return true;
|
|
18539
|
+
}
|
|
18540
|
+
|
|
18541
|
+
if (hasInlineEventHandlers(element)) {
|
|
18542
|
+
return true;
|
|
18543
|
+
}
|
|
18544
|
+
|
|
18545
|
+
if (hasInteractiveAriaRole(element)) {
|
|
18546
|
+
return true;
|
|
18547
|
+
}
|
|
18548
|
+
|
|
18549
|
+
if (tagName === 'a' && element.hasAttribute('href')) {
|
|
18550
|
+
return true;
|
|
18551
|
+
}
|
|
18552
|
+
|
|
18553
|
+
if (element.hasAttribute('tabindex')) {
|
|
18554
|
+
return true;
|
|
18555
|
+
}
|
|
18556
|
+
|
|
18557
|
+
return false;
|
|
18558
|
+
}
|
|
18559
|
+
|
|
18560
|
+
|
|
18561
|
+
function isDefinitelyNonInteractive(element) {
|
|
18562
|
+
if (!element || !element.tagName) {
|
|
18563
|
+
return true;
|
|
18564
|
+
}
|
|
18565
|
+
|
|
18566
|
+
var tagName = element.tagName.toLowerCase();
|
|
18567
|
+
|
|
18568
|
+
// These tags are definitely non-interactive
|
|
18569
|
+
if (ALWAYS_NON_INTERACTIVE_TAGS[tagName]) {
|
|
18570
|
+
return true;
|
|
18571
|
+
}
|
|
18572
|
+
|
|
18573
|
+
// For all other elements, we can only be certain they're non-interactive if they lack ALL indicators of interactivity
|
|
18574
|
+
// Check for any signs of interactivity
|
|
18575
|
+
if (hasAnyInteractivityIndicators(element)) {
|
|
18576
|
+
return false;
|
|
18577
|
+
}
|
|
18578
|
+
|
|
18579
|
+
// Check parent chain for interactive context
|
|
18580
|
+
var parent = element.parentElement;
|
|
18581
|
+
var depth = 0;
|
|
18582
|
+
|
|
18583
|
+
while (parent && depth < MAX_DEPTH) {
|
|
18584
|
+
if (hasAnyInteractivityIndicators(parent)) {
|
|
18585
|
+
return false; // Element is inside an interactive parent
|
|
18586
|
+
}
|
|
18587
|
+
|
|
18588
|
+
if (parent.getRootNode && parent.getRootNode() !== document$1) {
|
|
18589
|
+
var root = parent.getRootNode();
|
|
18590
|
+
if (root.host && hasAnyInteractivityIndicators(root.host)) {
|
|
18591
|
+
return false; // Inside an interactive shadow host
|
|
18592
|
+
}
|
|
18593
|
+
}
|
|
18594
|
+
|
|
18595
|
+
parent = parent.parentElement;
|
|
18596
|
+
depth++;
|
|
18597
|
+
}
|
|
18598
|
+
|
|
18599
|
+
// Pure text containers without any interactive context
|
|
18600
|
+
if (TEXT_CONTAINER_TAGS[tagName]) {
|
|
18601
|
+
// These are non-interactive ONLY if they have no interactive indicators (already checked as part of hasAnyInteractivityIndicators)
|
|
18602
|
+
return true;
|
|
18603
|
+
}
|
|
18604
|
+
|
|
18605
|
+
// Default: we can't be certain it's non-interactive
|
|
18606
|
+
return false;
|
|
18607
|
+
}
|
|
18608
|
+
|
|
18369
18609
|
/** @const */ var DEFAULT_RAGE_CLICK_THRESHOLD_PX = 30;
|
|
18370
18610
|
/** @const */ var DEFAULT_RAGE_CLICK_TIMEOUT_MS = 1000;
|
|
18371
18611
|
/** @const */ var DEFAULT_RAGE_CLICK_CLICK_COUNT = 4;
|
|
@@ -18398,6 +18638,350 @@ RageClickTracker.prototype.isRageClick = function(x, y, options) {
|
|
|
18398
18638
|
return false;
|
|
18399
18639
|
};
|
|
18400
18640
|
|
|
18641
|
+
function ShadowDOMObserver(changeCallback, observerConfig) {
|
|
18642
|
+
this.changeCallback = changeCallback || function() {};
|
|
18643
|
+
this.observerConfig = observerConfig;
|
|
18644
|
+
|
|
18645
|
+
this.observedShadowRoots = null;
|
|
18646
|
+
this.shadowObservers = [];
|
|
18647
|
+
}
|
|
18648
|
+
|
|
18649
|
+
ShadowDOMObserver.prototype.getEventTarget = function(event) {
|
|
18650
|
+
if (!this.observedShadowRoots) {
|
|
18651
|
+
return;
|
|
18652
|
+
}
|
|
18653
|
+
var path = this.getComposedPath(event);
|
|
18654
|
+
if (path && path.length) {
|
|
18655
|
+
return path[0];
|
|
18656
|
+
}
|
|
18657
|
+
|
|
18658
|
+
return event['target'] || event['srcElement'];
|
|
18659
|
+
};
|
|
18660
|
+
|
|
18661
|
+
|
|
18662
|
+
ShadowDOMObserver.prototype.getComposedPath = function(event) {
|
|
18663
|
+
if ('composedPath' in event) {
|
|
18664
|
+
return event['composedPath']();
|
|
18665
|
+
}
|
|
18666
|
+
|
|
18667
|
+
return [];
|
|
18668
|
+
};
|
|
18669
|
+
ShadowDOMObserver.prototype.observeFromEvent = function(event) {
|
|
18670
|
+
if (!this.observedShadowRoots) {
|
|
18671
|
+
return;
|
|
18672
|
+
}
|
|
18673
|
+
|
|
18674
|
+
var path = this.getComposedPath(event);
|
|
18675
|
+
|
|
18676
|
+
// Check each element in path for shadow roots
|
|
18677
|
+
for (var i = 0; i < path.length; i++) {
|
|
18678
|
+
var element = path[i];
|
|
18679
|
+
|
|
18680
|
+
if (element && element.shadowRoot) {
|
|
18681
|
+
this.observeShadowRoot(element.shadowRoot);
|
|
18682
|
+
}
|
|
18683
|
+
}
|
|
18684
|
+
};
|
|
18685
|
+
|
|
18686
|
+
|
|
18687
|
+
ShadowDOMObserver.prototype.observeShadowRoot = function(shadowRoot) {
|
|
18688
|
+
if (!this.observedShadowRoots || this.observedShadowRoots.has(shadowRoot)) {
|
|
18689
|
+
return;
|
|
18690
|
+
}
|
|
18691
|
+
|
|
18692
|
+
var self = this;
|
|
18693
|
+
|
|
18694
|
+
try {
|
|
18695
|
+
this.observedShadowRoots.add(shadowRoot);
|
|
18696
|
+
|
|
18697
|
+
var observer = new window.MutationObserver(function() {
|
|
18698
|
+
self.changeCallback();
|
|
18699
|
+
});
|
|
18700
|
+
|
|
18701
|
+
observer.observe(shadowRoot, this.observerConfig);
|
|
18702
|
+
this.shadowObservers.push(observer);
|
|
18703
|
+
} catch (e) {
|
|
18704
|
+
logger$1.critical('Error while observing shadow root', e);
|
|
18705
|
+
}
|
|
18706
|
+
};
|
|
18707
|
+
|
|
18708
|
+
|
|
18709
|
+
ShadowDOMObserver.prototype.start = function() {
|
|
18710
|
+
if (this.observedShadowRoots) {
|
|
18711
|
+
return;
|
|
18712
|
+
}
|
|
18713
|
+
|
|
18714
|
+
if (!weakSetSupported()) {
|
|
18715
|
+
logger$1.critical('Shadow DOM observation unavailable: WeakSet not supported');
|
|
18716
|
+
return;
|
|
18717
|
+
}
|
|
18718
|
+
|
|
18719
|
+
this.observedShadowRoots = new WeakSet();
|
|
18720
|
+
};
|
|
18721
|
+
|
|
18722
|
+
ShadowDOMObserver.prototype.stop = function() {
|
|
18723
|
+
if (!this.observedShadowRoots) {
|
|
18724
|
+
return;
|
|
18725
|
+
}
|
|
18726
|
+
|
|
18727
|
+
for (var i = 0; i < this.shadowObservers.length; i++) {
|
|
18728
|
+
try {
|
|
18729
|
+
this.shadowObservers[i].disconnect();
|
|
18730
|
+
} catch (e) {
|
|
18731
|
+
logger$1.critical('Error while disconnecting shadow DOM observer', e);
|
|
18732
|
+
}
|
|
18733
|
+
}
|
|
18734
|
+
this.shadowObservers = [];
|
|
18735
|
+
this.observedShadowRoots = null;
|
|
18736
|
+
};
|
|
18737
|
+
|
|
18738
|
+
/** @const */ var DEFAULT_DEAD_CLICK_TIMEOUT_MS = 500;
|
|
18739
|
+
/** @const */ var INTERACTION_EVENTS = [EV_CHANGE, EV_INPUT, EV_SUBMIT, EV_SELECT, EV_TOGGLE];
|
|
18740
|
+
/** @const */ var LAYOUT_EVENTS = [EV_SCROLLEND];
|
|
18741
|
+
/** @const */ var NAVIGATION_EVENTS = [EV_HASHCHANGE];
|
|
18742
|
+
/** @const */ var MUTATION_OBSERVER_CONFIG = {
|
|
18743
|
+
characterData: true,
|
|
18744
|
+
childList: true,
|
|
18745
|
+
subtree: true,
|
|
18746
|
+
attributes: true,
|
|
18747
|
+
attributeFilter: ['style', 'class', 'hidden', 'checked', 'selected', 'value', 'display', 'visibility']
|
|
18748
|
+
};
|
|
18749
|
+
|
|
18750
|
+
|
|
18751
|
+
function DeadClickTracker(onDeadClickCallback) {
|
|
18752
|
+
this.eventListeners = [];
|
|
18753
|
+
this.mutationObserver = null;
|
|
18754
|
+
this.shadowDOMObserver = null;
|
|
18755
|
+
|
|
18756
|
+
this.isTracking = false;
|
|
18757
|
+
this.lastChangeEventTimestamp = 0;
|
|
18758
|
+
this.pendingClicks = [];
|
|
18759
|
+
this.onDeadClickCallback = onDeadClickCallback;
|
|
18760
|
+
this.processingActive = false;
|
|
18761
|
+
this.processingTimeout = null;
|
|
18762
|
+
}
|
|
18763
|
+
|
|
18764
|
+
|
|
18765
|
+
DeadClickTracker.prototype.addClick = function(event) {
|
|
18766
|
+
var element = this.shadowDOMObserver && this.shadowDOMObserver.getEventTarget(event);
|
|
18767
|
+
|
|
18768
|
+
if (!element) {
|
|
18769
|
+
element = event['target'] || event['srcElement'];
|
|
18770
|
+
}
|
|
18771
|
+
|
|
18772
|
+
if (!element || isDefinitelyNonInteractive(element)) {
|
|
18773
|
+
return false;
|
|
18774
|
+
}
|
|
18775
|
+
|
|
18776
|
+
if (this.shadowDOMObserver) {
|
|
18777
|
+
this.shadowDOMObserver.observeFromEvent(event);
|
|
18778
|
+
}
|
|
18779
|
+
this.pendingClicks.push({
|
|
18780
|
+
element: element,
|
|
18781
|
+
event: event,
|
|
18782
|
+
timestamp: Date.now()
|
|
18783
|
+
});
|
|
18784
|
+
return true;
|
|
18785
|
+
};
|
|
18786
|
+
|
|
18787
|
+
DeadClickTracker.prototype.trackClick = function(event, config) {
|
|
18788
|
+
if (!this.isTracking) {
|
|
18789
|
+
return false;
|
|
18790
|
+
}
|
|
18791
|
+
|
|
18792
|
+
var added = this.addClick(event);
|
|
18793
|
+
if (added) {
|
|
18794
|
+
this.triggerProcessing(config);
|
|
18795
|
+
}
|
|
18796
|
+
return added;
|
|
18797
|
+
};
|
|
18798
|
+
|
|
18799
|
+
DeadClickTracker.prototype.getDeadClicks = function(config) {
|
|
18800
|
+
if (this.pendingClicks.length === 0) {
|
|
18801
|
+
return [];
|
|
18802
|
+
}
|
|
18803
|
+
|
|
18804
|
+
var timeoutMs = config['timeout_ms'];
|
|
18805
|
+
var now = Date.now();
|
|
18806
|
+
var clicksToEvaluate = this.pendingClicks.slice(); // Copy array
|
|
18807
|
+
this.pendingClicks = []; // Clear original
|
|
18808
|
+
|
|
18809
|
+
var deadClicks = [];
|
|
18810
|
+
|
|
18811
|
+
for (var i = 0; i < clicksToEvaluate.length; i++) {
|
|
18812
|
+
var click = clicksToEvaluate[i];
|
|
18813
|
+
|
|
18814
|
+
if (now - click.timestamp >= timeoutMs) {
|
|
18815
|
+
// Click has exceeded timeout, check if it's dead by looking for changes after this specific click
|
|
18816
|
+
if (!this.hasChangesAfter(click.timestamp)) {
|
|
18817
|
+
deadClicks.push(click);
|
|
18818
|
+
}
|
|
18819
|
+
} else {
|
|
18820
|
+
// Still pending - add back
|
|
18821
|
+
this.pendingClicks.push(click);
|
|
18822
|
+
}
|
|
18823
|
+
}
|
|
18824
|
+
|
|
18825
|
+
return deadClicks;
|
|
18826
|
+
};
|
|
18827
|
+
|
|
18828
|
+
DeadClickTracker.prototype.hasChangesAfter = function(timestamp) {
|
|
18829
|
+
// 100ms tolerance for race condition between when we record the click and the change event
|
|
18830
|
+
return this.lastChangeEventTimestamp >= (timestamp - 100);
|
|
18831
|
+
};
|
|
18832
|
+
|
|
18833
|
+
DeadClickTracker.prototype.recordChangeEvent = function() {
|
|
18834
|
+
this.lastChangeEventTimestamp = Date.now();
|
|
18835
|
+
};
|
|
18836
|
+
|
|
18837
|
+
DeadClickTracker.prototype.triggerProcessing = function(config) {
|
|
18838
|
+
// Prevent multiple concurrent processing chains
|
|
18839
|
+
if (this.processingActive) {
|
|
18840
|
+
return;
|
|
18841
|
+
}
|
|
18842
|
+
this.processingActive = true;
|
|
18843
|
+
this.processRecursively(config);
|
|
18844
|
+
};
|
|
18845
|
+
|
|
18846
|
+
DeadClickTracker.prototype.processRecursively = function(config) {
|
|
18847
|
+
if (!this.isTracking || !this.onDeadClickCallback) {
|
|
18848
|
+
this.processingActive = false;
|
|
18849
|
+
return;
|
|
18850
|
+
}
|
|
18851
|
+
|
|
18852
|
+
var timeoutMs = config['timeout_ms'];
|
|
18853
|
+
var self = this;
|
|
18854
|
+
|
|
18855
|
+
this.processingTimeout = setTimeout(function() {
|
|
18856
|
+
if (!self.processingActive) {
|
|
18857
|
+
return;
|
|
18858
|
+
}
|
|
18859
|
+
|
|
18860
|
+
var deadClicks = self.getDeadClicks(config);
|
|
18861
|
+
|
|
18862
|
+
for (var i = 0; i < deadClicks.length; i++) {
|
|
18863
|
+
self.onDeadClickCallback(deadClicks[i].event);
|
|
18864
|
+
}
|
|
18865
|
+
|
|
18866
|
+
if (self.pendingClicks.length > 0) {
|
|
18867
|
+
self.processRecursively(config);
|
|
18868
|
+
} else {
|
|
18869
|
+
self.processingActive = false;
|
|
18870
|
+
}
|
|
18871
|
+
}, timeoutMs);
|
|
18872
|
+
};
|
|
18873
|
+
|
|
18874
|
+
DeadClickTracker.prototype.startTracking = function() {
|
|
18875
|
+
if (this.isTracking) {
|
|
18876
|
+
return;
|
|
18877
|
+
}
|
|
18878
|
+
|
|
18879
|
+
this.isTracking = true;
|
|
18880
|
+
|
|
18881
|
+
var self = this;
|
|
18882
|
+
|
|
18883
|
+
INTERACTION_EVENTS.forEach(function(event) {
|
|
18884
|
+
var handler = function() {
|
|
18885
|
+
self.recordChangeEvent();
|
|
18886
|
+
};
|
|
18887
|
+
document.addEventListener(event, handler, { capture: true, passive: true });
|
|
18888
|
+
self.eventListeners.push({ target: document, event: event, handler: handler, options: { capture: true, passive: true } });
|
|
18889
|
+
});
|
|
18890
|
+
NAVIGATION_EVENTS.forEach(function(event) {
|
|
18891
|
+
var handler = function() {
|
|
18892
|
+
self.recordChangeEvent();
|
|
18893
|
+
};
|
|
18894
|
+
window.addEventListener(event, handler);
|
|
18895
|
+
self.eventListeners.push({ target: window, event: event, handler: handler });
|
|
18896
|
+
});
|
|
18897
|
+
LAYOUT_EVENTS.forEach(function(event) {
|
|
18898
|
+
var handler = function() {
|
|
18899
|
+
self.recordChangeEvent();
|
|
18900
|
+
};
|
|
18901
|
+
window.addEventListener(event, handler, { passive: true });
|
|
18902
|
+
self.eventListeners.push({ target: window, event: event, handler: handler, options: { passive: true } });
|
|
18903
|
+
});
|
|
18904
|
+
var selectionHandler = function() {
|
|
18905
|
+
self.recordChangeEvent();
|
|
18906
|
+
};
|
|
18907
|
+
document.addEventListener('selectionchange', selectionHandler);
|
|
18908
|
+
self.eventListeners.push({ target: document, event: 'selectionchange', handler: selectionHandler });
|
|
18909
|
+
|
|
18910
|
+
// Set up MutationObserver
|
|
18911
|
+
if (window.MutationObserver) {
|
|
18912
|
+
try {
|
|
18913
|
+
this.mutationObserver = new window.MutationObserver(function() {
|
|
18914
|
+
self.recordChangeEvent();
|
|
18915
|
+
});
|
|
18916
|
+
|
|
18917
|
+
this.mutationObserver.observe(document.body || document.documentElement, MUTATION_OBSERVER_CONFIG);
|
|
18918
|
+
} catch (e) {
|
|
18919
|
+
logger$1.critical('Error while setting up mutation observer', e);
|
|
18920
|
+
}
|
|
18921
|
+
}
|
|
18922
|
+
|
|
18923
|
+
// Set up Shadow DOM observer
|
|
18924
|
+
if (window.customElements) {
|
|
18925
|
+
try {
|
|
18926
|
+
this.shadowDOMObserver = new ShadowDOMObserver(
|
|
18927
|
+
function() {
|
|
18928
|
+
self.recordChangeEvent();
|
|
18929
|
+
},
|
|
18930
|
+
MUTATION_OBSERVER_CONFIG
|
|
18931
|
+
);
|
|
18932
|
+
this.shadowDOMObserver.start();
|
|
18933
|
+
} catch (e) {
|
|
18934
|
+
logger$1.critical('Error while setting up shadow DOM observer', e);
|
|
18935
|
+
this.shadowDOMObserver = null;
|
|
18936
|
+
}
|
|
18937
|
+
}
|
|
18938
|
+
};
|
|
18939
|
+
|
|
18940
|
+
DeadClickTracker.prototype.stopTracking = function() {
|
|
18941
|
+
if (!this.isTracking) {
|
|
18942
|
+
return;
|
|
18943
|
+
}
|
|
18944
|
+
|
|
18945
|
+
this.isTracking = false;
|
|
18946
|
+
this.pendingClicks = [];
|
|
18947
|
+
this.lastChangeEventTimestamp = 0;
|
|
18948
|
+
this.processingActive = false;
|
|
18949
|
+
|
|
18950
|
+
if (this.processingTimeout) {
|
|
18951
|
+
clearTimeout(this.processingTimeout);
|
|
18952
|
+
this.processingTimeout = null;
|
|
18953
|
+
}
|
|
18954
|
+
|
|
18955
|
+
// Remove all event listeners
|
|
18956
|
+
for (var i = 0; i < this.eventListeners.length; i++) {
|
|
18957
|
+
var listener = this.eventListeners[i];
|
|
18958
|
+
try {
|
|
18959
|
+
listener.target.removeEventListener(listener.event, listener.handler, listener.options);
|
|
18960
|
+
} catch (e) {
|
|
18961
|
+
logger$1.critical('Error while removing event listener', e);
|
|
18962
|
+
}
|
|
18963
|
+
}
|
|
18964
|
+
this.eventListeners = [];
|
|
18965
|
+
|
|
18966
|
+
if (this.mutationObserver) {
|
|
18967
|
+
try {
|
|
18968
|
+
this.mutationObserver.disconnect();
|
|
18969
|
+
} catch (e) {
|
|
18970
|
+
logger$1.critical('Error while disconnecting mutation observer', e);
|
|
18971
|
+
}
|
|
18972
|
+
this.mutationObserver = null;
|
|
18973
|
+
}
|
|
18974
|
+
|
|
18975
|
+
if (this.shadowDOMObserver) {
|
|
18976
|
+
try {
|
|
18977
|
+
this.shadowDOMObserver.stop();
|
|
18978
|
+
} catch (e) {
|
|
18979
|
+
logger$1.critical('Error while stopping shadow DOM observer', e);
|
|
18980
|
+
}
|
|
18981
|
+
this.shadowDOMObserver = null;
|
|
18982
|
+
}
|
|
18983
|
+
};
|
|
18984
|
+
|
|
18401
18985
|
var AUTOCAPTURE_CONFIG_KEY = 'autocapture';
|
|
18402
18986
|
var LEGACY_PAGEVIEW_CONFIG_KEY = 'track_pageview';
|
|
18403
18987
|
|
|
@@ -18417,10 +19001,12 @@ var CONFIG_CAPTURE_TEXT_CONTENT = 'capture_text_content';
|
|
|
18417
19001
|
var CONFIG_SCROLL_CAPTURE_ALL = 'scroll_capture_all';
|
|
18418
19002
|
var CONFIG_SCROLL_CHECKPOINTS = 'scroll_depth_percent_checkpoints';
|
|
18419
19003
|
var CONFIG_TRACK_CLICK = 'click';
|
|
19004
|
+
var CONFIG_TRACK_DEAD_CLICK = 'dead_click';
|
|
18420
19005
|
var CONFIG_TRACK_INPUT = 'input';
|
|
18421
19006
|
var CONFIG_TRACK_PAGEVIEW = 'pageview';
|
|
18422
19007
|
var CONFIG_TRACK_RAGE_CLICK = 'rage_click';
|
|
18423
19008
|
var CONFIG_TRACK_SCROLL = 'scroll';
|
|
19009
|
+
var CONFIG_TRACK_PAGE_LEAVE = 'page_leave';
|
|
18424
19010
|
var CONFIG_TRACK_SUBMIT = 'submit';
|
|
18425
19011
|
|
|
18426
19012
|
var CONFIG_DEFAULTS$1 = {};
|
|
@@ -18435,10 +19021,12 @@ CONFIG_DEFAULTS$1[CONFIG_CAPTURE_TEXT_CONTENT] = false;
|
|
|
18435
19021
|
CONFIG_DEFAULTS$1[CONFIG_SCROLL_CAPTURE_ALL] = false;
|
|
18436
19022
|
CONFIG_DEFAULTS$1[CONFIG_SCROLL_CHECKPOINTS] = [25, 50, 75, 100];
|
|
18437
19023
|
CONFIG_DEFAULTS$1[CONFIG_TRACK_CLICK] = true;
|
|
19024
|
+
CONFIG_DEFAULTS$1[CONFIG_TRACK_DEAD_CLICK] = true;
|
|
18438
19025
|
CONFIG_DEFAULTS$1[CONFIG_TRACK_INPUT] = true;
|
|
18439
19026
|
CONFIG_DEFAULTS$1[CONFIG_TRACK_PAGEVIEW] = PAGEVIEW_OPTION_FULL_URL;
|
|
18440
19027
|
CONFIG_DEFAULTS$1[CONFIG_TRACK_RAGE_CLICK] = true;
|
|
18441
19028
|
CONFIG_DEFAULTS$1[CONFIG_TRACK_SCROLL] = true;
|
|
19029
|
+
CONFIG_DEFAULTS$1[CONFIG_TRACK_PAGE_LEAVE] = false;
|
|
18442
19030
|
CONFIG_DEFAULTS$1[CONFIG_TRACK_SUBMIT] = true;
|
|
18443
19031
|
|
|
18444
19032
|
var DEFAULT_PROPS = {
|
|
@@ -18446,10 +19034,12 @@ var DEFAULT_PROPS = {
|
|
|
18446
19034
|
};
|
|
18447
19035
|
|
|
18448
19036
|
var MP_EV_CLICK = '$mp_click';
|
|
19037
|
+
var MP_EV_DEAD_CLICK = '$mp_dead_click';
|
|
18449
19038
|
var MP_EV_INPUT = '$mp_input_change';
|
|
18450
19039
|
var MP_EV_RAGE_CLICK = '$mp_rage_click';
|
|
18451
19040
|
var MP_EV_SCROLL = '$mp_scroll';
|
|
18452
19041
|
var MP_EV_SUBMIT = '$mp_submit';
|
|
19042
|
+
var MP_EV_PAGE_LEAVE = '$mp_page_leave';
|
|
18453
19043
|
|
|
18454
19044
|
/**
|
|
18455
19045
|
* Autocapture: manages automatic event tracking
|
|
@@ -18457,6 +19047,9 @@ var MP_EV_SUBMIT = '$mp_submit';
|
|
|
18457
19047
|
*/
|
|
18458
19048
|
var Autocapture = function(mp) {
|
|
18459
19049
|
this.mp = mp;
|
|
19050
|
+
this.maxScrollViewDepth = 0;
|
|
19051
|
+
this.hasTrackedScrollSession = false;
|
|
19052
|
+
this.previousScrollHeight = 0;
|
|
18460
19053
|
};
|
|
18461
19054
|
|
|
18462
19055
|
Autocapture.prototype.init = function() {
|
|
@@ -18464,13 +19057,15 @@ Autocapture.prototype.init = function() {
|
|
|
18464
19057
|
logger$1.critical('Autocapture unavailable: missing required DOM APIs');
|
|
18465
19058
|
return;
|
|
18466
19059
|
}
|
|
18467
|
-
|
|
19060
|
+
this.initPageListeners();
|
|
18468
19061
|
this.initPageviewTracking();
|
|
18469
19062
|
this.initClickTracking();
|
|
19063
|
+
this.initDeadClickTracking();
|
|
18470
19064
|
this.initInputTracking();
|
|
18471
19065
|
this.initScrollTracking();
|
|
18472
19066
|
this.initSubmitTracking();
|
|
18473
19067
|
this.initRageClickTracking();
|
|
19068
|
+
this.initPageLeaveTracking();
|
|
18474
19069
|
};
|
|
18475
19070
|
|
|
18476
19071
|
Autocapture.prototype.getFullConfig = function() {
|
|
@@ -18551,7 +19146,8 @@ Autocapture.prototype.trackDomEvent = function(ev, mpEventName) {
|
|
|
18551
19146
|
|
|
18552
19147
|
var isCapturedForHeatMap = this.mp.is_recording_heatmap_data() && (
|
|
18553
19148
|
(mpEventName === MP_EV_CLICK && !this.getConfig(CONFIG_TRACK_CLICK)) ||
|
|
18554
|
-
(mpEventName === MP_EV_RAGE_CLICK && !this.
|
|
19149
|
+
(mpEventName === MP_EV_RAGE_CLICK && !this._getClickTrackingConfig(CONFIG_TRACK_RAGE_CLICK)) ||
|
|
19150
|
+
(mpEventName === MP_EV_DEAD_CLICK && !this._getClickTrackingConfig(CONFIG_TRACK_DEAD_CLICK))
|
|
18555
19151
|
);
|
|
18556
19152
|
|
|
18557
19153
|
var props = getPropsForDOMEvent(ev, {
|
|
@@ -18570,11 +19166,45 @@ Autocapture.prototype.trackDomEvent = function(ev, mpEventName) {
|
|
|
18570
19166
|
}
|
|
18571
19167
|
};
|
|
18572
19168
|
|
|
18573
|
-
Autocapture.prototype.
|
|
18574
|
-
|
|
19169
|
+
Autocapture.prototype.initPageListeners = function() {
|
|
19170
|
+
win.removeEventListener(EV_POPSTATE, this.listenerPopstate);
|
|
19171
|
+
win.removeEventListener(EV_HASHCHANGE, this.listenerHashchange);
|
|
19172
|
+
|
|
19173
|
+
if (!this.pageviewTrackingConfig() && !this.getConfig(CONFIG_TRACK_PAGE_LEAVE) && !this.mp.get_config('record_heatmap_data')) {
|
|
19174
|
+
// These are all the configs that use these listeners
|
|
19175
|
+
return;
|
|
19176
|
+
}
|
|
19177
|
+
|
|
19178
|
+
this.listenerPopstate = function() {
|
|
19179
|
+
win.dispatchEvent(new Event(EV_MP_LOCATION_CHANGE));
|
|
19180
|
+
};
|
|
19181
|
+
this.listenerHashchange = function() {
|
|
19182
|
+
win.dispatchEvent(new Event(EV_MP_LOCATION_CHANGE));
|
|
19183
|
+
};
|
|
19184
|
+
|
|
19185
|
+
win.addEventListener(EV_POPSTATE, this.listenerPopstate);
|
|
19186
|
+
win.addEventListener(EV_HASHCHANGE, this.listenerHashchange);
|
|
19187
|
+
var nativePushState = win.history.pushState;
|
|
19188
|
+
if (typeof nativePushState === 'function') {
|
|
19189
|
+
win.history.pushState = function(state, unused, url) {
|
|
19190
|
+
nativePushState.call(win.history, state, unused, url);
|
|
19191
|
+
win.dispatchEvent(new Event(EV_MP_LOCATION_CHANGE));
|
|
19192
|
+
};
|
|
19193
|
+
}
|
|
19194
|
+
var nativeReplaceState = win.history.replaceState;
|
|
19195
|
+
if (typeof nativeReplaceState === 'function') {
|
|
19196
|
+
win.history.replaceState = function(state, unused, url) {
|
|
19197
|
+
nativeReplaceState.call(win.history, state, unused, url);
|
|
19198
|
+
win.dispatchEvent(new Event(EV_MP_LOCATION_CHANGE));
|
|
19199
|
+
};
|
|
19200
|
+
}
|
|
19201
|
+
};
|
|
19202
|
+
|
|
19203
|
+
Autocapture.prototype._getClickTrackingConfig = function(configKey) {
|
|
19204
|
+
var config = this.getConfig(configKey);
|
|
18575
19205
|
|
|
18576
19206
|
if (!config) {
|
|
18577
|
-
return null; //
|
|
19207
|
+
return null; // click tracking disabled
|
|
18578
19208
|
}
|
|
18579
19209
|
|
|
18580
19210
|
if (config === true) {
|
|
@@ -18588,6 +19218,70 @@ Autocapture.prototype._getRageClickConfig = function() {
|
|
|
18588
19218
|
return {}; // fallback to defaults for any other truthy value
|
|
18589
19219
|
};
|
|
18590
19220
|
|
|
19221
|
+
Autocapture.prototype._trackPageLeave = function(ev, currentUrl, currentScrollHeight) {
|
|
19222
|
+
if (this.hasTrackedScrollSession) {
|
|
19223
|
+
// User has navigated away already ending their impression.
|
|
19224
|
+
return;
|
|
19225
|
+
}
|
|
19226
|
+
|
|
19227
|
+
if (!this.getConfig(CONFIG_TRACK_PAGE_LEAVE) && !this.mp.is_recording_heatmap_data()) {
|
|
19228
|
+
return;
|
|
19229
|
+
}
|
|
19230
|
+
|
|
19231
|
+
this.hasTrackedScrollSession = true;
|
|
19232
|
+
var viewportHeight = Math.max(document$1.documentElement.clientHeight, win.innerHeight || 0);
|
|
19233
|
+
var scrollPercentage = Math.round(Math.max(this.maxScrollViewDepth - viewportHeight, 0) / (currentScrollHeight - viewportHeight) * 100);
|
|
19234
|
+
var foldLinePercentage = Math.round((viewportHeight / currentScrollHeight) * 100);
|
|
19235
|
+
if (currentScrollHeight <= viewportHeight) {
|
|
19236
|
+
// If the content fits within the viewport, consider it fully scrolled
|
|
19237
|
+
scrollPercentage = 100;
|
|
19238
|
+
foldLinePercentage = 100;
|
|
19239
|
+
}
|
|
19240
|
+
|
|
19241
|
+
var props = _.extend({
|
|
19242
|
+
'$max_scroll_view_depth': this.maxScrollViewDepth,
|
|
19243
|
+
'$max_scroll_percentage': scrollPercentage,
|
|
19244
|
+
'$fold_line_percentage': foldLinePercentage,
|
|
19245
|
+
'$scroll_height': currentScrollHeight,
|
|
19246
|
+
'$event_type': ev.type,
|
|
19247
|
+
'$current_url': currentUrl || _.info.currentUrl(),
|
|
19248
|
+
'$viewportHeight': viewportHeight, // This is the fold line
|
|
19249
|
+
'$viewportWidth': Math.max(document$1.documentElement.clientWidth, win.innerWidth || 0),
|
|
19250
|
+
'$captured_for_heatmap': this.mp.is_recording_heatmap_data()
|
|
19251
|
+
}, DEFAULT_PROPS);
|
|
19252
|
+
|
|
19253
|
+
// Send with beacon transport to ensure event is sent before unload
|
|
19254
|
+
this.mp.track(MP_EV_PAGE_LEAVE, props, {transport: 'sendBeacon'});
|
|
19255
|
+
};
|
|
19256
|
+
|
|
19257
|
+
Autocapture.prototype._initScrollDepthTracking = function() {
|
|
19258
|
+
win.removeEventListener(EV_SCROLL, this.listenerScrollDepth);
|
|
19259
|
+
win.removeEventListener(EV_SCROLLEND, this.listenerScrollDepth);
|
|
19260
|
+
|
|
19261
|
+
if (!this.mp.get_config('record_heatmap_data')) {
|
|
19262
|
+
return;
|
|
19263
|
+
}
|
|
19264
|
+
|
|
19265
|
+
logger$1.log('Initializing scroll depth tracking');
|
|
19266
|
+
|
|
19267
|
+
this.maxScrollViewDepth = Math.max(document$1.documentElement.clientHeight, win.innerHeight || 0);
|
|
19268
|
+
|
|
19269
|
+
var updateScrollDepth = function() {
|
|
19270
|
+
if (this.currentUrlBlocked()) {
|
|
19271
|
+
return;
|
|
19272
|
+
}
|
|
19273
|
+
var scrollViewHeight = Math.max(document$1.documentElement.clientHeight, win.innerHeight || 0) + win.scrollY;
|
|
19274
|
+
if (scrollViewHeight > this.maxScrollViewDepth) {
|
|
19275
|
+
this.maxScrollViewDepth = scrollViewHeight;
|
|
19276
|
+
}
|
|
19277
|
+
this.previousScrollHeight = document$1.body.scrollHeight;
|
|
19278
|
+
}.bind(this);
|
|
19279
|
+
|
|
19280
|
+
var scrollEndPolyfill = getPolyfillScrollEndFunction(updateScrollDepth);
|
|
19281
|
+
this.listenerScrollDepth = scrollEndPolyfill.listener;
|
|
19282
|
+
win.addEventListener(scrollEndPolyfill.eventType, this.listenerScrollDepth);
|
|
19283
|
+
};
|
|
19284
|
+
|
|
18591
19285
|
Autocapture.prototype.initClickTracking = function() {
|
|
18592
19286
|
win.removeEventListener(EV_CLICK, this.listenerClick);
|
|
18593
19287
|
|
|
@@ -18596,12 +19290,49 @@ Autocapture.prototype.initClickTracking = function() {
|
|
|
18596
19290
|
}
|
|
18597
19291
|
logger$1.log('Initializing click tracking');
|
|
18598
19292
|
|
|
18599
|
-
this.listenerClick =
|
|
19293
|
+
this.listenerClick = function(ev) {
|
|
18600
19294
|
if (!this.getConfig(CONFIG_TRACK_CLICK) && !this.mp.is_recording_heatmap_data()) {
|
|
18601
19295
|
return;
|
|
18602
19296
|
}
|
|
18603
19297
|
this.trackDomEvent(ev, MP_EV_CLICK);
|
|
18604
|
-
}.bind(this)
|
|
19298
|
+
}.bind(this);
|
|
19299
|
+
win.addEventListener(EV_CLICK, this.listenerClick);
|
|
19300
|
+
};
|
|
19301
|
+
|
|
19302
|
+
Autocapture.prototype.initDeadClickTracking = function() {
|
|
19303
|
+
var deadClickConfig = this._getClickTrackingConfig(CONFIG_TRACK_DEAD_CLICK);
|
|
19304
|
+
|
|
19305
|
+
if (!deadClickConfig && !this.mp.get_config('record_heatmap_data')) {
|
|
19306
|
+
this.stopDeadClickTracking();
|
|
19307
|
+
return;
|
|
19308
|
+
}
|
|
19309
|
+
|
|
19310
|
+
logger$1.log('Initializing dead click tracking');
|
|
19311
|
+
if (!this._deadClickTracker) {
|
|
19312
|
+
this._deadClickTracker = new DeadClickTracker(function(deadClickEvent) {
|
|
19313
|
+
this.trackDomEvent(deadClickEvent, MP_EV_DEAD_CLICK);
|
|
19314
|
+
}.bind(this));
|
|
19315
|
+
this._deadClickTracker.startTracking();
|
|
19316
|
+
}
|
|
19317
|
+
|
|
19318
|
+
if (!this.listenerDeadClick) {
|
|
19319
|
+
this.listenerDeadClick = function(ev) {
|
|
19320
|
+
var currentDeadClickConfig = this._getClickTrackingConfig(CONFIG_TRACK_DEAD_CLICK);
|
|
19321
|
+
if (!currentDeadClickConfig && !this.mp.is_recording_heatmap_data()) {
|
|
19322
|
+
return;
|
|
19323
|
+
}
|
|
19324
|
+
if (this.currentUrlBlocked()) {
|
|
19325
|
+
return;
|
|
19326
|
+
}
|
|
19327
|
+
// Normalize config to ensure timeout_ms is always set
|
|
19328
|
+
var normalizedConfig = currentDeadClickConfig || {};
|
|
19329
|
+
if (!normalizedConfig['timeout_ms']) {
|
|
19330
|
+
normalizedConfig['timeout_ms'] = DEFAULT_DEAD_CLICK_TIMEOUT_MS;
|
|
19331
|
+
}
|
|
19332
|
+
this._deadClickTracker.trackClick(ev, normalizedConfig);
|
|
19333
|
+
}.bind(this);
|
|
19334
|
+
win.addEventListener(EV_CLICK, this.listenerDeadClick);
|
|
19335
|
+
}
|
|
18605
19336
|
};
|
|
18606
19337
|
|
|
18607
19338
|
Autocapture.prototype.initInputTracking = function() {
|
|
@@ -18612,17 +19343,16 @@ Autocapture.prototype.initInputTracking = function() {
|
|
|
18612
19343
|
}
|
|
18613
19344
|
logger$1.log('Initializing input tracking');
|
|
18614
19345
|
|
|
18615
|
-
this.listenerChange =
|
|
19346
|
+
this.listenerChange = function(ev) {
|
|
18616
19347
|
if (!this.getConfig(CONFIG_TRACK_INPUT)) {
|
|
18617
19348
|
return;
|
|
18618
19349
|
}
|
|
18619
19350
|
this.trackDomEvent(ev, MP_EV_INPUT);
|
|
18620
|
-
}.bind(this)
|
|
19351
|
+
}.bind(this);
|
|
19352
|
+
win.addEventListener(EV_CHANGE, this.listenerChange);
|
|
18621
19353
|
};
|
|
18622
19354
|
|
|
18623
19355
|
Autocapture.prototype.initPageviewTracking = function() {
|
|
18624
|
-
win.removeEventListener(EV_POPSTATE, this.listenerPopstate);
|
|
18625
|
-
win.removeEventListener(EV_HASHCHANGE, this.listenerHashchange);
|
|
18626
19356
|
win.removeEventListener(EV_MP_LOCATION_CHANGE, this.listenerLocationchange);
|
|
18627
19357
|
|
|
18628
19358
|
if (!this.pageviewTrackingConfig()) {
|
|
@@ -18639,27 +19369,7 @@ Autocapture.prototype.initPageviewTracking = function() {
|
|
|
18639
19369
|
previousTrackedUrl = _.info.currentUrl();
|
|
18640
19370
|
}
|
|
18641
19371
|
|
|
18642
|
-
this.
|
|
18643
|
-
win.dispatchEvent(new Event(EV_MP_LOCATION_CHANGE));
|
|
18644
|
-
});
|
|
18645
|
-
this.listenerHashchange = win.addEventListener(EV_HASHCHANGE, function() {
|
|
18646
|
-
win.dispatchEvent(new Event(EV_MP_LOCATION_CHANGE));
|
|
18647
|
-
});
|
|
18648
|
-
var nativePushState = win.history.pushState;
|
|
18649
|
-
if (typeof nativePushState === 'function') {
|
|
18650
|
-
win.history.pushState = function(state, unused, url) {
|
|
18651
|
-
nativePushState.call(win.history, state, unused, url);
|
|
18652
|
-
win.dispatchEvent(new Event(EV_MP_LOCATION_CHANGE));
|
|
18653
|
-
};
|
|
18654
|
-
}
|
|
18655
|
-
var nativeReplaceState = win.history.replaceState;
|
|
18656
|
-
if (typeof nativeReplaceState === 'function') {
|
|
18657
|
-
win.history.replaceState = function(state, unused, url) {
|
|
18658
|
-
nativeReplaceState.call(win.history, state, unused, url);
|
|
18659
|
-
win.dispatchEvent(new Event(EV_MP_LOCATION_CHANGE));
|
|
18660
|
-
};
|
|
18661
|
-
}
|
|
18662
|
-
this.listenerLocationchange = win.addEventListener(EV_MP_LOCATION_CHANGE, safewrap(function() {
|
|
19372
|
+
this.listenerLocationchange = safewrap(function() {
|
|
18663
19373
|
if (this.currentUrlBlocked()) {
|
|
18664
19374
|
return;
|
|
18665
19375
|
}
|
|
@@ -18686,13 +19396,14 @@ Autocapture.prototype.initPageviewTracking = function() {
|
|
|
18686
19396
|
logger$1.log('Path change: re-initializing scroll depth checkpoints');
|
|
18687
19397
|
}
|
|
18688
19398
|
}
|
|
18689
|
-
}.bind(this))
|
|
19399
|
+
}.bind(this));
|
|
19400
|
+
win.addEventListener(EV_MP_LOCATION_CHANGE, this.listenerLocationchange);
|
|
18690
19401
|
};
|
|
18691
19402
|
|
|
18692
19403
|
Autocapture.prototype.initRageClickTracking = function() {
|
|
18693
19404
|
win.removeEventListener(EV_CLICK, this.listenerRageClick);
|
|
18694
19405
|
|
|
18695
|
-
var rageClickConfig = this.
|
|
19406
|
+
var rageClickConfig = this._getClickTrackingConfig(CONFIG_TRACK_RAGE_CLICK);
|
|
18696
19407
|
if (!rageClickConfig && !this.mp.get_config('record_heatmap_data')) {
|
|
18697
19408
|
return;
|
|
18698
19409
|
}
|
|
@@ -18703,7 +19414,7 @@ Autocapture.prototype.initRageClickTracking = function() {
|
|
|
18703
19414
|
}
|
|
18704
19415
|
|
|
18705
19416
|
this.listenerRageClick = function(ev) {
|
|
18706
|
-
var currentRageClickConfig = this.
|
|
19417
|
+
var currentRageClickConfig = this._getClickTrackingConfig(CONFIG_TRACK_RAGE_CLICK);
|
|
18707
19418
|
if (!currentRageClickConfig && !this.mp.is_recording_heatmap_data()) {
|
|
18708
19419
|
return;
|
|
18709
19420
|
}
|
|
@@ -18721,6 +19432,8 @@ Autocapture.prototype.initRageClickTracking = function() {
|
|
|
18721
19432
|
|
|
18722
19433
|
Autocapture.prototype.initScrollTracking = function() {
|
|
18723
19434
|
win.removeEventListener(EV_SCROLLEND, this.listenerScroll);
|
|
19435
|
+
win.removeEventListener(EV_SCROLL, this.listenerScroll);
|
|
19436
|
+
|
|
18724
19437
|
|
|
18725
19438
|
if (!this.getConfig(CONFIG_TRACK_SCROLL)) {
|
|
18726
19439
|
return;
|
|
@@ -18728,7 +19441,7 @@ Autocapture.prototype.initScrollTracking = function() {
|
|
|
18728
19441
|
logger$1.log('Initializing scroll tracking');
|
|
18729
19442
|
this.lastScrollCheckpoint = 0;
|
|
18730
19443
|
|
|
18731
|
-
|
|
19444
|
+
var scrollTrackFunction = function() {
|
|
18732
19445
|
if (!this.getConfig(CONFIG_TRACK_SCROLL)) {
|
|
18733
19446
|
return;
|
|
18734
19447
|
}
|
|
@@ -18767,7 +19480,11 @@ Autocapture.prototype.initScrollTracking = function() {
|
|
|
18767
19480
|
if (shouldTrack) {
|
|
18768
19481
|
this.mp.track(MP_EV_SCROLL, props);
|
|
18769
19482
|
}
|
|
18770
|
-
}.bind(this)
|
|
19483
|
+
}.bind(this);
|
|
19484
|
+
|
|
19485
|
+
var scrollEndPolyfill = getPolyfillScrollEndFunction(scrollTrackFunction);
|
|
19486
|
+
this.listenerScroll = scrollEndPolyfill.listener;
|
|
19487
|
+
win.addEventListener(scrollEndPolyfill.eventType, this.listenerScroll);
|
|
18771
19488
|
};
|
|
18772
19489
|
|
|
18773
19490
|
Autocapture.prototype.initSubmitTracking = function() {
|
|
@@ -18778,18 +19495,81 @@ Autocapture.prototype.initSubmitTracking = function() {
|
|
|
18778
19495
|
}
|
|
18779
19496
|
logger$1.log('Initializing submit tracking');
|
|
18780
19497
|
|
|
18781
|
-
this.listenerSubmit =
|
|
19498
|
+
this.listenerSubmit = function(ev) {
|
|
18782
19499
|
if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
|
|
18783
19500
|
return;
|
|
18784
19501
|
}
|
|
18785
19502
|
this.trackDomEvent(ev, MP_EV_SUBMIT);
|
|
19503
|
+
}.bind(this);
|
|
19504
|
+
win.addEventListener(EV_SUBMIT, this.listenerSubmit);
|
|
19505
|
+
};
|
|
19506
|
+
|
|
19507
|
+
Autocapture.prototype.initPageLeaveTracking = function() {
|
|
19508
|
+
// Capture page_leave both when the user navigates away from the page (visibilitychange) as well
|
|
19509
|
+
// as when they navigate to a different page within the SPA (popstate/pushstate/hashchange).
|
|
19510
|
+
document$1.removeEventListener(EV_VISIBILITYCHANGE, this.listenerPageLeaveVisibilitychange);
|
|
19511
|
+
win.removeEventListener(EV_MP_LOCATION_CHANGE, this.listenerPageLeaveLocationchange);
|
|
19512
|
+
win.removeEventListener(EV_LOAD, this.listenerPageLoad);
|
|
19513
|
+
|
|
19514
|
+
if (!this.getConfig(CONFIG_TRACK_PAGE_LEAVE) && !this.mp.get_config('record_heatmap_data')) {
|
|
19515
|
+
return;
|
|
19516
|
+
}
|
|
19517
|
+
|
|
19518
|
+
logger$1.log('Initializing page visibility tracking.');
|
|
19519
|
+
this._initScrollDepthTracking();
|
|
19520
|
+
var previousTrackedUrl = _.info.currentUrl();
|
|
19521
|
+
|
|
19522
|
+
// Initialize previousScrollHeight on `load` which handles async loading
|
|
19523
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event
|
|
19524
|
+
this.listenerPageLoad = function() {
|
|
19525
|
+
this.previousScrollHeight = document$1.body.scrollHeight;
|
|
19526
|
+
}.bind(this);
|
|
19527
|
+
win.addEventListener(EV_LOAD, this.listenerPageLoad);
|
|
19528
|
+
|
|
19529
|
+
// Track page navigation events similar to how initPageviewTracking does it
|
|
19530
|
+
this.listenerPageLeaveLocationchange = safewrap(function(ev) {
|
|
19531
|
+
if (this.currentUrlBlocked()) {
|
|
19532
|
+
return;
|
|
19533
|
+
}
|
|
19534
|
+
|
|
19535
|
+
var currentUrl = _.info.currentUrl();
|
|
19536
|
+
// Track all URL changes including query string or fragment changes as separate scroll sessions
|
|
19537
|
+
var shouldTrack = currentUrl !== previousTrackedUrl;
|
|
19538
|
+
|
|
19539
|
+
if (shouldTrack) {
|
|
19540
|
+
this._trackPageLeave(ev, previousTrackedUrl, this.previousScrollHeight);
|
|
19541
|
+
previousTrackedUrl = currentUrl;
|
|
19542
|
+
// Fragment navigation should call scroll(end) and trigger listener, don't add window.scrollY here.
|
|
19543
|
+
this.maxScrollViewDepth = Math.max(document$1.documentElement.clientHeight, win.innerHeight || 0);
|
|
19544
|
+
this.previousScrollHeight = document$1.body.scrollHeight;
|
|
19545
|
+
this.hasTrackedScrollSession = false;
|
|
19546
|
+
}
|
|
18786
19547
|
}.bind(this));
|
|
19548
|
+
win.addEventListener(EV_MP_LOCATION_CHANGE, this.listenerPageLeaveLocationchange);
|
|
19549
|
+
|
|
19550
|
+
this.listenerPageLeaveVisibilitychange = function(ev) {
|
|
19551
|
+
if (document$1.hidden) {
|
|
19552
|
+
this._trackPageLeave(ev, previousTrackedUrl, this.previousScrollHeight);
|
|
19553
|
+
}
|
|
19554
|
+
}.bind(this);
|
|
19555
|
+
document$1.addEventListener(EV_VISIBILITYCHANGE, this.listenerPageLeaveVisibilitychange);
|
|
19556
|
+
};
|
|
19557
|
+
|
|
19558
|
+
Autocapture.prototype.stopDeadClickTracking = function() {
|
|
19559
|
+
if (this.listenerDeadClick) {
|
|
19560
|
+
win.removeEventListener(EV_CLICK, this.listenerDeadClick);
|
|
19561
|
+
this.listenerDeadClick = null;
|
|
19562
|
+
}
|
|
19563
|
+
|
|
19564
|
+
if (this._deadClickTracker) {
|
|
19565
|
+
this._deadClickTracker.stopTracking();
|
|
19566
|
+
this._deadClickTracker = null;
|
|
19567
|
+
}
|
|
18787
19568
|
};
|
|
18788
19569
|
|
|
18789
19570
|
// TODO integrate error_reporter from mixpanel instance
|
|
18790
19571
|
safewrapClass(Autocapture);
|
|
18791
19572
|
|
|
18792
|
-
var fetch = win['fetch'];
|
|
18793
19573
|
var logger = console_with_prefix('flags');
|
|
18794
19574
|
|
|
18795
19575
|
var FLAGS_CONFIG_KEY = 'flags';
|
|
@@ -18803,6 +19583,7 @@ CONFIG_DEFAULTS[CONFIG_CONTEXT] = {};
|
|
|
18803
19583
|
* @constructor
|
|
18804
19584
|
*/
|
|
18805
19585
|
var FeatureFlagManager = function(initOptions) {
|
|
19586
|
+
this.fetch = win['fetch'];
|
|
18806
19587
|
this.getFullApiRoute = initOptions.getFullApiRoute;
|
|
18807
19588
|
this.getMpConfig = initOptions.getConfigFunc;
|
|
18808
19589
|
this.setMpConfig = initOptions.setConfigFunc;
|
|
@@ -18811,7 +19592,7 @@ var FeatureFlagManager = function(initOptions) {
|
|
|
18811
19592
|
};
|
|
18812
19593
|
|
|
18813
19594
|
FeatureFlagManager.prototype.init = function() {
|
|
18814
|
-
if (!minApisSupported()) {
|
|
19595
|
+
if (!this.minApisSupported()) {
|
|
18815
19596
|
logger.critical('Feature Flags unavailable: missing minimum required APIs');
|
|
18816
19597
|
return;
|
|
18817
19598
|
}
|
|
@@ -18874,6 +19655,7 @@ FeatureFlagManager.prototype.fetchFlags = function() {
|
|
|
18874
19655
|
|
|
18875
19656
|
var distinctId = this.getMpProperty('distinct_id');
|
|
18876
19657
|
var deviceId = this.getMpProperty('$device_id');
|
|
19658
|
+
var traceparent = generateTraceparent();
|
|
18877
19659
|
logger.log('Fetching flags for distinct ID: ' + distinctId);
|
|
18878
19660
|
|
|
18879
19661
|
var context = _.extend({'distinct_id': distinctId, 'device_id': deviceId}, this.getConfig(CONFIG_CONTEXT));
|
|
@@ -18885,10 +19667,11 @@ FeatureFlagManager.prototype.fetchFlags = function() {
|
|
|
18885
19667
|
var url = this.getFullApiRoute() + '?' + searchParams.toString();
|
|
18886
19668
|
|
|
18887
19669
|
this._fetchInProgressStartTime = Date.now();
|
|
18888
|
-
this.fetchPromise =
|
|
19670
|
+
this.fetchPromise = this.fetch.call(win, url, {
|
|
18889
19671
|
'method': 'GET',
|
|
18890
19672
|
'headers': {
|
|
18891
|
-
'Authorization': 'Basic ' + btoa(this.getMpConfig('token') + ':')
|
|
19673
|
+
'Authorization': 'Basic ' + btoa(this.getMpConfig('token') + ':'),
|
|
19674
|
+
'traceparent': traceparent
|
|
18892
19675
|
}
|
|
18893
19676
|
}).then(function(response) {
|
|
18894
19677
|
this.markFetchComplete();
|
|
@@ -18901,10 +19684,14 @@ FeatureFlagManager.prototype.fetchFlags = function() {
|
|
|
18901
19684
|
_.each(responseFlags, function(data, key) {
|
|
18902
19685
|
flags.set(key, {
|
|
18903
19686
|
'key': data['variant_key'],
|
|
18904
|
-
'value': data['variant_value']
|
|
19687
|
+
'value': data['variant_value'],
|
|
19688
|
+
'experiment_id': data['experiment_id'],
|
|
19689
|
+
'is_experiment_active': data['is_experiment_active'],
|
|
19690
|
+
'is_qa_tester': data['is_qa_tester']
|
|
18905
19691
|
});
|
|
18906
19692
|
});
|
|
18907
19693
|
this.flags = flags;
|
|
19694
|
+
this._traceparent = traceparent;
|
|
18908
19695
|
}.bind(this)).catch(function(error) {
|
|
18909
19696
|
this.markFetchComplete();
|
|
18910
19697
|
logger.error(error);
|
|
@@ -19001,22 +19788,36 @@ FeatureFlagManager.prototype.trackFeatureCheck = function(featureName, feature)
|
|
|
19001
19788
|
return;
|
|
19002
19789
|
}
|
|
19003
19790
|
this.trackedFeatures.add(featureName);
|
|
19004
|
-
|
|
19791
|
+
|
|
19792
|
+
var trackingProperties = {
|
|
19005
19793
|
'Experiment name': featureName,
|
|
19006
19794
|
'Variant name': feature['key'],
|
|
19007
19795
|
'$experiment_type': 'feature_flag',
|
|
19008
19796
|
'Variant fetch start time': new Date(this._fetchStartTime).toISOString(),
|
|
19009
19797
|
'Variant fetch complete time': new Date(this._fetchCompleteTime).toISOString(),
|
|
19010
|
-
'Variant fetch latency (ms)': this._fetchLatency
|
|
19011
|
-
|
|
19798
|
+
'Variant fetch latency (ms)': this._fetchLatency,
|
|
19799
|
+
'Variant fetch traceparent': this._traceparent,
|
|
19800
|
+
};
|
|
19801
|
+
|
|
19802
|
+
if (feature['experiment_id'] !== 'undefined') {
|
|
19803
|
+
trackingProperties['$experiment_id'] = feature['experiment_id'];
|
|
19804
|
+
}
|
|
19805
|
+
if (feature['is_experiment_active'] !== 'undefined') {
|
|
19806
|
+
trackingProperties['$is_experiment_active'] = feature['is_experiment_active'];
|
|
19807
|
+
}
|
|
19808
|
+
if (feature['is_qa_tester'] !== 'undefined') {
|
|
19809
|
+
trackingProperties['$is_qa_tester'] = feature['is_qa_tester'];
|
|
19810
|
+
}
|
|
19811
|
+
|
|
19812
|
+
this.track('$experiment_started', trackingProperties);
|
|
19012
19813
|
};
|
|
19013
19814
|
|
|
19014
|
-
function
|
|
19015
|
-
return !!fetch &&
|
|
19815
|
+
FeatureFlagManager.prototype.minApisSupported = function() {
|
|
19816
|
+
return !!this.fetch &&
|
|
19016
19817
|
typeof Promise !== 'undefined' &&
|
|
19017
19818
|
typeof Map !== 'undefined' &&
|
|
19018
19819
|
typeof Set !== 'undefined';
|
|
19019
|
-
}
|
|
19820
|
+
};
|
|
19020
19821
|
|
|
19021
19822
|
safewrapClass(FeatureFlagManager);
|
|
19022
19823
|
|