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