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