mixpanel-browser 2.66.0 → 2.68.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/.github/dependabot.yml +7 -0
- package/CHANGELOG.md +12 -0
- package/dist/mixpanel-core.cjs.js +162 -42
- package/dist/mixpanel-recorder.js +36 -12
- 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 +162 -42
- package/dist/mixpanel-with-recorder.js +197 -53
- package/dist/mixpanel-with-recorder.min.js +1 -1
- package/dist/mixpanel.amd.js +197 -53
- package/dist/mixpanel.cjs.js +197 -53
- package/dist/mixpanel.globals.js +162 -42
- package/dist/mixpanel.min.js +150 -148
- package/dist/mixpanel.module.js +197 -53
- package/dist/mixpanel.umd.js +197 -53
- package/package.json +1 -1
- package/src/autocapture/index.js +59 -1
- package/src/autocapture/rageclick.js +38 -0
- package/src/config.js +1 -1
- package/src/flags/index.js +51 -8
- package/src/index.d.ts +18 -0
- package/src/mixpanel-core.js +20 -32
- package/src/recorder/session-recording.js +35 -11
package/dist/mixpanel.umd.js
CHANGED
|
@@ -13948,7 +13948,7 @@
|
|
|
13948
13948
|
|
|
13949
13949
|
var Config = {
|
|
13950
13950
|
DEBUG: false,
|
|
13951
|
-
LIB_VERSION: '2.
|
|
13951
|
+
LIB_VERSION: '2.68.0'
|
|
13952
13952
|
};
|
|
13953
13953
|
|
|
13954
13954
|
/* eslint camelcase: "off", eqeqeq: "off" */
|
|
@@ -17066,6 +17066,13 @@
|
|
|
17066
17066
|
* @property {string} replayStartUrl
|
|
17067
17067
|
*/
|
|
17068
17068
|
|
|
17069
|
+
/**
|
|
17070
|
+
* @typedef {Object} UserIdInfo
|
|
17071
|
+
* @property {string} distinct_id
|
|
17072
|
+
* @property {string} user_id
|
|
17073
|
+
* @property {string} device_id
|
|
17074
|
+
*/
|
|
17075
|
+
|
|
17069
17076
|
|
|
17070
17077
|
/**
|
|
17071
17078
|
* This class encapsulates a single session recording and its lifecycle.
|
|
@@ -17121,6 +17128,30 @@
|
|
|
17121
17128
|
});
|
|
17122
17129
|
};
|
|
17123
17130
|
|
|
17131
|
+
/**
|
|
17132
|
+
* @returns {UserIdInfo}
|
|
17133
|
+
*/
|
|
17134
|
+
SessionRecording.prototype.getUserIdInfo = function () {
|
|
17135
|
+
if (this.finalFlushUserIdInfo) {
|
|
17136
|
+
return this.finalFlushUserIdInfo;
|
|
17137
|
+
}
|
|
17138
|
+
|
|
17139
|
+
var userIdInfo = {
|
|
17140
|
+
'distinct_id': String(this._mixpanel.get_distinct_id()),
|
|
17141
|
+
};
|
|
17142
|
+
|
|
17143
|
+
// send ID management props if they exist
|
|
17144
|
+
var deviceId = this._mixpanel.get_property('$device_id');
|
|
17145
|
+
if (deviceId) {
|
|
17146
|
+
userIdInfo['$device_id'] = deviceId;
|
|
17147
|
+
}
|
|
17148
|
+
var userId = this._mixpanel.get_property('$user_id');
|
|
17149
|
+
if (userId) {
|
|
17150
|
+
userIdInfo['$user_id'] = userId;
|
|
17151
|
+
}
|
|
17152
|
+
return userIdInfo;
|
|
17153
|
+
};
|
|
17154
|
+
|
|
17124
17155
|
SessionRecording.prototype.unloadPersistedData = function () {
|
|
17125
17156
|
this.batcher.stop();
|
|
17126
17157
|
return this.batcher.flush()
|
|
@@ -17245,6 +17276,9 @@
|
|
|
17245
17276
|
};
|
|
17246
17277
|
|
|
17247
17278
|
SessionRecording.prototype.stopRecording = function (skipFlush) {
|
|
17279
|
+
// store the user ID info in case this is getting called in mixpanel.reset()
|
|
17280
|
+
this.finalFlushUserIdInfo = this.getUserIdInfo();
|
|
17281
|
+
|
|
17248
17282
|
if (!this.isRrwebStopped()) {
|
|
17249
17283
|
try {
|
|
17250
17284
|
this._stopRecording();
|
|
@@ -17410,7 +17444,6 @@
|
|
|
17410
17444
|
'$current_url': this.batchStartUrl,
|
|
17411
17445
|
'$lib_version': Config.LIB_VERSION,
|
|
17412
17446
|
'batch_start_time': batchStartTime / 1000,
|
|
17413
|
-
'distinct_id': String(this._mixpanel.get_distinct_id()),
|
|
17414
17447
|
'mp_lib': 'web',
|
|
17415
17448
|
'replay_id': replayId,
|
|
17416
17449
|
'replay_length_ms': replayLengthMs,
|
|
@@ -17419,16 +17452,7 @@
|
|
|
17419
17452
|
'seq': this.seqNo
|
|
17420
17453
|
};
|
|
17421
17454
|
var eventsJson = JSON.stringify(data);
|
|
17422
|
-
|
|
17423
|
-
// send ID management props if they exist
|
|
17424
|
-
var deviceId = this._mixpanel.get_property('$device_id');
|
|
17425
|
-
if (deviceId) {
|
|
17426
|
-
reqParams['$device_id'] = deviceId;
|
|
17427
|
-
}
|
|
17428
|
-
var userId = this._mixpanel.get_property('$user_id');
|
|
17429
|
-
if (userId) {
|
|
17430
|
-
reqParams['$user_id'] = userId;
|
|
17431
|
-
}
|
|
17455
|
+
Object.assign(reqParams, this.getUserIdInfo());
|
|
17432
17456
|
|
|
17433
17457
|
if (CompressionStream) {
|
|
17434
17458
|
var jsonStream = new Blob([eventsJson], {type: 'application/json'}).stream();
|
|
@@ -18209,6 +18233,38 @@
|
|
|
18209
18233
|
return true;
|
|
18210
18234
|
}
|
|
18211
18235
|
|
|
18236
|
+
/** @const */ var DEFAULT_RAGE_CLICK_THRESHOLD_PX = 30;
|
|
18237
|
+
/** @const */ var DEFAULT_RAGE_CLICK_TIMEOUT_MS = 1000;
|
|
18238
|
+
/** @const */ var DEFAULT_RAGE_CLICK_CLICK_COUNT = 4;
|
|
18239
|
+
|
|
18240
|
+
function RageClickTracker() {
|
|
18241
|
+
this.clicks = [];
|
|
18242
|
+
}
|
|
18243
|
+
|
|
18244
|
+
RageClickTracker.prototype.isRageClick = function(x, y, options) {
|
|
18245
|
+
options = options || {};
|
|
18246
|
+
var thresholdPx = options['threshold_px'] || DEFAULT_RAGE_CLICK_THRESHOLD_PX;
|
|
18247
|
+
var timeoutMs = options['timeout_ms'] || DEFAULT_RAGE_CLICK_TIMEOUT_MS;
|
|
18248
|
+
var clickCount = options['click_count'] || DEFAULT_RAGE_CLICK_CLICK_COUNT;
|
|
18249
|
+
var timestamp = Date.now();
|
|
18250
|
+
|
|
18251
|
+
var lastClick = this.clicks[this.clicks.length - 1];
|
|
18252
|
+
if (
|
|
18253
|
+
lastClick &&
|
|
18254
|
+
timestamp - lastClick.timestamp < timeoutMs &&
|
|
18255
|
+
Math.sqrt(Math.pow(x - lastClick.x, 2) + Math.pow(y - lastClick.y, 2)) < thresholdPx
|
|
18256
|
+
) {
|
|
18257
|
+
this.clicks.push({ x: x, y: y, timestamp: timestamp });
|
|
18258
|
+
if (this.clicks.length >= clickCount) {
|
|
18259
|
+
this.clicks = [];
|
|
18260
|
+
return true;
|
|
18261
|
+
}
|
|
18262
|
+
} else {
|
|
18263
|
+
this.clicks = [{ x: x, y: y, timestamp: timestamp }];
|
|
18264
|
+
}
|
|
18265
|
+
return false;
|
|
18266
|
+
};
|
|
18267
|
+
|
|
18212
18268
|
var AUTOCAPTURE_CONFIG_KEY = 'autocapture';
|
|
18213
18269
|
var LEGACY_PAGEVIEW_CONFIG_KEY = 'track_pageview';
|
|
18214
18270
|
|
|
@@ -18230,6 +18286,7 @@
|
|
|
18230
18286
|
var CONFIG_TRACK_CLICK = 'click';
|
|
18231
18287
|
var CONFIG_TRACK_INPUT = 'input';
|
|
18232
18288
|
var CONFIG_TRACK_PAGEVIEW = 'pageview';
|
|
18289
|
+
var CONFIG_TRACK_RAGE_CLICK = 'rage_click';
|
|
18233
18290
|
var CONFIG_TRACK_SCROLL = 'scroll';
|
|
18234
18291
|
var CONFIG_TRACK_SUBMIT = 'submit';
|
|
18235
18292
|
|
|
@@ -18247,6 +18304,7 @@
|
|
|
18247
18304
|
CONFIG_DEFAULTS$1[CONFIG_TRACK_CLICK] = true;
|
|
18248
18305
|
CONFIG_DEFAULTS$1[CONFIG_TRACK_INPUT] = true;
|
|
18249
18306
|
CONFIG_DEFAULTS$1[CONFIG_TRACK_PAGEVIEW] = PAGEVIEW_OPTION_FULL_URL;
|
|
18307
|
+
CONFIG_DEFAULTS$1[CONFIG_TRACK_RAGE_CLICK] = true;
|
|
18250
18308
|
CONFIG_DEFAULTS$1[CONFIG_TRACK_SCROLL] = true;
|
|
18251
18309
|
CONFIG_DEFAULTS$1[CONFIG_TRACK_SUBMIT] = true;
|
|
18252
18310
|
|
|
@@ -18256,6 +18314,7 @@
|
|
|
18256
18314
|
|
|
18257
18315
|
var MP_EV_CLICK = '$mp_click';
|
|
18258
18316
|
var MP_EV_INPUT = '$mp_input_change';
|
|
18317
|
+
var MP_EV_RAGE_CLICK = '$mp_rage_click';
|
|
18259
18318
|
var MP_EV_SCROLL = '$mp_scroll';
|
|
18260
18319
|
var MP_EV_SUBMIT = '$mp_submit';
|
|
18261
18320
|
|
|
@@ -18278,6 +18337,7 @@
|
|
|
18278
18337
|
this.initInputTracking();
|
|
18279
18338
|
this.initScrollTracking();
|
|
18280
18339
|
this.initSubmitTracking();
|
|
18340
|
+
this.initRageClickTracking();
|
|
18281
18341
|
};
|
|
18282
18342
|
|
|
18283
18343
|
Autocapture.prototype.getFullConfig = function() {
|
|
@@ -18356,6 +18416,11 @@
|
|
|
18356
18416
|
return;
|
|
18357
18417
|
}
|
|
18358
18418
|
|
|
18419
|
+
var isCapturedForHeatMap = this.mp.is_recording_heatmap_data() && (
|
|
18420
|
+
(mpEventName === MP_EV_CLICK && !this.getConfig(CONFIG_TRACK_CLICK)) ||
|
|
18421
|
+
(mpEventName === MP_EV_RAGE_CLICK && !this._getRageClickConfig())
|
|
18422
|
+
);
|
|
18423
|
+
|
|
18359
18424
|
var props = getPropsForDOMEvent(ev, {
|
|
18360
18425
|
allowElementCallback: this.getConfig(CONFIG_ALLOW_ELEMENT_CALLBACK),
|
|
18361
18426
|
allowSelectors: this.getConfig(CONFIG_ALLOW_SELECTORS),
|
|
@@ -18364,7 +18429,7 @@
|
|
|
18364
18429
|
blockSelectors: this.getConfig(CONFIG_BLOCK_SELECTORS),
|
|
18365
18430
|
captureExtraAttrs: this.getConfig(CONFIG_CAPTURE_EXTRA_ATTRS),
|
|
18366
18431
|
captureTextContent: this.getConfig(CONFIG_CAPTURE_TEXT_CONTENT),
|
|
18367
|
-
capturedForHeatMap:
|
|
18432
|
+
capturedForHeatMap: isCapturedForHeatMap,
|
|
18368
18433
|
});
|
|
18369
18434
|
if (props) {
|
|
18370
18435
|
_.extend(props, DEFAULT_PROPS);
|
|
@@ -18372,6 +18437,24 @@
|
|
|
18372
18437
|
}
|
|
18373
18438
|
};
|
|
18374
18439
|
|
|
18440
|
+
Autocapture.prototype._getRageClickConfig = function() {
|
|
18441
|
+
var config = this.getConfig(CONFIG_TRACK_RAGE_CLICK);
|
|
18442
|
+
|
|
18443
|
+
if (!config) {
|
|
18444
|
+
return null; // rage click tracking disabled
|
|
18445
|
+
}
|
|
18446
|
+
|
|
18447
|
+
if (config === true) {
|
|
18448
|
+
return {}; // use defaults
|
|
18449
|
+
}
|
|
18450
|
+
|
|
18451
|
+
if (typeof config === 'object') {
|
|
18452
|
+
return config; // use custom configuration
|
|
18453
|
+
}
|
|
18454
|
+
|
|
18455
|
+
return {}; // fallback to defaults for any other truthy value
|
|
18456
|
+
};
|
|
18457
|
+
|
|
18375
18458
|
Autocapture.prototype.initClickTracking = function() {
|
|
18376
18459
|
win.removeEventListener(EV_CLICK, this.listenerClick);
|
|
18377
18460
|
|
|
@@ -18473,6 +18556,36 @@
|
|
|
18473
18556
|
}.bind(this)));
|
|
18474
18557
|
};
|
|
18475
18558
|
|
|
18559
|
+
Autocapture.prototype.initRageClickTracking = function() {
|
|
18560
|
+
win.removeEventListener(EV_CLICK, this.listenerRageClick);
|
|
18561
|
+
|
|
18562
|
+
var rageClickConfig = this._getRageClickConfig();
|
|
18563
|
+
if (!rageClickConfig && !this.mp.get_config('record_heatmap_data')) {
|
|
18564
|
+
return;
|
|
18565
|
+
}
|
|
18566
|
+
|
|
18567
|
+
logger$1.log('Initializing rage click tracking');
|
|
18568
|
+
if (!this._rageClickTracker) {
|
|
18569
|
+
this._rageClickTracker = new RageClickTracker();
|
|
18570
|
+
}
|
|
18571
|
+
|
|
18572
|
+
this.listenerRageClick = function(ev) {
|
|
18573
|
+
var currentRageClickConfig = this._getRageClickConfig();
|
|
18574
|
+
if (!currentRageClickConfig && !this.mp.is_recording_heatmap_data()) {
|
|
18575
|
+
return;
|
|
18576
|
+
}
|
|
18577
|
+
|
|
18578
|
+
if (this.currentUrlBlocked()) {
|
|
18579
|
+
return;
|
|
18580
|
+
}
|
|
18581
|
+
|
|
18582
|
+
if (this._rageClickTracker.isRageClick(ev['pageX'], ev['pageY'], currentRageClickConfig)) {
|
|
18583
|
+
this.trackDomEvent(ev, MP_EV_RAGE_CLICK);
|
|
18584
|
+
}
|
|
18585
|
+
}.bind(this);
|
|
18586
|
+
win.addEventListener(EV_CLICK, this.listenerRageClick);
|
|
18587
|
+
};
|
|
18588
|
+
|
|
18476
18589
|
Autocapture.prototype.initScrollTracking = function() {
|
|
18477
18590
|
win.removeEventListener(EV_SCROLLEND, this.listenerScroll);
|
|
18478
18591
|
|
|
@@ -18557,8 +18670,10 @@
|
|
|
18557
18670
|
* @constructor
|
|
18558
18671
|
*/
|
|
18559
18672
|
var FeatureFlagManager = function(initOptions) {
|
|
18673
|
+
this.getFullApiRoute = initOptions.getFullApiRoute;
|
|
18560
18674
|
this.getMpConfig = initOptions.getConfigFunc;
|
|
18561
|
-
this.
|
|
18675
|
+
this.setMpConfig = initOptions.setConfigFunc;
|
|
18676
|
+
this.getMpProperty = initOptions.getPropertyFunc;
|
|
18562
18677
|
this.track = initOptions.trackingFunc;
|
|
18563
18678
|
};
|
|
18564
18679
|
|
|
@@ -18595,6 +18710,23 @@
|
|
|
18595
18710
|
return !!this.getMpConfig(FLAGS_CONFIG_KEY);
|
|
18596
18711
|
};
|
|
18597
18712
|
|
|
18713
|
+
FeatureFlagManager.prototype.updateContext = function(newContext, options) {
|
|
18714
|
+
if (!this.isSystemEnabled()) {
|
|
18715
|
+
logger.critical('Feature Flags not enabled, cannot update context');
|
|
18716
|
+
return Promise.resolve();
|
|
18717
|
+
}
|
|
18718
|
+
|
|
18719
|
+
var ffConfig = this.getMpConfig(FLAGS_CONFIG_KEY);
|
|
18720
|
+
if (!_.isObject(ffConfig)) {
|
|
18721
|
+
ffConfig = {};
|
|
18722
|
+
}
|
|
18723
|
+
var oldContext = (options && options['replace']) ? {} : this.getConfig(CONFIG_CONTEXT);
|
|
18724
|
+
ffConfig[CONFIG_CONTEXT] = _.extend({}, oldContext, newContext);
|
|
18725
|
+
|
|
18726
|
+
this.setMpConfig(FLAGS_CONFIG_KEY, ffConfig);
|
|
18727
|
+
return this.fetchFlags();
|
|
18728
|
+
};
|
|
18729
|
+
|
|
18598
18730
|
FeatureFlagManager.prototype.areFlagsReady = function() {
|
|
18599
18731
|
if (!this.isSystemEnabled()) {
|
|
18600
18732
|
logger.error('Feature Flags not enabled');
|
|
@@ -18604,15 +18736,17 @@
|
|
|
18604
18736
|
|
|
18605
18737
|
FeatureFlagManager.prototype.fetchFlags = function() {
|
|
18606
18738
|
if (!this.isSystemEnabled()) {
|
|
18607
|
-
return;
|
|
18739
|
+
return Promise.resolve();
|
|
18608
18740
|
}
|
|
18609
18741
|
|
|
18610
|
-
var distinctId = this.
|
|
18742
|
+
var distinctId = this.getMpProperty('distinct_id');
|
|
18743
|
+
var deviceId = this.getMpProperty('$device_id');
|
|
18611
18744
|
logger.log('Fetching flags for distinct ID: ' + distinctId);
|
|
18612
18745
|
var reqParams = {
|
|
18613
|
-
'context': _.extend({'distinct_id': distinctId}, this.getConfig(CONFIG_CONTEXT))
|
|
18746
|
+
'context': _.extend({'distinct_id': distinctId, 'device_id': deviceId}, this.getConfig(CONFIG_CONTEXT))
|
|
18614
18747
|
};
|
|
18615
|
-
this.
|
|
18748
|
+
this._fetchInProgressStartTime = Date.now();
|
|
18749
|
+
this.fetchPromise = win['fetch'](this.getFullApiRoute(), {
|
|
18616
18750
|
'method': 'POST',
|
|
18617
18751
|
'headers': {
|
|
18618
18752
|
'Authorization': 'Basic ' + btoa(this.getMpConfig('token') + ':'),
|
|
@@ -18620,6 +18754,7 @@
|
|
|
18620
18754
|
},
|
|
18621
18755
|
'body': JSON.stringify(reqParams)
|
|
18622
18756
|
}).then(function(response) {
|
|
18757
|
+
this.markFetchComplete();
|
|
18623
18758
|
return response.json().then(function(responseBody) {
|
|
18624
18759
|
var responseFlags = responseBody['flags'];
|
|
18625
18760
|
if (!responseFlags) {
|
|
@@ -18634,9 +18769,26 @@
|
|
|
18634
18769
|
});
|
|
18635
18770
|
this.flags = flags;
|
|
18636
18771
|
}.bind(this)).catch(function(error) {
|
|
18772
|
+
this.markFetchComplete();
|
|
18637
18773
|
logger.error(error);
|
|
18638
|
-
});
|
|
18639
|
-
}.bind(this)).catch(function() {
|
|
18774
|
+
}.bind(this));
|
|
18775
|
+
}.bind(this)).catch(function(error) {
|
|
18776
|
+
this.markFetchComplete();
|
|
18777
|
+
logger.error(error);
|
|
18778
|
+
}.bind(this));
|
|
18779
|
+
|
|
18780
|
+
return this.fetchPromise;
|
|
18781
|
+
};
|
|
18782
|
+
|
|
18783
|
+
FeatureFlagManager.prototype.markFetchComplete = function() {
|
|
18784
|
+
if (!this._fetchInProgressStartTime) {
|
|
18785
|
+
logger.error('Fetch in progress started time not set, cannot mark fetch complete');
|
|
18786
|
+
return;
|
|
18787
|
+
}
|
|
18788
|
+
this._fetchStartTime = this._fetchInProgressStartTime;
|
|
18789
|
+
this._fetchCompleteTime = Date.now();
|
|
18790
|
+
this._fetchLatency = this._fetchCompleteTime - this._fetchStartTime;
|
|
18791
|
+
this._fetchInProgressStartTime = null;
|
|
18640
18792
|
};
|
|
18641
18793
|
|
|
18642
18794
|
FeatureFlagManager.prototype.getVariant = function(featureName, fallback) {
|
|
@@ -18715,7 +18867,10 @@
|
|
|
18715
18867
|
this.track('$experiment_started', {
|
|
18716
18868
|
'Experiment name': featureName,
|
|
18717
18869
|
'Variant name': feature['key'],
|
|
18718
|
-
'$experiment_type': 'feature_flag'
|
|
18870
|
+
'$experiment_type': 'feature_flag',
|
|
18871
|
+
'Variant fetch start time': new Date(this._fetchStartTime).toISOString(),
|
|
18872
|
+
'Variant fetch complete time': new Date(this._fetchCompleteTime).toISOString(),
|
|
18873
|
+
'Variant fetch latency (ms)': this._fetchLatency
|
|
18719
18874
|
});
|
|
18720
18875
|
};
|
|
18721
18876
|
|
|
@@ -18735,6 +18890,7 @@
|
|
|
18735
18890
|
FeatureFlagManager.prototype['get_variant_value_sync'] = FeatureFlagManager.prototype.getVariantValueSync;
|
|
18736
18891
|
FeatureFlagManager.prototype['is_enabled'] = FeatureFlagManager.prototype.isEnabled;
|
|
18737
18892
|
FeatureFlagManager.prototype['is_enabled_sync'] = FeatureFlagManager.prototype.isEnabledSync;
|
|
18893
|
+
FeatureFlagManager.prototype['update_context'] = FeatureFlagManager.prototype.updateContext;
|
|
18738
18894
|
|
|
18739
18895
|
// Deprecated method
|
|
18740
18896
|
FeatureFlagManager.prototype['get_feature_data'] = FeatureFlagManager.prototype.getFeatureData;
|
|
@@ -20195,7 +20351,7 @@
|
|
|
20195
20351
|
'batch_autostart': true,
|
|
20196
20352
|
'hooks': {},
|
|
20197
20353
|
'record_block_class': new RegExp('^(mp-block|fs-exclude|amp-block|rr-block|ph-no-capture)$'),
|
|
20198
|
-
'record_block_selector': 'img, video',
|
|
20354
|
+
'record_block_selector': 'img, video, audio',
|
|
20199
20355
|
'record_canvas': false,
|
|
20200
20356
|
'record_collect_fonts': false,
|
|
20201
20357
|
'record_heatmap_data': false,
|
|
@@ -20415,8 +20571,12 @@
|
|
|
20415
20571
|
}
|
|
20416
20572
|
|
|
20417
20573
|
this.flags = new FeatureFlagManager({
|
|
20574
|
+
getFullApiRoute: _.bind(function() {
|
|
20575
|
+
return this.get_api_host('flags') + '/' + this.get_config('api_routes')['flags'];
|
|
20576
|
+
}, this),
|
|
20418
20577
|
getConfigFunc: _.bind(this.get_config, this),
|
|
20419
|
-
|
|
20578
|
+
setConfigFunc: _.bind(this.set_config, this),
|
|
20579
|
+
getPropertyFunc: _.bind(this.get_property, this),
|
|
20420
20580
|
trackingFunc: _.bind(this.track, this)
|
|
20421
20581
|
});
|
|
20422
20582
|
this.flags.init();
|
|
@@ -20902,11 +21062,10 @@
|
|
|
20902
21062
|
|
|
20903
21063
|
MixpanelLib.prototype.get_batcher_configs = function() {
|
|
20904
21064
|
var queue_prefix = '__mpq_' + this.get_config('token');
|
|
20905
|
-
var api_routes = this.get_config('api_routes');
|
|
20906
21065
|
this._batcher_configs = this._batcher_configs || {
|
|
20907
|
-
events: {type: 'events',
|
|
20908
|
-
people: {type: 'people',
|
|
20909
|
-
groups: {type: 'groups',
|
|
21066
|
+
events: {type: 'events', api_name: 'track', queue_key: queue_prefix + '_ev'},
|
|
21067
|
+
people: {type: 'people', api_name: 'engage', queue_key: queue_prefix + '_pp'},
|
|
21068
|
+
groups: {type: 'groups', api_name: 'groups', queue_key: queue_prefix + '_gr'}
|
|
20910
21069
|
};
|
|
20911
21070
|
return this._batcher_configs;
|
|
20912
21071
|
};
|
|
@@ -20920,8 +21079,9 @@
|
|
|
20920
21079
|
libConfig: this['config'],
|
|
20921
21080
|
errorReporter: this.get_config('error_reporter'),
|
|
20922
21081
|
sendRequestFunc: _.bind(function(data, options, cb) {
|
|
21082
|
+
var api_routes = this.get_config('api_routes');
|
|
20923
21083
|
this._send_request(
|
|
20924
|
-
this.
|
|
21084
|
+
this.get_api_host(attrs.api_name) + '/' + api_routes[attrs.api_name],
|
|
20925
21085
|
this._encode_data_for_request(data),
|
|
20926
21086
|
options,
|
|
20927
21087
|
this._prepare_callback(cb, data)
|
|
@@ -21649,31 +21809,15 @@
|
|
|
21649
21809
|
* Useful for clearing data when a user logs out.
|
|
21650
21810
|
*/
|
|
21651
21811
|
MixpanelLib.prototype.reset = function() {
|
|
21652
|
-
|
|
21653
|
-
|
|
21654
|
-
|
|
21655
|
-
|
|
21656
|
-
|
|
21657
|
-
|
|
21658
|
-
|
|
21659
|
-
|
|
21660
|
-
|
|
21661
|
-
}, '');
|
|
21662
|
-
};
|
|
21663
|
-
|
|
21664
|
-
if (self._recorder) {
|
|
21665
|
-
self.stop_session_recording()
|
|
21666
|
-
.then(function () {
|
|
21667
|
-
reset();
|
|
21668
|
-
self._check_and_start_session_recording();
|
|
21669
|
-
})
|
|
21670
|
-
.catch(_.bind(function (err) {
|
|
21671
|
-
reset();
|
|
21672
|
-
this.report_error('Error restarting recording session', err);
|
|
21673
|
-
}, this));
|
|
21674
|
-
} else {
|
|
21675
|
-
reset();
|
|
21676
|
-
}
|
|
21812
|
+
this.stop_session_recording();
|
|
21813
|
+
this['persistence'].clear();
|
|
21814
|
+
this._flags.identify_called = false;
|
|
21815
|
+
var uuid = _.UUID();
|
|
21816
|
+
this.register_once({
|
|
21817
|
+
'distinct_id': DEVICE_ID_PREFIX + uuid,
|
|
21818
|
+
'$device_id': uuid
|
|
21819
|
+
}, '');
|
|
21820
|
+
this._check_and_start_session_recording();
|
|
21677
21821
|
};
|
|
21678
21822
|
|
|
21679
21823
|
/**
|
package/package.json
CHANGED
package/src/autocapture/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
EV_CHANGE, EV_CLICK, EV_HASHCHANGE, EV_MP_LOCATION_CHANGE, EV_POPSTATE,
|
|
6
6
|
EV_SCROLLEND, EV_SUBMIT
|
|
7
7
|
} from './utils';
|
|
8
|
+
import { RageClickTracker } from './rageclick';
|
|
8
9
|
|
|
9
10
|
var AUTOCAPTURE_CONFIG_KEY = 'autocapture';
|
|
10
11
|
var LEGACY_PAGEVIEW_CONFIG_KEY = 'track_pageview';
|
|
@@ -27,6 +28,7 @@ var CONFIG_SCROLL_CHECKPOINTS = 'scroll_depth_percent_checkpoints';
|
|
|
27
28
|
var CONFIG_TRACK_CLICK = 'click';
|
|
28
29
|
var CONFIG_TRACK_INPUT = 'input';
|
|
29
30
|
var CONFIG_TRACK_PAGEVIEW = 'pageview';
|
|
31
|
+
var CONFIG_TRACK_RAGE_CLICK = 'rage_click';
|
|
30
32
|
var CONFIG_TRACK_SCROLL = 'scroll';
|
|
31
33
|
var CONFIG_TRACK_SUBMIT = 'submit';
|
|
32
34
|
|
|
@@ -44,6 +46,7 @@ CONFIG_DEFAULTS[CONFIG_SCROLL_CHECKPOINTS] = [25, 50, 75, 100];
|
|
|
44
46
|
CONFIG_DEFAULTS[CONFIG_TRACK_CLICK] = true;
|
|
45
47
|
CONFIG_DEFAULTS[CONFIG_TRACK_INPUT] = true;
|
|
46
48
|
CONFIG_DEFAULTS[CONFIG_TRACK_PAGEVIEW] = PAGEVIEW_OPTION_FULL_URL;
|
|
49
|
+
CONFIG_DEFAULTS[CONFIG_TRACK_RAGE_CLICK] = true;
|
|
47
50
|
CONFIG_DEFAULTS[CONFIG_TRACK_SCROLL] = true;
|
|
48
51
|
CONFIG_DEFAULTS[CONFIG_TRACK_SUBMIT] = true;
|
|
49
52
|
|
|
@@ -53,6 +56,7 @@ var DEFAULT_PROPS = {
|
|
|
53
56
|
|
|
54
57
|
var MP_EV_CLICK = '$mp_click';
|
|
55
58
|
var MP_EV_INPUT = '$mp_input_change';
|
|
59
|
+
var MP_EV_RAGE_CLICK = '$mp_rage_click';
|
|
56
60
|
var MP_EV_SCROLL = '$mp_scroll';
|
|
57
61
|
var MP_EV_SUBMIT = '$mp_submit';
|
|
58
62
|
|
|
@@ -75,6 +79,7 @@ Autocapture.prototype.init = function() {
|
|
|
75
79
|
this.initInputTracking();
|
|
76
80
|
this.initScrollTracking();
|
|
77
81
|
this.initSubmitTracking();
|
|
82
|
+
this.initRageClickTracking();
|
|
78
83
|
};
|
|
79
84
|
|
|
80
85
|
Autocapture.prototype.getFullConfig = function() {
|
|
@@ -153,6 +158,11 @@ Autocapture.prototype.trackDomEvent = function(ev, mpEventName) {
|
|
|
153
158
|
return;
|
|
154
159
|
}
|
|
155
160
|
|
|
161
|
+
var isCapturedForHeatMap = this.mp.is_recording_heatmap_data() && (
|
|
162
|
+
(mpEventName === MP_EV_CLICK && !this.getConfig(CONFIG_TRACK_CLICK)) ||
|
|
163
|
+
(mpEventName === MP_EV_RAGE_CLICK && !this._getRageClickConfig())
|
|
164
|
+
);
|
|
165
|
+
|
|
156
166
|
var props = getPropsForDOMEvent(ev, {
|
|
157
167
|
allowElementCallback: this.getConfig(CONFIG_ALLOW_ELEMENT_CALLBACK),
|
|
158
168
|
allowSelectors: this.getConfig(CONFIG_ALLOW_SELECTORS),
|
|
@@ -161,7 +171,7 @@ Autocapture.prototype.trackDomEvent = function(ev, mpEventName) {
|
|
|
161
171
|
blockSelectors: this.getConfig(CONFIG_BLOCK_SELECTORS),
|
|
162
172
|
captureExtraAttrs: this.getConfig(CONFIG_CAPTURE_EXTRA_ATTRS),
|
|
163
173
|
captureTextContent: this.getConfig(CONFIG_CAPTURE_TEXT_CONTENT),
|
|
164
|
-
capturedForHeatMap:
|
|
174
|
+
capturedForHeatMap: isCapturedForHeatMap,
|
|
165
175
|
});
|
|
166
176
|
if (props) {
|
|
167
177
|
_.extend(props, DEFAULT_PROPS);
|
|
@@ -169,6 +179,24 @@ Autocapture.prototype.trackDomEvent = function(ev, mpEventName) {
|
|
|
169
179
|
}
|
|
170
180
|
};
|
|
171
181
|
|
|
182
|
+
Autocapture.prototype._getRageClickConfig = function() {
|
|
183
|
+
var config = this.getConfig(CONFIG_TRACK_RAGE_CLICK);
|
|
184
|
+
|
|
185
|
+
if (!config) {
|
|
186
|
+
return null; // rage click tracking disabled
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (config === true) {
|
|
190
|
+
return {}; // use defaults
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (typeof config === 'object') {
|
|
194
|
+
return config; // use custom configuration
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return {}; // fallback to defaults for any other truthy value
|
|
198
|
+
};
|
|
199
|
+
|
|
172
200
|
Autocapture.prototype.initClickTracking = function() {
|
|
173
201
|
window.removeEventListener(EV_CLICK, this.listenerClick);
|
|
174
202
|
|
|
@@ -270,6 +298,36 @@ Autocapture.prototype.initPageviewTracking = function() {
|
|
|
270
298
|
}.bind(this)));
|
|
271
299
|
};
|
|
272
300
|
|
|
301
|
+
Autocapture.prototype.initRageClickTracking = function() {
|
|
302
|
+
window.removeEventListener(EV_CLICK, this.listenerRageClick);
|
|
303
|
+
|
|
304
|
+
var rageClickConfig = this._getRageClickConfig();
|
|
305
|
+
if (!rageClickConfig && !this.mp.get_config('record_heatmap_data')) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
logger.log('Initializing rage click tracking');
|
|
310
|
+
if (!this._rageClickTracker) {
|
|
311
|
+
this._rageClickTracker = new RageClickTracker();
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
this.listenerRageClick = function(ev) {
|
|
315
|
+
var currentRageClickConfig = this._getRageClickConfig();
|
|
316
|
+
if (!currentRageClickConfig && !this.mp.is_recording_heatmap_data()) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (this.currentUrlBlocked()) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (this._rageClickTracker.isRageClick(ev['pageX'], ev['pageY'], currentRageClickConfig)) {
|
|
325
|
+
this.trackDomEvent(ev, MP_EV_RAGE_CLICK);
|
|
326
|
+
}
|
|
327
|
+
}.bind(this);
|
|
328
|
+
window.addEventListener(EV_CLICK, this.listenerRageClick);
|
|
329
|
+
};
|
|
330
|
+
|
|
273
331
|
Autocapture.prototype.initScrollTracking = function() {
|
|
274
332
|
window.removeEventListener(EV_SCROLLEND, this.listenerScroll);
|
|
275
333
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** @const */ var DEFAULT_RAGE_CLICK_THRESHOLD_PX = 30;
|
|
2
|
+
/** @const */ var DEFAULT_RAGE_CLICK_TIMEOUT_MS = 1000;
|
|
3
|
+
/** @const */ var DEFAULT_RAGE_CLICK_CLICK_COUNT = 4;
|
|
4
|
+
|
|
5
|
+
function RageClickTracker() {
|
|
6
|
+
this.clicks = [];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
RageClickTracker.prototype.isRageClick = function(x, y, options) {
|
|
10
|
+
options = options || {};
|
|
11
|
+
var thresholdPx = options['threshold_px'] || DEFAULT_RAGE_CLICK_THRESHOLD_PX;
|
|
12
|
+
var timeoutMs = options['timeout_ms'] || DEFAULT_RAGE_CLICK_TIMEOUT_MS;
|
|
13
|
+
var clickCount = options['click_count'] || DEFAULT_RAGE_CLICK_CLICK_COUNT;
|
|
14
|
+
var timestamp = Date.now();
|
|
15
|
+
|
|
16
|
+
var lastClick = this.clicks[this.clicks.length - 1];
|
|
17
|
+
if (
|
|
18
|
+
lastClick &&
|
|
19
|
+
timestamp - lastClick.timestamp < timeoutMs &&
|
|
20
|
+
Math.sqrt(Math.pow(x - lastClick.x, 2) + Math.pow(y - lastClick.y, 2)) < thresholdPx
|
|
21
|
+
) {
|
|
22
|
+
this.clicks.push({ x: x, y: y, timestamp: timestamp });
|
|
23
|
+
if (this.clicks.length >= clickCount) {
|
|
24
|
+
this.clicks = [];
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
} else {
|
|
28
|
+
this.clicks = [{ x: x, y: y, timestamp: timestamp }];
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export {
|
|
34
|
+
RageClickTracker,
|
|
35
|
+
DEFAULT_RAGE_CLICK_THRESHOLD_PX,
|
|
36
|
+
DEFAULT_RAGE_CLICK_TIMEOUT_MS,
|
|
37
|
+
DEFAULT_RAGE_CLICK_CLICK_COUNT
|
|
38
|
+
};
|