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