mixpanel-browser 2.63.0 → 2.65.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.
@@ -13942,7 +13942,7 @@ if (typeof Promise !== 'undefined' && Promise.toString().indexOf('[native code]'
13942
13942
 
13943
13943
  var Config = {
13944
13944
  DEBUG: false,
13945
- LIB_VERSION: '2.63.0'
13945
+ LIB_VERSION: '2.65.0'
13946
13946
  };
13947
13947
 
13948
13948
  /* eslint camelcase: "off", eqeqeq: "off" */
@@ -15427,6 +15427,9 @@ _.info = {
15427
15427
  return 'Microsoft Edge';
15428
15428
  } else if (_.includes(user_agent, 'FBIOS')) {
15429
15429
  return 'Facebook Mobile';
15430
+ } else if (_.includes(user_agent, 'Whale/')) {
15431
+ // https://user-agents.net/browsers/whale-browser
15432
+ return 'Whale Browser';
15430
15433
  } else if (_.includes(user_agent, 'Chrome')) {
15431
15434
  return 'Chrome';
15432
15435
  } else if (_.includes(user_agent, 'CriOS')) {
@@ -15478,7 +15481,8 @@ _.info = {
15478
15481
  'Android Mobile': /android\s(\d+(\.\d+)?)/,
15479
15482
  'Samsung Internet': /SamsungBrowser\/(\d+(\.\d+)?)/,
15480
15483
  'Internet Explorer': /(rv:|MSIE )(\d+(\.\d+)?)/,
15481
- 'Mozilla': /rv:(\d+(\.\d+)?)/
15484
+ 'Mozilla': /rv:(\d+(\.\d+)?)/,
15485
+ 'Whale Browser': /Whale\/(\d+(\.\d+)?)/
15482
15486
  };
15483
15487
  var regex = versionRegexs[browser];
15484
15488
  if (regex === undefined) {
@@ -16101,7 +16105,7 @@ function _addOptOutCheck(method, getConfigValue) {
16101
16105
  };
16102
16106
  }
16103
16107
 
16104
- var logger$5 = console_with_prefix('lock');
16108
+ var logger$6 = console_with_prefix('lock');
16105
16109
 
16106
16110
  /**
16107
16111
  * SharedLock: a mutex built on HTML5 localStorage, to ensure that only one browser
@@ -16153,7 +16157,7 @@ SharedLock.prototype.withLock = function(lockedCB, pid) {
16153
16157
 
16154
16158
  var delay = function(cb) {
16155
16159
  if (new Date().getTime() - startTime > timeoutMS) {
16156
- logger$5.error('Timeout waiting for mutex on ' + key + '; clearing lock. [' + i + ']');
16160
+ logger$6.error('Timeout waiting for mutex on ' + key + '; clearing lock. [' + i + ']');
16157
16161
  storage.removeItem(keyZ);
16158
16162
  storage.removeItem(keyY);
16159
16163
  loop();
@@ -16296,7 +16300,7 @@ LocalStorageWrapper.prototype.removeItem = function (key) {
16296
16300
  }, this));
16297
16301
  };
16298
16302
 
16299
- var logger$4 = console_with_prefix('batch');
16303
+ var logger$5 = console_with_prefix('batch');
16300
16304
 
16301
16305
  /**
16302
16306
  * RequestQueue: queue for batching API requests with localStorage backup for retries.
@@ -16325,7 +16329,7 @@ var RequestQueue = function (storageKey, options) {
16325
16329
  timeoutMS: options.sharedLockTimeoutMS,
16326
16330
  });
16327
16331
  }
16328
- this.reportError = options.errorReporter || _.bind(logger$4.error, logger$4);
16332
+ this.reportError = options.errorReporter || _.bind(logger$5.error, logger$5);
16329
16333
 
16330
16334
  this.pid = options.pid || null; // pass pid to test out storage lock contention scenarios
16331
16335
 
@@ -16658,7 +16662,7 @@ RequestQueue.prototype.clear = function () {
16658
16662
  // maximum interval between request retries after exponential backoff
16659
16663
  var MAX_RETRY_INTERVAL_MS = 10 * 60 * 1000; // 10 minutes
16660
16664
 
16661
- var logger$3 = console_with_prefix('batch');
16665
+ var logger$4 = console_with_prefix('batch');
16662
16666
 
16663
16667
  /**
16664
16668
  * RequestBatcher: manages the queueing, flushing, retry etc of requests of one
@@ -16786,7 +16790,7 @@ RequestBatcher.prototype.sendRequestPromise = function(data, options) {
16786
16790
  */
16787
16791
  RequestBatcher.prototype.flush = function(options) {
16788
16792
  if (this.requestInProgress) {
16789
- logger$3.log('Flush: Request already in progress');
16793
+ logger$4.log('Flush: Request already in progress');
16790
16794
  return PromisePolyfill.resolve();
16791
16795
  }
16792
16796
 
@@ -16963,7 +16967,7 @@ RequestBatcher.prototype.flush = function(options) {
16963
16967
  if (options.unloading) {
16964
16968
  requestOptions.transport = 'sendBeacon';
16965
16969
  }
16966
- logger$3.log('MIXPANEL REQUEST:', dataForRequest);
16970
+ logger$4.log('MIXPANEL REQUEST:', dataForRequest);
16967
16971
  return this.sendRequestPromise(dataForRequest, requestOptions).then(batchSendCallback);
16968
16972
  }, this))
16969
16973
  .catch(_.bind(function(err) {
@@ -16976,7 +16980,7 @@ RequestBatcher.prototype.flush = function(options) {
16976
16980
  * Log error to global logger and optional user-defined logger.
16977
16981
  */
16978
16982
  RequestBatcher.prototype.reportError = function(msg, err) {
16979
- logger$3.error.apply(logger$3.error, arguments);
16983
+ logger$4.error.apply(logger$4.error, arguments);
16980
16984
  if (this.errorReporter) {
16981
16985
  try {
16982
16986
  if (!(err instanceof Error)) {
@@ -16984,7 +16988,7 @@ RequestBatcher.prototype.reportError = function(msg, err) {
16984
16988
  }
16985
16989
  this.errorReporter(msg, err);
16986
16990
  } catch(err) {
16987
- logger$3.error(err);
16991
+ logger$4.error(err);
16988
16992
  }
16989
16993
  }
16990
16994
  };
@@ -17000,7 +17004,7 @@ var isRecordingExpired = function(serializedRecording) {
17000
17004
 
17001
17005
  var RECORD_ENQUEUE_THROTTLE_MS = 250;
17002
17006
 
17003
- var logger$2 = console_with_prefix('recorder');
17007
+ var logger$3 = console_with_prefix('recorder');
17004
17008
  var CompressionStream = win['CompressionStream'];
17005
17009
 
17006
17010
  var RECORDER_BATCHER_LIB_CONFIG = {
@@ -17137,14 +17141,14 @@ SessionRecording.prototype.startRecording = function (shouldStopBatcher) {
17137
17141
  }
17138
17142
 
17139
17143
  if (this._stopRecording !== null) {
17140
- logger$2.log('Recording already in progress, skipping startRecording.');
17144
+ logger$3.log('Recording already in progress, skipping startRecording.');
17141
17145
  return;
17142
17146
  }
17143
17147
 
17144
17148
  this.recordMaxMs = this.getConfig('record_max_ms');
17145
17149
  if (this.recordMaxMs > MAX_RECORDING_MS) {
17146
17150
  this.recordMaxMs = MAX_RECORDING_MS;
17147
- logger$2.critical('record_max_ms cannot be greater than ' + MAX_RECORDING_MS + 'ms. Capping value.');
17151
+ logger$3.critical('record_max_ms cannot be greater than ' + MAX_RECORDING_MS + 'ms. Capping value.');
17148
17152
  }
17149
17153
 
17150
17154
  if (!this.maxExpires) {
@@ -17154,7 +17158,7 @@ SessionRecording.prototype.startRecording = function (shouldStopBatcher) {
17154
17158
  this.recordMinMs = this.getConfig('record_min_ms');
17155
17159
  if (this.recordMinMs > MAX_VALUE_FOR_MIN_RECORDING_MS) {
17156
17160
  this.recordMinMs = MAX_VALUE_FOR_MIN_RECORDING_MS;
17157
- logger$2.critical('record_min_ms cannot be greater than ' + MAX_VALUE_FOR_MIN_RECORDING_MS + 'ms. Capping value.');
17161
+ logger$3.critical('record_min_ms cannot be greater than ' + MAX_VALUE_FOR_MIN_RECORDING_MS + 'ms. Capping value.');
17158
17162
  }
17159
17163
 
17160
17164
  if (!this.replayStartTime) {
@@ -17438,14 +17442,14 @@ SessionRecording.prototype._flushEvents = addOptOutCheckMixpanelLib(function (da
17438
17442
 
17439
17443
 
17440
17444
  SessionRecording.prototype.reportError = function(msg, err) {
17441
- logger$2.error.apply(logger$2.error, arguments);
17445
+ logger$3.error.apply(logger$3.error, arguments);
17442
17446
  try {
17443
17447
  if (!err && !(msg instanceof Error)) {
17444
17448
  msg = new Error(msg);
17445
17449
  }
17446
17450
  this.getConfig('error_reporter')(msg, err);
17447
17451
  } catch(err) {
17448
- logger$2.error(err);
17452
+ logger$3.error(err);
17449
17453
  }
17450
17454
  };
17451
17455
 
@@ -17541,7 +17545,7 @@ RecordingRegistry.prototype.flushInactiveRecordings = function () {
17541
17545
  .catch(this.handleError.bind(this));
17542
17546
  };
17543
17547
 
17544
- var logger$1 = console_with_prefix('recorder');
17548
+ var logger$2 = console_with_prefix('recorder');
17545
17549
 
17546
17550
  /**
17547
17551
  * Recorder API: bundles rrweb and and exposes methods to start and stop recordings.
@@ -17557,7 +17561,7 @@ var MixpanelRecorder = function(mixpanelInstance, rrwebRecord, sharedLockStorage
17557
17561
  */
17558
17562
  this.recordingRegistry = new RecordingRegistry({
17559
17563
  mixpanelInstance: this.mixpanelInstance,
17560
- errorReporter: logger$1.error,
17564
+ errorReporter: logger$2.error,
17561
17565
  sharedLockStorage: sharedLockStorage
17562
17566
  });
17563
17567
  this._flushInactivePromise = this.recordingRegistry.flushInactiveRecordings();
@@ -17568,17 +17572,17 @@ var MixpanelRecorder = function(mixpanelInstance, rrwebRecord, sharedLockStorage
17568
17572
  MixpanelRecorder.prototype.startRecording = function(options) {
17569
17573
  options = options || {};
17570
17574
  if (this.activeRecording && !this.activeRecording.isRrwebStopped()) {
17571
- logger$1.log('Recording already in progress, skipping startRecording.');
17575
+ logger$2.log('Recording already in progress, skipping startRecording.');
17572
17576
  return;
17573
17577
  }
17574
17578
 
17575
17579
  var onIdleTimeout = function () {
17576
- logger$1.log('Idle timeout reached, restarting recording.');
17580
+ logger$2.log('Idle timeout reached, restarting recording.');
17577
17581
  this.resetRecording();
17578
17582
  }.bind(this);
17579
17583
 
17580
17584
  var onMaxLengthReached = function () {
17581
- logger$1.log('Max recording length reached, stopping recording.');
17585
+ logger$2.log('Max recording length reached, stopping recording.');
17582
17586
  this.resetRecording();
17583
17587
  }.bind(this);
17584
17588
 
@@ -17641,7 +17645,7 @@ MixpanelRecorder.prototype.resumeRecording = function (startNewIfInactive) {
17641
17645
  } else if (startNewIfInactive) {
17642
17646
  return this.startRecording({shouldStopBatcher: false});
17643
17647
  } else {
17644
- logger$1.log('No resumable recording found.');
17648
+ logger$2.log('No resumable recording found.');
17645
17649
  return null;
17646
17650
  }
17647
17651
  }.bind(this));
@@ -17699,7 +17703,7 @@ var TRACKED_ATTRS = [
17699
17703
  'href', 'name', 'role', 'title', 'type'
17700
17704
  ];
17701
17705
 
17702
- var logger = console_with_prefix('autocapture');
17706
+ var logger$1 = console_with_prefix('autocapture');
17703
17707
 
17704
17708
 
17705
17709
  function getClasses(el) {
@@ -17785,6 +17789,7 @@ function getPropsForDOMEvent(ev, config) {
17785
17789
  var blockSelectors = config.blockSelectors || [];
17786
17790
  var captureTextContent = config.captureTextContent || false;
17787
17791
  var captureExtraAttrs = config.captureExtraAttrs || [];
17792
+ var capturedForHeatMap = config.capturedForHeatMap || false;
17788
17793
 
17789
17794
  // convert array to set every time, as the config may have changed
17790
17795
  var blockAttrsSet = {};
@@ -17839,7 +17844,9 @@ function getPropsForDOMEvent(ev, config) {
17839
17844
  '$elements': elementsJson,
17840
17845
  '$el_attr__href': href,
17841
17846
  '$viewportHeight': Math.max(docElement['clientHeight'], win['innerHeight'] || 0),
17842
- '$viewportWidth': Math.max(docElement['clientWidth'], win['innerWidth'] || 0)
17847
+ '$viewportWidth': Math.max(docElement['clientWidth'], win['innerWidth'] || 0),
17848
+ '$pageHeight': document$1['body']['offsetHeight'] || 0,
17849
+ '$pageWidth': document$1['body']['offsetWidth'] || 0,
17843
17850
  };
17844
17851
  _.each(captureExtraAttrs, function(attr) {
17845
17852
  if (!blockAttrsSet[attr] && target.hasAttribute(attr)) {
@@ -17863,6 +17870,9 @@ function getPropsForDOMEvent(ev, config) {
17863
17870
  props['$' + prop] = ev[prop];
17864
17871
  }
17865
17872
  });
17873
+ if (capturedForHeatMap) {
17874
+ props['$captured_for_heatmap'] = true;
17875
+ }
17866
17876
  target = guessRealClickTarget(ev);
17867
17877
  }
17868
17878
  // prioritize text content from "real" click target if different from original target
@@ -17957,7 +17967,7 @@ function isElementAllowed(el, ev, allowElementCallback, allowSelectors) {
17957
17967
  return false;
17958
17968
  }
17959
17969
  } catch (err) {
17960
- logger.critical('Error while checking element in allowElementCallback', err);
17970
+ logger$1.critical('Error while checking element in allowElementCallback', err);
17961
17971
  return false;
17962
17972
  }
17963
17973
  }
@@ -17974,7 +17984,7 @@ function isElementAllowed(el, ev, allowElementCallback, allowSelectors) {
17974
17984
  return true;
17975
17985
  }
17976
17986
  } catch (err) {
17977
- logger.critical('Error while checking selector: ' + sel, err);
17987
+ logger$1.critical('Error while checking selector: ' + sel, err);
17978
17988
  }
17979
17989
  }
17980
17990
  return false;
@@ -17989,7 +17999,7 @@ function isElementBlocked(el, ev, blockElementCallback, blockSelectors) {
17989
17999
  return true;
17990
18000
  }
17991
18001
  } catch (err) {
17992
- logger.critical('Error while checking element in blockElementCallback', err);
18002
+ logger$1.critical('Error while checking element in blockElementCallback', err);
17993
18003
  return true;
17994
18004
  }
17995
18005
  }
@@ -18003,7 +18013,7 @@ function isElementBlocked(el, ev, blockElementCallback, blockSelectors) {
18003
18013
  return true;
18004
18014
  }
18005
18015
  } catch (err) {
18006
- logger.critical('Error while checking selector: ' + sel, err);
18016
+ logger$1.critical('Error while checking selector: ' + sel, err);
18007
18017
  }
18008
18018
  }
18009
18019
  }
@@ -18209,22 +18219,22 @@ var CONFIG_TRACK_PAGEVIEW = 'pageview';
18209
18219
  var CONFIG_TRACK_SCROLL = 'scroll';
18210
18220
  var CONFIG_TRACK_SUBMIT = 'submit';
18211
18221
 
18212
- var CONFIG_DEFAULTS = {};
18213
- CONFIG_DEFAULTS[CONFIG_ALLOW_SELECTORS] = [];
18214
- CONFIG_DEFAULTS[CONFIG_ALLOW_URL_REGEXES] = [];
18215
- CONFIG_DEFAULTS[CONFIG_BLOCK_ATTRS] = [];
18216
- CONFIG_DEFAULTS[CONFIG_BLOCK_ELEMENT_CALLBACK] = null;
18217
- CONFIG_DEFAULTS[CONFIG_BLOCK_SELECTORS] = [];
18218
- CONFIG_DEFAULTS[CONFIG_BLOCK_URL_REGEXES] = [];
18219
- CONFIG_DEFAULTS[CONFIG_CAPTURE_EXTRA_ATTRS] = [];
18220
- CONFIG_DEFAULTS[CONFIG_CAPTURE_TEXT_CONTENT] = false;
18221
- CONFIG_DEFAULTS[CONFIG_SCROLL_CAPTURE_ALL] = false;
18222
- CONFIG_DEFAULTS[CONFIG_SCROLL_CHECKPOINTS] = [25, 50, 75, 100];
18223
- CONFIG_DEFAULTS[CONFIG_TRACK_CLICK] = true;
18224
- CONFIG_DEFAULTS[CONFIG_TRACK_INPUT] = true;
18225
- CONFIG_DEFAULTS[CONFIG_TRACK_PAGEVIEW] = PAGEVIEW_OPTION_FULL_URL;
18226
- CONFIG_DEFAULTS[CONFIG_TRACK_SCROLL] = true;
18227
- CONFIG_DEFAULTS[CONFIG_TRACK_SUBMIT] = true;
18222
+ var CONFIG_DEFAULTS$1 = {};
18223
+ CONFIG_DEFAULTS$1[CONFIG_ALLOW_SELECTORS] = [];
18224
+ CONFIG_DEFAULTS$1[CONFIG_ALLOW_URL_REGEXES] = [];
18225
+ CONFIG_DEFAULTS$1[CONFIG_BLOCK_ATTRS] = [];
18226
+ CONFIG_DEFAULTS$1[CONFIG_BLOCK_ELEMENT_CALLBACK] = null;
18227
+ CONFIG_DEFAULTS$1[CONFIG_BLOCK_SELECTORS] = [];
18228
+ CONFIG_DEFAULTS$1[CONFIG_BLOCK_URL_REGEXES] = [];
18229
+ CONFIG_DEFAULTS$1[CONFIG_CAPTURE_EXTRA_ATTRS] = [];
18230
+ CONFIG_DEFAULTS$1[CONFIG_CAPTURE_TEXT_CONTENT] = false;
18231
+ CONFIG_DEFAULTS$1[CONFIG_SCROLL_CAPTURE_ALL] = false;
18232
+ CONFIG_DEFAULTS$1[CONFIG_SCROLL_CHECKPOINTS] = [25, 50, 75, 100];
18233
+ CONFIG_DEFAULTS$1[CONFIG_TRACK_CLICK] = true;
18234
+ CONFIG_DEFAULTS$1[CONFIG_TRACK_INPUT] = true;
18235
+ CONFIG_DEFAULTS$1[CONFIG_TRACK_PAGEVIEW] = PAGEVIEW_OPTION_FULL_URL;
18236
+ CONFIG_DEFAULTS$1[CONFIG_TRACK_SCROLL] = true;
18237
+ CONFIG_DEFAULTS$1[CONFIG_TRACK_SUBMIT] = true;
18228
18238
 
18229
18239
  var DEFAULT_PROPS = {
18230
18240
  '$mp_autocapture': true
@@ -18245,7 +18255,7 @@ var Autocapture = function(mp) {
18245
18255
 
18246
18256
  Autocapture.prototype.init = function() {
18247
18257
  if (!minDOMApisSupported()) {
18248
- logger.critical('Autocapture unavailable: missing required DOM APIs');
18258
+ logger$1.critical('Autocapture unavailable: missing required DOM APIs');
18249
18259
  return;
18250
18260
  }
18251
18261
 
@@ -18262,10 +18272,10 @@ Autocapture.prototype.getFullConfig = function() {
18262
18272
  // Autocapture is completely off
18263
18273
  return {};
18264
18274
  } else if (_.isObject(autocaptureConfig)) {
18265
- return _.extend({}, CONFIG_DEFAULTS, autocaptureConfig);
18275
+ return _.extend({}, CONFIG_DEFAULTS$1, autocaptureConfig);
18266
18276
  } else {
18267
18277
  // Autocapture config is non-object truthy value, return default
18268
- return CONFIG_DEFAULTS;
18278
+ return CONFIG_DEFAULTS$1;
18269
18279
  }
18270
18280
  };
18271
18281
 
@@ -18289,7 +18299,7 @@ Autocapture.prototype.currentUrlBlocked = function() {
18289
18299
  break;
18290
18300
  }
18291
18301
  } catch (err) {
18292
- logger.critical('Error while checking block URL regex: ' + allowRegex, err);
18302
+ logger$1.critical('Error while checking block URL regex: ' + allowRegex, err);
18293
18303
  return true;
18294
18304
  }
18295
18305
  }
@@ -18310,7 +18320,7 @@ Autocapture.prototype.currentUrlBlocked = function() {
18310
18320
  return true;
18311
18321
  }
18312
18322
  } catch (err) {
18313
- logger.critical('Error while checking block URL regex: ' + blockUrlRegexes[i], err);
18323
+ logger$1.critical('Error while checking block URL regex: ' + blockUrlRegexes[i], err);
18314
18324
  return true;
18315
18325
  }
18316
18326
  }
@@ -18339,7 +18349,8 @@ Autocapture.prototype.trackDomEvent = function(ev, mpEventName) {
18339
18349
  blockElementCallback: this.getConfig(CONFIG_BLOCK_ELEMENT_CALLBACK),
18340
18350
  blockSelectors: this.getConfig(CONFIG_BLOCK_SELECTORS),
18341
18351
  captureExtraAttrs: this.getConfig(CONFIG_CAPTURE_EXTRA_ATTRS),
18342
- captureTextContent: this.getConfig(CONFIG_CAPTURE_TEXT_CONTENT)
18352
+ captureTextContent: this.getConfig(CONFIG_CAPTURE_TEXT_CONTENT),
18353
+ capturedForHeatMap: mpEventName === MP_EV_CLICK && !this.getConfig(CONFIG_TRACK_CLICK) && this.mp.is_recording_heatmap_data(),
18343
18354
  });
18344
18355
  if (props) {
18345
18356
  _.extend(props, DEFAULT_PROPS);
@@ -18350,13 +18361,13 @@ Autocapture.prototype.trackDomEvent = function(ev, mpEventName) {
18350
18361
  Autocapture.prototype.initClickTracking = function() {
18351
18362
  win.removeEventListener(EV_CLICK, this.listenerClick);
18352
18363
 
18353
- if (!this.getConfig(CONFIG_TRACK_CLICK)) {
18364
+ if (!this.getConfig(CONFIG_TRACK_CLICK) && !this.mp.get_config('record_heatmap_data')) {
18354
18365
  return;
18355
18366
  }
18356
- logger.log('Initializing click tracking');
18367
+ logger$1.log('Initializing click tracking');
18357
18368
 
18358
18369
  this.listenerClick = win.addEventListener(EV_CLICK, function(ev) {
18359
- if (!this.getConfig(CONFIG_TRACK_CLICK)) {
18370
+ if (!this.getConfig(CONFIG_TRACK_CLICK) && !this.mp.is_recording_heatmap_data()) {
18360
18371
  return;
18361
18372
  }
18362
18373
  this.trackDomEvent(ev, MP_EV_CLICK);
@@ -18369,7 +18380,7 @@ Autocapture.prototype.initInputTracking = function() {
18369
18380
  if (!this.getConfig(CONFIG_TRACK_INPUT)) {
18370
18381
  return;
18371
18382
  }
18372
- logger.log('Initializing input tracking');
18383
+ logger$1.log('Initializing input tracking');
18373
18384
 
18374
18385
  this.listenerChange = win.addEventListener(EV_CHANGE, function(ev) {
18375
18386
  if (!this.getConfig(CONFIG_TRACK_INPUT)) {
@@ -18387,7 +18398,7 @@ Autocapture.prototype.initPageviewTracking = function() {
18387
18398
  if (!this.pageviewTrackingConfig()) {
18388
18399
  return;
18389
18400
  }
18390
- logger.log('Initializing pageview tracking');
18401
+ logger$1.log('Initializing pageview tracking');
18391
18402
 
18392
18403
  var previousTrackedUrl = '';
18393
18404
  var tracked = false;
@@ -18442,7 +18453,7 @@ Autocapture.prototype.initPageviewTracking = function() {
18442
18453
  }
18443
18454
  if (didPathChange) {
18444
18455
  this.lastScrollCheckpoint = 0;
18445
- logger.log('Path change: re-initializing scroll depth checkpoints');
18456
+ logger$1.log('Path change: re-initializing scroll depth checkpoints');
18446
18457
  }
18447
18458
  }
18448
18459
  }.bind(this)));
@@ -18454,7 +18465,7 @@ Autocapture.prototype.initScrollTracking = function() {
18454
18465
  if (!this.getConfig(CONFIG_TRACK_SCROLL)) {
18455
18466
  return;
18456
18467
  }
18457
- logger.log('Initializing scroll tracking');
18468
+ logger$1.log('Initializing scroll tracking');
18458
18469
  this.lastScrollCheckpoint = 0;
18459
18470
 
18460
18471
  this.listenerScroll = win.addEventListener(EV_SCROLLEND, safewrap(function() {
@@ -18491,7 +18502,7 @@ Autocapture.prototype.initScrollTracking = function() {
18491
18502
  }
18492
18503
  }
18493
18504
  } catch (err) {
18494
- logger.critical('Error while calculating scroll percentage', err);
18505
+ logger$1.critical('Error while calculating scroll percentage', err);
18495
18506
  }
18496
18507
  if (shouldTrack) {
18497
18508
  this.mp.track(MP_EV_SCROLL, props);
@@ -18505,7 +18516,7 @@ Autocapture.prototype.initSubmitTracking = function() {
18505
18516
  if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
18506
18517
  return;
18507
18518
  }
18508
- logger.log('Initializing submit tracking');
18519
+ logger$1.log('Initializing submit tracking');
18509
18520
 
18510
18521
  this.listenerSubmit = win.addEventListener(EV_SUBMIT, function(ev) {
18511
18522
  if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
@@ -18518,6 +18529,202 @@ Autocapture.prototype.initSubmitTracking = function() {
18518
18529
  // TODO integrate error_reporter from mixpanel instance
18519
18530
  safewrapClass(Autocapture);
18520
18531
 
18532
+ var fetch = win['fetch'];
18533
+ var logger = console_with_prefix('flags');
18534
+
18535
+ var FLAGS_CONFIG_KEY = 'flags';
18536
+
18537
+ var CONFIG_CONTEXT = 'context';
18538
+ var CONFIG_DEFAULTS = {};
18539
+ CONFIG_DEFAULTS[CONFIG_CONTEXT] = {};
18540
+
18541
+ /**
18542
+ * FeatureFlagManager: support for Mixpanel's feature flagging product
18543
+ * @constructor
18544
+ */
18545
+ var FeatureFlagManager = function(initOptions) {
18546
+ this.getMpConfig = initOptions.getConfigFunc;
18547
+ this.getDistinctId = initOptions.getDistinctIdFunc;
18548
+ this.track = initOptions.trackingFunc;
18549
+ };
18550
+
18551
+ FeatureFlagManager.prototype.init = function() {
18552
+ if (!minApisSupported()) {
18553
+ logger.critical('Feature Flags unavailable: missing minimum required APIs');
18554
+ return;
18555
+ }
18556
+
18557
+ this.flags = null;
18558
+ this.fetchFlags();
18559
+
18560
+ this.trackedFeatures = new Set();
18561
+ };
18562
+
18563
+ FeatureFlagManager.prototype.getFullConfig = function() {
18564
+ var ffConfig = this.getMpConfig(FLAGS_CONFIG_KEY);
18565
+ if (!ffConfig) {
18566
+ // flags are completely off
18567
+ return {};
18568
+ } else if (_.isObject(ffConfig)) {
18569
+ return _.extend({}, CONFIG_DEFAULTS, ffConfig);
18570
+ } else {
18571
+ // config is non-object truthy value, return default
18572
+ return CONFIG_DEFAULTS;
18573
+ }
18574
+ };
18575
+
18576
+ FeatureFlagManager.prototype.getConfig = function(key) {
18577
+ return this.getFullConfig()[key];
18578
+ };
18579
+
18580
+ FeatureFlagManager.prototype.isSystemEnabled = function() {
18581
+ return !!this.getMpConfig(FLAGS_CONFIG_KEY);
18582
+ };
18583
+
18584
+ FeatureFlagManager.prototype.areFlagsReady = function() {
18585
+ if (!this.isSystemEnabled()) {
18586
+ logger.error('Feature Flags not enabled');
18587
+ }
18588
+ return !!this.flags;
18589
+ };
18590
+
18591
+ FeatureFlagManager.prototype.fetchFlags = function() {
18592
+ if (!this.isSystemEnabled()) {
18593
+ return;
18594
+ }
18595
+
18596
+ var distinctId = this.getDistinctId();
18597
+ logger.log('Fetching flags for distinct ID: ' + distinctId);
18598
+ var reqParams = {
18599
+ 'context': _.extend({'distinct_id': distinctId}, this.getConfig(CONFIG_CONTEXT))
18600
+ };
18601
+ this.fetchPromise = win['fetch'](this.getMpConfig('api_host') + '/' + this.getMpConfig('api_routes')['flags'], {
18602
+ 'method': 'POST',
18603
+ 'headers': {
18604
+ 'Authorization': 'Basic ' + btoa(this.getMpConfig('token') + ':'),
18605
+ 'Content-Type': 'application/octet-stream'
18606
+ },
18607
+ 'body': JSON.stringify(reqParams)
18608
+ }).then(function(response) {
18609
+ return response.json().then(function(responseBody) {
18610
+ var responseFlags = responseBody['flags'];
18611
+ if (!responseFlags) {
18612
+ throw new Error('No flags in API response');
18613
+ }
18614
+ var flags = new Map();
18615
+ _.each(responseFlags, function(data, key) {
18616
+ flags.set(key, {
18617
+ 'key': data['variant_key'],
18618
+ 'value': data['variant_value']
18619
+ });
18620
+ });
18621
+ this.flags = flags;
18622
+ }.bind(this)).catch(function(error) {
18623
+ logger.error(error);
18624
+ });
18625
+ }.bind(this)).catch(function() {});
18626
+ };
18627
+
18628
+ FeatureFlagManager.prototype.getVariant = function(featureName, fallback) {
18629
+ if (!this.fetchPromise) {
18630
+ return new Promise(function(resolve) {
18631
+ logger.critical('Feature Flags not initialized');
18632
+ resolve(fallback);
18633
+ });
18634
+ }
18635
+
18636
+ return this.fetchPromise.then(function() {
18637
+ return this.getVariantSync(featureName, fallback);
18638
+ }.bind(this)).catch(function(error) {
18639
+ logger.error(error);
18640
+ return fallback;
18641
+ });
18642
+ };
18643
+
18644
+ FeatureFlagManager.prototype.getVariantSync = function(featureName, fallback) {
18645
+ if (!this.areFlagsReady()) {
18646
+ logger.log('Flags not loaded yet');
18647
+ return fallback;
18648
+ }
18649
+ var feature = this.flags.get(featureName);
18650
+ if (!feature) {
18651
+ logger.log('No flag found: "' + featureName + '"');
18652
+ return fallback;
18653
+ }
18654
+ this.trackFeatureCheck(featureName, feature);
18655
+ return feature;
18656
+ };
18657
+
18658
+ FeatureFlagManager.prototype.getVariantValue = function(featureName, fallbackValue) {
18659
+ return this.getVariant(featureName, {'value': fallbackValue}).then(function(feature) {
18660
+ return feature['value'];
18661
+ }).catch(function(error) {
18662
+ logger.error(error);
18663
+ return fallbackValue;
18664
+ });
18665
+ };
18666
+
18667
+ // TODO remove deprecated method
18668
+ FeatureFlagManager.prototype.getFeatureData = function(featureName, fallbackValue) {
18669
+ logger.critical('mixpanel.flags.get_feature_data() is deprecated and will be removed in a future release. Use mixpanel.flags.get_variant_value() instead.');
18670
+ return this.getVariantValue(featureName, fallbackValue);
18671
+ };
18672
+
18673
+ FeatureFlagManager.prototype.getVariantValueSync = function(featureName, fallbackValue) {
18674
+ return this.getVariantSync(featureName, {'value': fallbackValue})['value'];
18675
+ };
18676
+
18677
+ FeatureFlagManager.prototype.isEnabled = function(featureName, fallbackValue) {
18678
+ return this.getVariantValue(featureName).then(function() {
18679
+ return this.isEnabledSync(featureName, fallbackValue);
18680
+ }.bind(this)).catch(function(error) {
18681
+ logger.error(error);
18682
+ return fallbackValue;
18683
+ });
18684
+ };
18685
+
18686
+ FeatureFlagManager.prototype.isEnabledSync = function(featureName, fallbackValue) {
18687
+ fallbackValue = fallbackValue || false;
18688
+ var val = this.getVariantValueSync(featureName, fallbackValue);
18689
+ if (val !== true && val !== false) {
18690
+ logger.error('Feature flag "' + featureName + '" value: ' + val + ' is not a boolean; returning fallback value: ' + fallbackValue);
18691
+ val = fallbackValue;
18692
+ }
18693
+ return val;
18694
+ };
18695
+
18696
+ FeatureFlagManager.prototype.trackFeatureCheck = function(featureName, feature) {
18697
+ if (this.trackedFeatures.has(featureName)) {
18698
+ return;
18699
+ }
18700
+ this.trackedFeatures.add(featureName);
18701
+ this.track('$experiment_started', {
18702
+ 'Experiment name': featureName,
18703
+ 'Variant name': feature['key'],
18704
+ '$experiment_type': 'feature_flag'
18705
+ });
18706
+ };
18707
+
18708
+ function minApisSupported() {
18709
+ return !!fetch &&
18710
+ typeof Promise !== 'undefined' &&
18711
+ typeof Map !== 'undefined' &&
18712
+ typeof Set !== 'undefined';
18713
+ }
18714
+
18715
+ safewrapClass(FeatureFlagManager);
18716
+
18717
+ FeatureFlagManager.prototype['are_flags_ready'] = FeatureFlagManager.prototype.areFlagsReady;
18718
+ FeatureFlagManager.prototype['get_variant'] = FeatureFlagManager.prototype.getVariant;
18719
+ FeatureFlagManager.prototype['get_variant_sync'] = FeatureFlagManager.prototype.getVariantSync;
18720
+ FeatureFlagManager.prototype['get_variant_value'] = FeatureFlagManager.prototype.getVariantValue;
18721
+ FeatureFlagManager.prototype['get_variant_value_sync'] = FeatureFlagManager.prototype.getVariantValueSync;
18722
+ FeatureFlagManager.prototype['is_enabled'] = FeatureFlagManager.prototype.isEnabled;
18723
+ FeatureFlagManager.prototype['is_enabled_sync'] = FeatureFlagManager.prototype.isEnabledSync;
18724
+
18725
+ // Deprecated method
18726
+ FeatureFlagManager.prototype['get_feature_data'] = FeatureFlagManager.prototype.getFeatureData;
18727
+
18521
18728
  /* eslint camelcase: "off" */
18522
18729
 
18523
18730
 
@@ -19212,18 +19419,8 @@ MixpanelPeople.prototype.union = addOptOutCheckMixpanelPeople(function(list_name
19212
19419
  * @param {Function} [callback] If provided, the callback will be called when the server responds
19213
19420
  * @deprecated
19214
19421
  */
19215
- MixpanelPeople.prototype.track_charge = addOptOutCheckMixpanelPeople(function(amount, properties, callback) {
19216
- if (!_.isNumber(amount)) {
19217
- amount = parseFloat(amount);
19218
- if (isNaN(amount)) {
19219
- console$1.error('Invalid value passed to mixpanel.people.track_charge - must be a number');
19220
- return;
19221
- }
19222
- }
19223
-
19224
- return this.append('$transactions', _.extend({
19225
- '$amount': amount
19226
- }, properties), callback);
19422
+ MixpanelPeople.prototype.track_charge = addOptOutCheckMixpanelPeople(function() {
19423
+ console$1.error('mixpanel.people.track_charge() is deprecated and no longer has any effect.');
19227
19424
  });
19228
19425
 
19229
19426
  /*
@@ -19922,10 +20119,11 @@ if (navigator['sendBeacon']) {
19922
20119
  }
19923
20120
 
19924
20121
  var DEFAULT_API_ROUTES = {
19925
- 'track': 'track/',
20122
+ 'track': 'track/',
19926
20123
  'engage': 'engage/',
19927
20124
  'groups': 'groups/',
19928
- 'record': 'record/'
20125
+ 'record': 'record/',
20126
+ 'flags': 'flags/'
19929
20127
  };
19930
20128
 
19931
20129
  /*
@@ -19934,6 +20132,7 @@ var DEFAULT_API_ROUTES = {
19934
20132
  var DEFAULT_CONFIG = {
19935
20133
  'api_host': 'https://api-js.mixpanel.com',
19936
20134
  'api_routes': DEFAULT_API_ROUTES,
20135
+ 'api_extra_query_params': {},
19937
20136
  'api_method': 'POST',
19938
20137
  'api_transport': 'XHR',
19939
20138
  'api_payload_format': PAYLOAD_TYPE_BASE64,
@@ -19943,6 +20142,7 @@ var DEFAULT_CONFIG = {
19943
20142
  'cross_site_cookie': false,
19944
20143
  'cross_subdomain_cookie': true,
19945
20144
  'error_reporter': NOOP_FUNC,
20145
+ 'flags': false,
19946
20146
  'persistence': 'cookie',
19947
20147
  'persistence_name': '',
19948
20148
  'cookie_domain': '',
@@ -19983,6 +20183,7 @@ var DEFAULT_CONFIG = {
19983
20183
  'record_block_selector': 'img, video',
19984
20184
  'record_canvas': false,
19985
20185
  'record_collect_fonts': false,
20186
+ 'record_heatmap_data': false,
19986
20187
  'record_idle_timeout_ms': 30 * 60 * 1000, // 30 minutes
19987
20188
  'record_mask_text_class': new RegExp('^(mp-mask|fs-mask|amp-mask|rr-mask|ph-mask)$'),
19988
20189
  'record_mask_text_selector': '*',
@@ -20198,6 +20399,14 @@ MixpanelLib.prototype._init = function(token, config, name) {
20198
20399
  }, '');
20199
20400
  }
20200
20401
 
20402
+ this.flags = new FeatureFlagManager({
20403
+ getConfigFunc: _.bind(this.get_config, this),
20404
+ getDistinctIdFunc: _.bind(this.get_distinct_id, this),
20405
+ trackingFunc: _.bind(this.track, this)
20406
+ });
20407
+ this.flags.init();
20408
+ this['flags'] = this.flags;
20409
+
20201
20410
  this.autocapture = new Autocapture(this);
20202
20411
  this.autocapture.init();
20203
20412
 
@@ -20323,6 +20532,10 @@ MixpanelLib.prototype.resume_session_recording = function () {
20323
20532
  }
20324
20533
  };
20325
20534
 
20535
+ MixpanelLib.prototype.is_recording_heatmap_data = function () {
20536
+ return this._get_session_replay_id() && this.get_config('record_heatmap_data');
20537
+ };
20538
+
20326
20539
  MixpanelLib.prototype.get_session_recording_properties = function () {
20327
20540
  var props = {};
20328
20541
  var replay_id = this._get_session_replay_id();
@@ -20507,6 +20720,8 @@ MixpanelLib.prototype._send_request = function(url, data, options, callback) {
20507
20720
  delete data['data'];
20508
20721
  }
20509
20722
 
20723
+ _.extend(data, this.get_config('api_extra_query_params'));
20724
+
20510
20725
  url += '?' + _.HTTPBuildQuery(data);
20511
20726
 
20512
20727
  var lib = this;
@@ -21404,6 +21619,11 @@ MixpanelLib.prototype.identify = function(
21404
21619
  '$anon_distinct_id': previous_distinct_id
21405
21620
  }, {skip_hooks: true});
21406
21621
  }
21622
+
21623
+ // check feature flags again if distinct id has changed
21624
+ if (new_distinct_id !== previous_distinct_id) {
21625
+ this.flags.fetchFlags();
21626
+ }
21407
21627
  };
21408
21628
 
21409
21629
  /**
@@ -21418,6 +21638,8 @@ MixpanelLib.prototype.reset = function() {
21418
21638
  'distinct_id': DEVICE_ID_PREFIX + uuid,
21419
21639
  '$device_id': uuid
21420
21640
  }, '');
21641
+ this.stop_session_recording();
21642
+ this._check_and_start_session_recording();
21421
21643
  };
21422
21644
 
21423
21645
  /**
@@ -21678,7 +21900,7 @@ MixpanelLib.prototype.set_config = function(config) {
21678
21900
  }
21679
21901
  Config.DEBUG = Config.DEBUG || this.get_config('debug');
21680
21902
 
21681
- if ('autocapture' in config && this.autocapture) {
21903
+ if (('autocapture' in config || 'record_heatmap_data' in config) && this.autocapture) {
21682
21904
  this.autocapture.init();
21683
21905
  }
21684
21906
  }