mixpanel-browser 2.59.0 → 2.60.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 +5 -1
- package/README.md +1 -1
- package/dist/mixpanel-core.cjs.js +215 -49
- package/dist/mixpanel-recorder.js +1 -1
- package/dist/mixpanel-recorder.min.js +1 -1
- package/dist/mixpanel-recorder.min.js.map +1 -1
- package/dist/mixpanel-with-async-recorder.cjs.js +215 -49
- package/dist/mixpanel.amd.js +215 -49
- package/dist/mixpanel.cjs.js +215 -49
- package/dist/mixpanel.globals.js +215 -49
- package/dist/mixpanel.min.js +138 -134
- package/dist/mixpanel.module.js +215 -49
- package/dist/mixpanel.umd.js +215 -49
- package/package.json +1 -1
- package/src/autocapture/index.js +80 -9
- package/src/autocapture/utils.js +129 -38
- package/src/config.js +1 -1
- package/src/mixpanel-persistence.js +6 -2
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var Config = {
|
|
4
4
|
DEBUG: false,
|
|
5
|
-
LIB_VERSION: '2.
|
|
5
|
+
LIB_VERSION: '2.60.0'
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
// since es6 imports are static and we run unit tests from the console, window won't be defined when importing this file
|
|
@@ -2196,7 +2196,7 @@ function getPreviousElementSibling(el) {
|
|
|
2196
2196
|
}
|
|
2197
2197
|
}
|
|
2198
2198
|
|
|
2199
|
-
function getPropertiesFromElement(el) {
|
|
2199
|
+
function getPropertiesFromElement(el, ev, blockAttrsSet, extraAttrs, allowElementCallback, allowSelectors) {
|
|
2200
2200
|
var props = {
|
|
2201
2201
|
'$classes': getClassName(el).split(' '),
|
|
2202
2202
|
'$tag_name': el.tagName.toLowerCase()
|
|
@@ -2206,9 +2206,9 @@ function getPropertiesFromElement(el) {
|
|
|
2206
2206
|
props['$id'] = elId;
|
|
2207
2207
|
}
|
|
2208
2208
|
|
|
2209
|
-
if (
|
|
2210
|
-
_.each(TRACKED_ATTRS, function(attr) {
|
|
2211
|
-
if (el.hasAttribute(attr)) {
|
|
2209
|
+
if (shouldTrackElementDetails(el, ev, allowElementCallback, allowSelectors)) {
|
|
2210
|
+
_.each(TRACKED_ATTRS.concat(extraAttrs), function(attr) {
|
|
2211
|
+
if (el.hasAttribute(attr) && !blockAttrsSet[attr]) {
|
|
2212
2212
|
var attrVal = el.getAttribute(attr);
|
|
2213
2213
|
if (shouldTrackValue(attrVal)) {
|
|
2214
2214
|
props['$attr-' + attr] = attrVal;
|
|
@@ -2232,8 +2232,21 @@ function getPropertiesFromElement(el) {
|
|
|
2232
2232
|
return props;
|
|
2233
2233
|
}
|
|
2234
2234
|
|
|
2235
|
-
function getPropsForDOMEvent(ev,
|
|
2236
|
-
|
|
2235
|
+
function getPropsForDOMEvent(ev, config) {
|
|
2236
|
+
var allowElementCallback = config.allowElementCallback;
|
|
2237
|
+
var allowSelectors = config.allowSelectors || [];
|
|
2238
|
+
var blockAttrs = config.blockAttrs || [];
|
|
2239
|
+
var blockElementCallback = config.blockElementCallback;
|
|
2240
|
+
var blockSelectors = config.blockSelectors || [];
|
|
2241
|
+
var captureTextContent = config.captureTextContent || false;
|
|
2242
|
+
var captureExtraAttrs = config.captureExtraAttrs || [];
|
|
2243
|
+
|
|
2244
|
+
// convert array to set every time, as the config may have changed
|
|
2245
|
+
var blockAttrsSet = {};
|
|
2246
|
+
_.each(blockAttrs, function(attr) {
|
|
2247
|
+
blockAttrsSet[attr] = true;
|
|
2248
|
+
});
|
|
2249
|
+
|
|
2237
2250
|
var props = null;
|
|
2238
2251
|
|
|
2239
2252
|
var target = typeof ev.target === 'undefined' ? ev.srcElement : ev.target;
|
|
@@ -2241,7 +2254,11 @@ function getPropsForDOMEvent(ev, blockSelectors, captureTextContent) {
|
|
|
2241
2254
|
target = target.parentNode;
|
|
2242
2255
|
}
|
|
2243
2256
|
|
|
2244
|
-
if (
|
|
2257
|
+
if (
|
|
2258
|
+
shouldTrackDomEvent(target, ev) &&
|
|
2259
|
+
isElementAllowed(target, ev, allowElementCallback, allowSelectors) &&
|
|
2260
|
+
!isElementBlocked(target, ev, blockElementCallback, blockSelectors)
|
|
2261
|
+
) {
|
|
2245
2262
|
var targetElementList = [target];
|
|
2246
2263
|
var curEl = target;
|
|
2247
2264
|
while (curEl.parentNode && !isTag(curEl, 'body')) {
|
|
@@ -2252,37 +2269,20 @@ function getPropsForDOMEvent(ev, blockSelectors, captureTextContent) {
|
|
|
2252
2269
|
var elementsJson = [];
|
|
2253
2270
|
var href, explicitNoTrack = false;
|
|
2254
2271
|
_.each(targetElementList, function(el) {
|
|
2255
|
-
var
|
|
2272
|
+
var shouldTrackDetails = shouldTrackElementDetails(el, ev, allowElementCallback, allowSelectors);
|
|
2256
2273
|
|
|
2257
2274
|
// if the element or a parent element is an anchor tag
|
|
2258
2275
|
// include the href as a property
|
|
2259
|
-
if (el.tagName.toLowerCase() === 'a') {
|
|
2276
|
+
if (!blockAttrsSet['href'] && el.tagName.toLowerCase() === 'a') {
|
|
2260
2277
|
href = el.getAttribute('href');
|
|
2261
|
-
href =
|
|
2278
|
+
href = shouldTrackDetails && shouldTrackValue(href) && href;
|
|
2262
2279
|
}
|
|
2263
2280
|
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
_.each(OPT_OUT_CLASSES, function(cls) {
|
|
2267
|
-
if (classes[cls]) {
|
|
2268
|
-
explicitNoTrack = true;
|
|
2269
|
-
}
|
|
2270
|
-
});
|
|
2271
|
-
|
|
2272
|
-
if (!explicitNoTrack) {
|
|
2273
|
-
// programmatically prevent tracking of elements that match CSS selectors
|
|
2274
|
-
_.each(blockSelectors, function(sel) {
|
|
2275
|
-
try {
|
|
2276
|
-
if (el['matches'](sel)) {
|
|
2277
|
-
explicitNoTrack = true;
|
|
2278
|
-
}
|
|
2279
|
-
} catch (err) {
|
|
2280
|
-
logger$3.critical('Error while checking selector: ' + sel, err);
|
|
2281
|
-
}
|
|
2282
|
-
});
|
|
2281
|
+
if (isElementBlocked(el, ev, blockElementCallback, blockSelectors)) {
|
|
2282
|
+
explicitNoTrack = true;
|
|
2283
2283
|
}
|
|
2284
2284
|
|
|
2285
|
-
elementsJson.push(getPropertiesFromElement(el));
|
|
2285
|
+
elementsJson.push(getPropertiesFromElement(el, ev, blockAttrsSet, captureExtraAttrs, allowElementCallback, allowSelectors));
|
|
2286
2286
|
}, this);
|
|
2287
2287
|
|
|
2288
2288
|
if (!explicitNoTrack) {
|
|
@@ -2296,9 +2296,17 @@ function getPropsForDOMEvent(ev, blockSelectors, captureTextContent) {
|
|
|
2296
2296
|
'$viewportHeight': Math.max(docElement['clientHeight'], win['innerHeight'] || 0),
|
|
2297
2297
|
'$viewportWidth': Math.max(docElement['clientWidth'], win['innerWidth'] || 0)
|
|
2298
2298
|
};
|
|
2299
|
+
_.each(captureExtraAttrs, function(attr) {
|
|
2300
|
+
if (!blockAttrsSet[attr] && target.hasAttribute(attr)) {
|
|
2301
|
+
var attrVal = target.getAttribute(attr);
|
|
2302
|
+
if (shouldTrackValue(attrVal)) {
|
|
2303
|
+
props['$el_attr__' + attr] = attrVal;
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
});
|
|
2299
2307
|
|
|
2300
2308
|
if (captureTextContent) {
|
|
2301
|
-
elementText = getSafeText(target);
|
|
2309
|
+
elementText = getSafeText(target, ev, allowElementCallback, allowSelectors);
|
|
2302
2310
|
if (elementText && elementText.length) {
|
|
2303
2311
|
props['$el_text'] = elementText;
|
|
2304
2312
|
}
|
|
@@ -2314,14 +2322,22 @@ function getPropsForDOMEvent(ev, blockSelectors, captureTextContent) {
|
|
|
2314
2322
|
}
|
|
2315
2323
|
// prioritize text content from "real" click target if different from original target
|
|
2316
2324
|
if (captureTextContent) {
|
|
2317
|
-
var elementText = getSafeText(target);
|
|
2325
|
+
var elementText = getSafeText(target, ev, allowElementCallback, allowSelectors);
|
|
2318
2326
|
if (elementText && elementText.length) {
|
|
2319
2327
|
props['$el_text'] = elementText;
|
|
2320
2328
|
}
|
|
2321
2329
|
}
|
|
2322
2330
|
|
|
2323
2331
|
if (target) {
|
|
2324
|
-
|
|
2332
|
+
// target may have been recalculated; check allowlists and blocklists again
|
|
2333
|
+
if (
|
|
2334
|
+
!isElementAllowed(target, ev, allowElementCallback, allowSelectors) ||
|
|
2335
|
+
isElementBlocked(target, ev, blockElementCallback, blockSelectors)
|
|
2336
|
+
) {
|
|
2337
|
+
return null;
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
var targetProps = getPropertiesFromElement(target, ev, blockAttrsSet, captureExtraAttrs, allowElementCallback, allowSelectors);
|
|
2325
2341
|
props['$target'] = targetProps;
|
|
2326
2342
|
// pull up more props onto main event props
|
|
2327
2343
|
props['$el_classes'] = targetProps['$classes'];
|
|
@@ -2337,19 +2353,20 @@ function getPropsForDOMEvent(ev, blockSelectors, captureTextContent) {
|
|
|
2337
2353
|
}
|
|
2338
2354
|
|
|
2339
2355
|
|
|
2340
|
-
|
|
2356
|
+
/**
|
|
2341
2357
|
* Get the direct text content of an element, protecting against sensitive data collection.
|
|
2342
2358
|
* Concats textContent of each of the element's text node children; this avoids potential
|
|
2343
2359
|
* collection of sensitive data that could happen if we used element.textContent and the
|
|
2344
2360
|
* element had sensitive child elements, since element.textContent includes child content.
|
|
2345
2361
|
* Scrubs values that look like they could be sensitive (i.e. cc or ssn number).
|
|
2346
2362
|
* @param {Element} el - element to get the text of
|
|
2363
|
+
* @param {Array<string>} allowSelectors - CSS selectors for elements that should be included
|
|
2347
2364
|
* @returns {string} the element's direct text content
|
|
2348
2365
|
*/
|
|
2349
|
-
function getSafeText(el) {
|
|
2366
|
+
function getSafeText(el, ev, allowElementCallback, allowSelectors) {
|
|
2350
2367
|
var elText = '';
|
|
2351
2368
|
|
|
2352
|
-
if (
|
|
2369
|
+
if (shouldTrackElementDetails(el, ev, allowElementCallback, allowSelectors) && el.childNodes && el.childNodes.length) {
|
|
2353
2370
|
_.each(el.childNodes, function(child) {
|
|
2354
2371
|
if (isTextNode(child) && child.textContent) {
|
|
2355
2372
|
elText += _.trim(child.textContent)
|
|
@@ -2388,6 +2405,75 @@ function guessRealClickTarget(ev) {
|
|
|
2388
2405
|
return target;
|
|
2389
2406
|
}
|
|
2390
2407
|
|
|
2408
|
+
function isElementAllowed(el, ev, allowElementCallback, allowSelectors) {
|
|
2409
|
+
if (allowElementCallback) {
|
|
2410
|
+
try {
|
|
2411
|
+
if (!allowElementCallback(el, ev)) {
|
|
2412
|
+
return false;
|
|
2413
|
+
}
|
|
2414
|
+
} catch (err) {
|
|
2415
|
+
logger$3.critical('Error while checking element in allowElementCallback', err);
|
|
2416
|
+
return false;
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
|
|
2420
|
+
if (!allowSelectors.length) {
|
|
2421
|
+
// no allowlist; all elements are fair game
|
|
2422
|
+
return true;
|
|
2423
|
+
}
|
|
2424
|
+
|
|
2425
|
+
for (var i = 0; i < allowSelectors.length; i++) {
|
|
2426
|
+
var sel = allowSelectors[i];
|
|
2427
|
+
try {
|
|
2428
|
+
if (el['matches'](sel)) {
|
|
2429
|
+
return true;
|
|
2430
|
+
}
|
|
2431
|
+
} catch (err) {
|
|
2432
|
+
logger$3.critical('Error while checking selector: ' + sel, err);
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
return false;
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
function isElementBlocked(el, ev, blockElementCallback, blockSelectors) {
|
|
2439
|
+
var i;
|
|
2440
|
+
|
|
2441
|
+
if (blockElementCallback) {
|
|
2442
|
+
try {
|
|
2443
|
+
if (blockElementCallback(el, ev)) {
|
|
2444
|
+
return true;
|
|
2445
|
+
}
|
|
2446
|
+
} catch (err) {
|
|
2447
|
+
logger$3.critical('Error while checking element in blockElementCallback', err);
|
|
2448
|
+
return true;
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
|
|
2452
|
+
if (blockSelectors && blockSelectors.length) {
|
|
2453
|
+
// programmatically prevent tracking of elements that match CSS selectors
|
|
2454
|
+
for (i = 0; i < blockSelectors.length; i++) {
|
|
2455
|
+
var sel = blockSelectors[i];
|
|
2456
|
+
try {
|
|
2457
|
+
if (el['matches'](sel)) {
|
|
2458
|
+
return true;
|
|
2459
|
+
}
|
|
2460
|
+
} catch (err) {
|
|
2461
|
+
logger$3.critical('Error while checking selector: ' + sel, err);
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
}
|
|
2465
|
+
|
|
2466
|
+
// allow users to programmatically prevent tracking of elements by adding default classes such as 'mp-no-track'
|
|
2467
|
+
var classes = getClasses(el);
|
|
2468
|
+
for (i = 0; i < OPT_OUT_CLASSES.length; i++) {
|
|
2469
|
+
if (classes[OPT_OUT_CLASSES[i]]) {
|
|
2470
|
+
return true;
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
|
|
2474
|
+
return false;
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2391
2477
|
/*
|
|
2392
2478
|
* Check whether a DOM node has nodeType Node.ELEMENT_NODE
|
|
2393
2479
|
* @param {Node} node - node to check
|
|
@@ -2462,11 +2548,16 @@ function shouldTrackDomEvent(el, ev) {
|
|
|
2462
2548
|
* Check whether a DOM element should be "tracked" or if it may contain sensitive data
|
|
2463
2549
|
* using a variety of heuristics.
|
|
2464
2550
|
* @param {Element} el - element to check
|
|
2551
|
+
* @param {Array<string>} allowSelectors - CSS selectors for elements that should be included
|
|
2465
2552
|
* @returns {boolean} whether the element should be tracked
|
|
2466
2553
|
*/
|
|
2467
|
-
function
|
|
2554
|
+
function shouldTrackElementDetails(el, ev, allowElementCallback, allowSelectors) {
|
|
2468
2555
|
var i;
|
|
2469
2556
|
|
|
2557
|
+
if (!isElementAllowed(el, ev, allowElementCallback, allowSelectors)) {
|
|
2558
|
+
return false;
|
|
2559
|
+
}
|
|
2560
|
+
|
|
2470
2561
|
for (var curEl = el; curEl.parentNode && !isTag(curEl, 'body'); curEl = curEl.parentNode) {
|
|
2471
2562
|
var classes = getClasses(curEl);
|
|
2472
2563
|
for (i = 0; i < SENSITIVE_DATA_CLASSES.length; i++) {
|
|
@@ -2556,9 +2647,17 @@ var PAGEVIEW_OPTION_FULL_URL = 'full-url';
|
|
|
2556
2647
|
var PAGEVIEW_OPTION_URL_WITH_PATH_AND_QUERY_STRING = 'url-with-path-and-query-string';
|
|
2557
2648
|
var PAGEVIEW_OPTION_URL_WITH_PATH = 'url-with-path';
|
|
2558
2649
|
|
|
2650
|
+
var CONFIG_ALLOW_ELEMENT_CALLBACK = 'allow_element_callback';
|
|
2651
|
+
var CONFIG_ALLOW_SELECTORS = 'allow_selectors';
|
|
2652
|
+
var CONFIG_ALLOW_URL_REGEXES = 'allow_url_regexes';
|
|
2653
|
+
var CONFIG_BLOCK_ATTRS = 'block_attrs';
|
|
2654
|
+
var CONFIG_BLOCK_ELEMENT_CALLBACK = 'block_element_callback';
|
|
2559
2655
|
var CONFIG_BLOCK_SELECTORS = 'block_selectors';
|
|
2560
2656
|
var CONFIG_BLOCK_URL_REGEXES = 'block_url_regexes';
|
|
2657
|
+
var CONFIG_CAPTURE_EXTRA_ATTRS = 'capture_extra_attrs';
|
|
2561
2658
|
var CONFIG_CAPTURE_TEXT_CONTENT = 'capture_text_content';
|
|
2659
|
+
var CONFIG_SCROLL_CAPTURE_ALL = 'scroll_capture_all';
|
|
2660
|
+
var CONFIG_SCROLL_CHECKPOINTS = 'scroll_depth_percent_checkpoints';
|
|
2562
2661
|
var CONFIG_TRACK_CLICK = 'click';
|
|
2563
2662
|
var CONFIG_TRACK_INPUT = 'input';
|
|
2564
2663
|
var CONFIG_TRACK_PAGEVIEW = 'pageview';
|
|
@@ -2566,7 +2665,16 @@ var CONFIG_TRACK_SCROLL = 'scroll';
|
|
|
2566
2665
|
var CONFIG_TRACK_SUBMIT = 'submit';
|
|
2567
2666
|
|
|
2568
2667
|
var CONFIG_DEFAULTS = {};
|
|
2668
|
+
CONFIG_DEFAULTS[CONFIG_ALLOW_SELECTORS] = [];
|
|
2669
|
+
CONFIG_DEFAULTS[CONFIG_ALLOW_URL_REGEXES] = [];
|
|
2670
|
+
CONFIG_DEFAULTS[CONFIG_BLOCK_ATTRS] = [];
|
|
2671
|
+
CONFIG_DEFAULTS[CONFIG_BLOCK_ELEMENT_CALLBACK] = null;
|
|
2672
|
+
CONFIG_DEFAULTS[CONFIG_BLOCK_SELECTORS] = [];
|
|
2673
|
+
CONFIG_DEFAULTS[CONFIG_BLOCK_URL_REGEXES] = [];
|
|
2674
|
+
CONFIG_DEFAULTS[CONFIG_CAPTURE_EXTRA_ATTRS] = [];
|
|
2569
2675
|
CONFIG_DEFAULTS[CONFIG_CAPTURE_TEXT_CONTENT] = false;
|
|
2676
|
+
CONFIG_DEFAULTS[CONFIG_SCROLL_CAPTURE_ALL] = false;
|
|
2677
|
+
CONFIG_DEFAULTS[CONFIG_SCROLL_CHECKPOINTS] = [25, 50, 75, 100];
|
|
2570
2678
|
CONFIG_DEFAULTS[CONFIG_TRACK_CLICK] = true;
|
|
2571
2679
|
CONFIG_DEFAULTS[CONFIG_TRACK_INPUT] = true;
|
|
2572
2680
|
CONFIG_DEFAULTS[CONFIG_TRACK_PAGEVIEW] = PAGEVIEW_OPTION_FULL_URL;
|
|
@@ -2621,13 +2729,37 @@ Autocapture.prototype.getConfig = function(key) {
|
|
|
2621
2729
|
};
|
|
2622
2730
|
|
|
2623
2731
|
Autocapture.prototype.currentUrlBlocked = function() {
|
|
2732
|
+
var i;
|
|
2733
|
+
var currentUrl = _.info.currentUrl();
|
|
2734
|
+
|
|
2735
|
+
var allowUrlRegexes = this.getConfig(CONFIG_ALLOW_URL_REGEXES) || [];
|
|
2736
|
+
if (allowUrlRegexes.length) {
|
|
2737
|
+
// we're using an allowlist, only track if current URL matches
|
|
2738
|
+
var allowed = false;
|
|
2739
|
+
for (i = 0; i < allowUrlRegexes.length; i++) {
|
|
2740
|
+
var allowRegex = allowUrlRegexes[i];
|
|
2741
|
+
try {
|
|
2742
|
+
if (currentUrl.match(allowRegex)) {
|
|
2743
|
+
allowed = true;
|
|
2744
|
+
break;
|
|
2745
|
+
}
|
|
2746
|
+
} catch (err) {
|
|
2747
|
+
logger$3.critical('Error while checking block URL regex: ' + allowRegex, err);
|
|
2748
|
+
return true;
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
if (!allowed) {
|
|
2752
|
+
// wasn't allowed by any regex
|
|
2753
|
+
return true;
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
|
|
2624
2757
|
var blockUrlRegexes = this.getConfig(CONFIG_BLOCK_URL_REGEXES) || [];
|
|
2625
2758
|
if (!blockUrlRegexes || !blockUrlRegexes.length) {
|
|
2626
2759
|
return false;
|
|
2627
2760
|
}
|
|
2628
2761
|
|
|
2629
|
-
|
|
2630
|
-
for (var i = 0; i < blockUrlRegexes.length; i++) {
|
|
2762
|
+
for (i = 0; i < blockUrlRegexes.length; i++) {
|
|
2631
2763
|
try {
|
|
2632
2764
|
if (currentUrl.match(blockUrlRegexes[i])) {
|
|
2633
2765
|
return true;
|
|
@@ -2655,11 +2787,15 @@ Autocapture.prototype.trackDomEvent = function(ev, mpEventName) {
|
|
|
2655
2787
|
return;
|
|
2656
2788
|
}
|
|
2657
2789
|
|
|
2658
|
-
var props = getPropsForDOMEvent(
|
|
2659
|
-
|
|
2660
|
-
this.getConfig(
|
|
2661
|
-
this.getConfig(
|
|
2662
|
-
|
|
2790
|
+
var props = getPropsForDOMEvent(ev, {
|
|
2791
|
+
allowElementCallback: this.getConfig(CONFIG_ALLOW_ELEMENT_CALLBACK),
|
|
2792
|
+
allowSelectors: this.getConfig(CONFIG_ALLOW_SELECTORS),
|
|
2793
|
+
blockAttrs: this.getConfig(CONFIG_BLOCK_ATTRS),
|
|
2794
|
+
blockElementCallback: this.getConfig(CONFIG_BLOCK_ELEMENT_CALLBACK),
|
|
2795
|
+
blockSelectors: this.getConfig(CONFIG_BLOCK_SELECTORS),
|
|
2796
|
+
captureExtraAttrs: this.getConfig(CONFIG_CAPTURE_EXTRA_ATTRS),
|
|
2797
|
+
captureTextContent: this.getConfig(CONFIG_CAPTURE_TEXT_CONTENT)
|
|
2798
|
+
});
|
|
2663
2799
|
if (props) {
|
|
2664
2800
|
_.extend(props, DEFAULT_PROPS);
|
|
2665
2801
|
this.mp.track(mpEventName, props);
|
|
@@ -2744,13 +2880,14 @@ Autocapture.prototype.initPageviewTracking = function() {
|
|
|
2744
2880
|
|
|
2745
2881
|
var currentUrl = _.info.currentUrl();
|
|
2746
2882
|
var shouldTrack = false;
|
|
2883
|
+
var didPathChange = currentUrl.split('#')[0].split('?')[0] !== previousTrackedUrl.split('#')[0].split('?')[0];
|
|
2747
2884
|
var trackPageviewOption = this.pageviewTrackingConfig();
|
|
2748
2885
|
if (trackPageviewOption === PAGEVIEW_OPTION_FULL_URL) {
|
|
2749
2886
|
shouldTrack = currentUrl !== previousTrackedUrl;
|
|
2750
2887
|
} else if (trackPageviewOption === PAGEVIEW_OPTION_URL_WITH_PATH_AND_QUERY_STRING) {
|
|
2751
2888
|
shouldTrack = currentUrl.split('#')[0] !== previousTrackedUrl.split('#')[0];
|
|
2752
2889
|
} else if (trackPageviewOption === PAGEVIEW_OPTION_URL_WITH_PATH) {
|
|
2753
|
-
shouldTrack =
|
|
2890
|
+
shouldTrack = didPathChange;
|
|
2754
2891
|
}
|
|
2755
2892
|
|
|
2756
2893
|
if (shouldTrack) {
|
|
@@ -2758,6 +2895,10 @@ Autocapture.prototype.initPageviewTracking = function() {
|
|
|
2758
2895
|
if (tracked) {
|
|
2759
2896
|
previousTrackedUrl = currentUrl;
|
|
2760
2897
|
}
|
|
2898
|
+
if (didPathChange) {
|
|
2899
|
+
this.lastScrollCheckpoint = 0;
|
|
2900
|
+
logger$3.log('Path change: re-initializing scroll depth checkpoints');
|
|
2901
|
+
}
|
|
2761
2902
|
}
|
|
2762
2903
|
}.bind(this)));
|
|
2763
2904
|
};
|
|
@@ -2769,6 +2910,7 @@ Autocapture.prototype.initScrollTracking = function() {
|
|
|
2769
2910
|
return;
|
|
2770
2911
|
}
|
|
2771
2912
|
logger$3.log('Initializing scroll tracking');
|
|
2913
|
+
this.lastScrollCheckpoint = 0;
|
|
2772
2914
|
|
|
2773
2915
|
this.listenerScroll = win.addEventListener(EV_SCROLLEND, safewrap(function() {
|
|
2774
2916
|
if (!this.getConfig(CONFIG_TRACK_SCROLL)) {
|
|
@@ -2778,6 +2920,11 @@ Autocapture.prototype.initScrollTracking = function() {
|
|
|
2778
2920
|
return;
|
|
2779
2921
|
}
|
|
2780
2922
|
|
|
2923
|
+
var shouldTrack = this.getConfig(CONFIG_SCROLL_CAPTURE_ALL);
|
|
2924
|
+
var scrollCheckpoints = (this.getConfig(CONFIG_SCROLL_CHECKPOINTS) || [])
|
|
2925
|
+
.slice()
|
|
2926
|
+
.sort(function(a, b) { return a - b; });
|
|
2927
|
+
|
|
2781
2928
|
var scrollTop = win.scrollY;
|
|
2782
2929
|
var props = _.extend({'$scroll_top': scrollTop}, DEFAULT_PROPS);
|
|
2783
2930
|
try {
|
|
@@ -2785,10 +2932,25 @@ Autocapture.prototype.initScrollTracking = function() {
|
|
|
2785
2932
|
var scrollPercentage = Math.round((scrollTop / (scrollHeight - win.innerHeight)) * 100);
|
|
2786
2933
|
props['$scroll_height'] = scrollHeight;
|
|
2787
2934
|
props['$scroll_percentage'] = scrollPercentage;
|
|
2935
|
+
if (scrollPercentage > this.lastScrollCheckpoint) {
|
|
2936
|
+
for (var i = 0; i < scrollCheckpoints.length; i++) {
|
|
2937
|
+
var checkpoint = scrollCheckpoints[i];
|
|
2938
|
+
if (
|
|
2939
|
+
scrollPercentage >= checkpoint &&
|
|
2940
|
+
this.lastScrollCheckpoint < checkpoint
|
|
2941
|
+
) {
|
|
2942
|
+
props['$scroll_checkpoint'] = checkpoint;
|
|
2943
|
+
this.lastScrollCheckpoint = checkpoint;
|
|
2944
|
+
shouldTrack = true;
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2788
2948
|
} catch (err) {
|
|
2789
2949
|
logger$3.critical('Error while calculating scroll percentage', err);
|
|
2790
2950
|
}
|
|
2791
|
-
|
|
2951
|
+
if (shouldTrack) {
|
|
2952
|
+
this.mp.track(MP_EV_SCROLL, props);
|
|
2953
|
+
}
|
|
2792
2954
|
}.bind(this)));
|
|
2793
2955
|
};
|
|
2794
2956
|
|
|
@@ -5240,8 +5402,12 @@ MixpanelPersistence.prototype._add_to_people_queue = function(queue, data) {
|
|
|
5240
5402
|
if (!(k in union_q)) {
|
|
5241
5403
|
union_q[k] = [];
|
|
5242
5404
|
}
|
|
5243
|
-
//
|
|
5244
|
-
|
|
5405
|
+
// Prevent duplicate values
|
|
5406
|
+
_.each(v, function(item) {
|
|
5407
|
+
if (!_.include(union_q[k], item)) {
|
|
5408
|
+
union_q[k].push(item);
|
|
5409
|
+
}
|
|
5410
|
+
});
|
|
5245
5411
|
}
|
|
5246
5412
|
});
|
|
5247
5413
|
this._pop_from_people_queue(UNSET_ACTION, q_data);
|