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