mixpanel-browser 2.63.0 → 2.64.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.
@@ -13945,7 +13945,7 @@
13945
13945
 
13946
13946
  var Config = {
13947
13947
  DEBUG: false,
13948
- LIB_VERSION: '2.63.0'
13948
+ LIB_VERSION: '2.64.0'
13949
13949
  };
13950
13950
 
13951
13951
  /* eslint camelcase: "off", eqeqeq: "off" */
@@ -16104,7 +16104,7 @@
16104
16104
  };
16105
16105
  }
16106
16106
 
16107
- var logger$5 = console_with_prefix('lock');
16107
+ var logger$6 = console_with_prefix('lock');
16108
16108
 
16109
16109
  /**
16110
16110
  * SharedLock: a mutex built on HTML5 localStorage, to ensure that only one browser
@@ -16156,7 +16156,7 @@
16156
16156
 
16157
16157
  var delay = function(cb) {
16158
16158
  if (new Date().getTime() - startTime > timeoutMS) {
16159
- logger$5.error('Timeout waiting for mutex on ' + key + '; clearing lock. [' + i + ']');
16159
+ logger$6.error('Timeout waiting for mutex on ' + key + '; clearing lock. [' + i + ']');
16160
16160
  storage.removeItem(keyZ);
16161
16161
  storage.removeItem(keyY);
16162
16162
  loop();
@@ -16299,7 +16299,7 @@
16299
16299
  }, this));
16300
16300
  };
16301
16301
 
16302
- var logger$4 = console_with_prefix('batch');
16302
+ var logger$5 = console_with_prefix('batch');
16303
16303
 
16304
16304
  /**
16305
16305
  * RequestQueue: queue for batching API requests with localStorage backup for retries.
@@ -16328,7 +16328,7 @@
16328
16328
  timeoutMS: options.sharedLockTimeoutMS,
16329
16329
  });
16330
16330
  }
16331
- this.reportError = options.errorReporter || _.bind(logger$4.error, logger$4);
16331
+ this.reportError = options.errorReporter || _.bind(logger$5.error, logger$5);
16332
16332
 
16333
16333
  this.pid = options.pid || null; // pass pid to test out storage lock contention scenarios
16334
16334
 
@@ -16661,7 +16661,7 @@
16661
16661
  // maximum interval between request retries after exponential backoff
16662
16662
  var MAX_RETRY_INTERVAL_MS = 10 * 60 * 1000; // 10 minutes
16663
16663
 
16664
- var logger$3 = console_with_prefix('batch');
16664
+ var logger$4 = console_with_prefix('batch');
16665
16665
 
16666
16666
  /**
16667
16667
  * RequestBatcher: manages the queueing, flushing, retry etc of requests of one
@@ -16789,7 +16789,7 @@
16789
16789
  */
16790
16790
  RequestBatcher.prototype.flush = function(options) {
16791
16791
  if (this.requestInProgress) {
16792
- logger$3.log('Flush: Request already in progress');
16792
+ logger$4.log('Flush: Request already in progress');
16793
16793
  return PromisePolyfill.resolve();
16794
16794
  }
16795
16795
 
@@ -16966,7 +16966,7 @@
16966
16966
  if (options.unloading) {
16967
16967
  requestOptions.transport = 'sendBeacon';
16968
16968
  }
16969
- logger$3.log('MIXPANEL REQUEST:', dataForRequest);
16969
+ logger$4.log('MIXPANEL REQUEST:', dataForRequest);
16970
16970
  return this.sendRequestPromise(dataForRequest, requestOptions).then(batchSendCallback);
16971
16971
  }, this))
16972
16972
  .catch(_.bind(function(err) {
@@ -16979,7 +16979,7 @@
16979
16979
  * Log error to global logger and optional user-defined logger.
16980
16980
  */
16981
16981
  RequestBatcher.prototype.reportError = function(msg, err) {
16982
- logger$3.error.apply(logger$3.error, arguments);
16982
+ logger$4.error.apply(logger$4.error, arguments);
16983
16983
  if (this.errorReporter) {
16984
16984
  try {
16985
16985
  if (!(err instanceof Error)) {
@@ -16987,7 +16987,7 @@
16987
16987
  }
16988
16988
  this.errorReporter(msg, err);
16989
16989
  } catch(err) {
16990
- logger$3.error(err);
16990
+ logger$4.error(err);
16991
16991
  }
16992
16992
  }
16993
16993
  };
@@ -17003,7 +17003,7 @@
17003
17003
 
17004
17004
  var RECORD_ENQUEUE_THROTTLE_MS = 250;
17005
17005
 
17006
- var logger$2 = console_with_prefix('recorder');
17006
+ var logger$3 = console_with_prefix('recorder');
17007
17007
  var CompressionStream = win['CompressionStream'];
17008
17008
 
17009
17009
  var RECORDER_BATCHER_LIB_CONFIG = {
@@ -17140,14 +17140,14 @@
17140
17140
  }
17141
17141
 
17142
17142
  if (this._stopRecording !== null) {
17143
- logger$2.log('Recording already in progress, skipping startRecording.');
17143
+ logger$3.log('Recording already in progress, skipping startRecording.');
17144
17144
  return;
17145
17145
  }
17146
17146
 
17147
17147
  this.recordMaxMs = this.getConfig('record_max_ms');
17148
17148
  if (this.recordMaxMs > MAX_RECORDING_MS) {
17149
17149
  this.recordMaxMs = MAX_RECORDING_MS;
17150
- logger$2.critical('record_max_ms cannot be greater than ' + MAX_RECORDING_MS + 'ms. Capping value.');
17150
+ logger$3.critical('record_max_ms cannot be greater than ' + MAX_RECORDING_MS + 'ms. Capping value.');
17151
17151
  }
17152
17152
 
17153
17153
  if (!this.maxExpires) {
@@ -17157,7 +17157,7 @@
17157
17157
  this.recordMinMs = this.getConfig('record_min_ms');
17158
17158
  if (this.recordMinMs > MAX_VALUE_FOR_MIN_RECORDING_MS) {
17159
17159
  this.recordMinMs = MAX_VALUE_FOR_MIN_RECORDING_MS;
17160
- logger$2.critical('record_min_ms cannot be greater than ' + MAX_VALUE_FOR_MIN_RECORDING_MS + 'ms. Capping value.');
17160
+ logger$3.critical('record_min_ms cannot be greater than ' + MAX_VALUE_FOR_MIN_RECORDING_MS + 'ms. Capping value.');
17161
17161
  }
17162
17162
 
17163
17163
  if (!this.replayStartTime) {
@@ -17441,14 +17441,14 @@
17441
17441
 
17442
17442
 
17443
17443
  SessionRecording.prototype.reportError = function(msg, err) {
17444
- logger$2.error.apply(logger$2.error, arguments);
17444
+ logger$3.error.apply(logger$3.error, arguments);
17445
17445
  try {
17446
17446
  if (!err && !(msg instanceof Error)) {
17447
17447
  msg = new Error(msg);
17448
17448
  }
17449
17449
  this.getConfig('error_reporter')(msg, err);
17450
17450
  } catch(err) {
17451
- logger$2.error(err);
17451
+ logger$3.error(err);
17452
17452
  }
17453
17453
  };
17454
17454
 
@@ -17544,7 +17544,7 @@
17544
17544
  .catch(this.handleError.bind(this));
17545
17545
  };
17546
17546
 
17547
- var logger$1 = console_with_prefix('recorder');
17547
+ var logger$2 = console_with_prefix('recorder');
17548
17548
 
17549
17549
  /**
17550
17550
  * Recorder API: bundles rrweb and and exposes methods to start and stop recordings.
@@ -17560,7 +17560,7 @@
17560
17560
  */
17561
17561
  this.recordingRegistry = new RecordingRegistry({
17562
17562
  mixpanelInstance: this.mixpanelInstance,
17563
- errorReporter: logger$1.error,
17563
+ errorReporter: logger$2.error,
17564
17564
  sharedLockStorage: sharedLockStorage
17565
17565
  });
17566
17566
  this._flushInactivePromise = this.recordingRegistry.flushInactiveRecordings();
@@ -17571,17 +17571,17 @@
17571
17571
  MixpanelRecorder.prototype.startRecording = function(options) {
17572
17572
  options = options || {};
17573
17573
  if (this.activeRecording && !this.activeRecording.isRrwebStopped()) {
17574
- logger$1.log('Recording already in progress, skipping startRecording.');
17574
+ logger$2.log('Recording already in progress, skipping startRecording.');
17575
17575
  return;
17576
17576
  }
17577
17577
 
17578
17578
  var onIdleTimeout = function () {
17579
- logger$1.log('Idle timeout reached, restarting recording.');
17579
+ logger$2.log('Idle timeout reached, restarting recording.');
17580
17580
  this.resetRecording();
17581
17581
  }.bind(this);
17582
17582
 
17583
17583
  var onMaxLengthReached = function () {
17584
- logger$1.log('Max recording length reached, stopping recording.');
17584
+ logger$2.log('Max recording length reached, stopping recording.');
17585
17585
  this.resetRecording();
17586
17586
  }.bind(this);
17587
17587
 
@@ -17644,7 +17644,7 @@
17644
17644
  } else if (startNewIfInactive) {
17645
17645
  return this.startRecording({shouldStopBatcher: false});
17646
17646
  } else {
17647
- logger$1.log('No resumable recording found.');
17647
+ logger$2.log('No resumable recording found.');
17648
17648
  return null;
17649
17649
  }
17650
17650
  }.bind(this));
@@ -17702,7 +17702,7 @@
17702
17702
  'href', 'name', 'role', 'title', 'type'
17703
17703
  ];
17704
17704
 
17705
- var logger = console_with_prefix('autocapture');
17705
+ var logger$1 = console_with_prefix('autocapture');
17706
17706
 
17707
17707
 
17708
17708
  function getClasses(el) {
@@ -17788,6 +17788,7 @@
17788
17788
  var blockSelectors = config.blockSelectors || [];
17789
17789
  var captureTextContent = config.captureTextContent || false;
17790
17790
  var captureExtraAttrs = config.captureExtraAttrs || [];
17791
+ var capturedForHeatMap = config.capturedForHeatMap || false;
17791
17792
 
17792
17793
  // convert array to set every time, as the config may have changed
17793
17794
  var blockAttrsSet = {};
@@ -17866,6 +17867,9 @@
17866
17867
  props['$' + prop] = ev[prop];
17867
17868
  }
17868
17869
  });
17870
+ if (capturedForHeatMap) {
17871
+ props['$captured_for_heatmap'] = true;
17872
+ }
17869
17873
  target = guessRealClickTarget(ev);
17870
17874
  }
17871
17875
  // prioritize text content from "real" click target if different from original target
@@ -17960,7 +17964,7 @@
17960
17964
  return false;
17961
17965
  }
17962
17966
  } catch (err) {
17963
- logger.critical('Error while checking element in allowElementCallback', err);
17967
+ logger$1.critical('Error while checking element in allowElementCallback', err);
17964
17968
  return false;
17965
17969
  }
17966
17970
  }
@@ -17977,7 +17981,7 @@
17977
17981
  return true;
17978
17982
  }
17979
17983
  } catch (err) {
17980
- logger.critical('Error while checking selector: ' + sel, err);
17984
+ logger$1.critical('Error while checking selector: ' + sel, err);
17981
17985
  }
17982
17986
  }
17983
17987
  return false;
@@ -17992,7 +17996,7 @@
17992
17996
  return true;
17993
17997
  }
17994
17998
  } catch (err) {
17995
- logger.critical('Error while checking element in blockElementCallback', err);
17999
+ logger$1.critical('Error while checking element in blockElementCallback', err);
17996
18000
  return true;
17997
18001
  }
17998
18002
  }
@@ -18006,7 +18010,7 @@
18006
18010
  return true;
18007
18011
  }
18008
18012
  } catch (err) {
18009
- logger.critical('Error while checking selector: ' + sel, err);
18013
+ logger$1.critical('Error while checking selector: ' + sel, err);
18010
18014
  }
18011
18015
  }
18012
18016
  }
@@ -18212,22 +18216,22 @@
18212
18216
  var CONFIG_TRACK_SCROLL = 'scroll';
18213
18217
  var CONFIG_TRACK_SUBMIT = 'submit';
18214
18218
 
18215
- var CONFIG_DEFAULTS = {};
18216
- CONFIG_DEFAULTS[CONFIG_ALLOW_SELECTORS] = [];
18217
- CONFIG_DEFAULTS[CONFIG_ALLOW_URL_REGEXES] = [];
18218
- CONFIG_DEFAULTS[CONFIG_BLOCK_ATTRS] = [];
18219
- CONFIG_DEFAULTS[CONFIG_BLOCK_ELEMENT_CALLBACK] = null;
18220
- CONFIG_DEFAULTS[CONFIG_BLOCK_SELECTORS] = [];
18221
- CONFIG_DEFAULTS[CONFIG_BLOCK_URL_REGEXES] = [];
18222
- CONFIG_DEFAULTS[CONFIG_CAPTURE_EXTRA_ATTRS] = [];
18223
- CONFIG_DEFAULTS[CONFIG_CAPTURE_TEXT_CONTENT] = false;
18224
- CONFIG_DEFAULTS[CONFIG_SCROLL_CAPTURE_ALL] = false;
18225
- CONFIG_DEFAULTS[CONFIG_SCROLL_CHECKPOINTS] = [25, 50, 75, 100];
18226
- CONFIG_DEFAULTS[CONFIG_TRACK_CLICK] = true;
18227
- CONFIG_DEFAULTS[CONFIG_TRACK_INPUT] = true;
18228
- CONFIG_DEFAULTS[CONFIG_TRACK_PAGEVIEW] = PAGEVIEW_OPTION_FULL_URL;
18229
- CONFIG_DEFAULTS[CONFIG_TRACK_SCROLL] = true;
18230
- CONFIG_DEFAULTS[CONFIG_TRACK_SUBMIT] = true;
18219
+ var CONFIG_DEFAULTS$1 = {};
18220
+ CONFIG_DEFAULTS$1[CONFIG_ALLOW_SELECTORS] = [];
18221
+ CONFIG_DEFAULTS$1[CONFIG_ALLOW_URL_REGEXES] = [];
18222
+ CONFIG_DEFAULTS$1[CONFIG_BLOCK_ATTRS] = [];
18223
+ CONFIG_DEFAULTS$1[CONFIG_BLOCK_ELEMENT_CALLBACK] = null;
18224
+ CONFIG_DEFAULTS$1[CONFIG_BLOCK_SELECTORS] = [];
18225
+ CONFIG_DEFAULTS$1[CONFIG_BLOCK_URL_REGEXES] = [];
18226
+ CONFIG_DEFAULTS$1[CONFIG_CAPTURE_EXTRA_ATTRS] = [];
18227
+ CONFIG_DEFAULTS$1[CONFIG_CAPTURE_TEXT_CONTENT] = false;
18228
+ CONFIG_DEFAULTS$1[CONFIG_SCROLL_CAPTURE_ALL] = false;
18229
+ CONFIG_DEFAULTS$1[CONFIG_SCROLL_CHECKPOINTS] = [25, 50, 75, 100];
18230
+ CONFIG_DEFAULTS$1[CONFIG_TRACK_CLICK] = true;
18231
+ CONFIG_DEFAULTS$1[CONFIG_TRACK_INPUT] = true;
18232
+ CONFIG_DEFAULTS$1[CONFIG_TRACK_PAGEVIEW] = PAGEVIEW_OPTION_FULL_URL;
18233
+ CONFIG_DEFAULTS$1[CONFIG_TRACK_SCROLL] = true;
18234
+ CONFIG_DEFAULTS$1[CONFIG_TRACK_SUBMIT] = true;
18231
18235
 
18232
18236
  var DEFAULT_PROPS = {
18233
18237
  '$mp_autocapture': true
@@ -18248,7 +18252,7 @@
18248
18252
 
18249
18253
  Autocapture.prototype.init = function() {
18250
18254
  if (!minDOMApisSupported()) {
18251
- logger.critical('Autocapture unavailable: missing required DOM APIs');
18255
+ logger$1.critical('Autocapture unavailable: missing required DOM APIs');
18252
18256
  return;
18253
18257
  }
18254
18258
 
@@ -18265,10 +18269,10 @@
18265
18269
  // Autocapture is completely off
18266
18270
  return {};
18267
18271
  } else if (_.isObject(autocaptureConfig)) {
18268
- return _.extend({}, CONFIG_DEFAULTS, autocaptureConfig);
18272
+ return _.extend({}, CONFIG_DEFAULTS$1, autocaptureConfig);
18269
18273
  } else {
18270
18274
  // Autocapture config is non-object truthy value, return default
18271
- return CONFIG_DEFAULTS;
18275
+ return CONFIG_DEFAULTS$1;
18272
18276
  }
18273
18277
  };
18274
18278
 
@@ -18292,7 +18296,7 @@
18292
18296
  break;
18293
18297
  }
18294
18298
  } catch (err) {
18295
- logger.critical('Error while checking block URL regex: ' + allowRegex, err);
18299
+ logger$1.critical('Error while checking block URL regex: ' + allowRegex, err);
18296
18300
  return true;
18297
18301
  }
18298
18302
  }
@@ -18313,7 +18317,7 @@
18313
18317
  return true;
18314
18318
  }
18315
18319
  } catch (err) {
18316
- logger.critical('Error while checking block URL regex: ' + blockUrlRegexes[i], err);
18320
+ logger$1.critical('Error while checking block URL regex: ' + blockUrlRegexes[i], err);
18317
18321
  return true;
18318
18322
  }
18319
18323
  }
@@ -18342,7 +18346,8 @@
18342
18346
  blockElementCallback: this.getConfig(CONFIG_BLOCK_ELEMENT_CALLBACK),
18343
18347
  blockSelectors: this.getConfig(CONFIG_BLOCK_SELECTORS),
18344
18348
  captureExtraAttrs: this.getConfig(CONFIG_CAPTURE_EXTRA_ATTRS),
18345
- captureTextContent: this.getConfig(CONFIG_CAPTURE_TEXT_CONTENT)
18349
+ captureTextContent: this.getConfig(CONFIG_CAPTURE_TEXT_CONTENT),
18350
+ capturedForHeatMap: mpEventName === MP_EV_CLICK && !this.getConfig(CONFIG_TRACK_CLICK) && this.mp.is_recording_heatmap_data(),
18346
18351
  });
18347
18352
  if (props) {
18348
18353
  _.extend(props, DEFAULT_PROPS);
@@ -18353,13 +18358,13 @@
18353
18358
  Autocapture.prototype.initClickTracking = function() {
18354
18359
  win.removeEventListener(EV_CLICK, this.listenerClick);
18355
18360
 
18356
- if (!this.getConfig(CONFIG_TRACK_CLICK)) {
18361
+ if (!this.getConfig(CONFIG_TRACK_CLICK) && !this.mp.get_config('record_heatmap_data')) {
18357
18362
  return;
18358
18363
  }
18359
- logger.log('Initializing click tracking');
18364
+ logger$1.log('Initializing click tracking');
18360
18365
 
18361
18366
  this.listenerClick = win.addEventListener(EV_CLICK, function(ev) {
18362
- if (!this.getConfig(CONFIG_TRACK_CLICK)) {
18367
+ if (!this.getConfig(CONFIG_TRACK_CLICK) && !this.mp.is_recording_heatmap_data()) {
18363
18368
  return;
18364
18369
  }
18365
18370
  this.trackDomEvent(ev, MP_EV_CLICK);
@@ -18372,7 +18377,7 @@
18372
18377
  if (!this.getConfig(CONFIG_TRACK_INPUT)) {
18373
18378
  return;
18374
18379
  }
18375
- logger.log('Initializing input tracking');
18380
+ logger$1.log('Initializing input tracking');
18376
18381
 
18377
18382
  this.listenerChange = win.addEventListener(EV_CHANGE, function(ev) {
18378
18383
  if (!this.getConfig(CONFIG_TRACK_INPUT)) {
@@ -18390,7 +18395,7 @@
18390
18395
  if (!this.pageviewTrackingConfig()) {
18391
18396
  return;
18392
18397
  }
18393
- logger.log('Initializing pageview tracking');
18398
+ logger$1.log('Initializing pageview tracking');
18394
18399
 
18395
18400
  var previousTrackedUrl = '';
18396
18401
  var tracked = false;
@@ -18445,7 +18450,7 @@
18445
18450
  }
18446
18451
  if (didPathChange) {
18447
18452
  this.lastScrollCheckpoint = 0;
18448
- logger.log('Path change: re-initializing scroll depth checkpoints');
18453
+ logger$1.log('Path change: re-initializing scroll depth checkpoints');
18449
18454
  }
18450
18455
  }
18451
18456
  }.bind(this)));
@@ -18457,7 +18462,7 @@
18457
18462
  if (!this.getConfig(CONFIG_TRACK_SCROLL)) {
18458
18463
  return;
18459
18464
  }
18460
- logger.log('Initializing scroll tracking');
18465
+ logger$1.log('Initializing scroll tracking');
18461
18466
  this.lastScrollCheckpoint = 0;
18462
18467
 
18463
18468
  this.listenerScroll = win.addEventListener(EV_SCROLLEND, safewrap(function() {
@@ -18494,7 +18499,7 @@
18494
18499
  }
18495
18500
  }
18496
18501
  } catch (err) {
18497
- logger.critical('Error while calculating scroll percentage', err);
18502
+ logger$1.critical('Error while calculating scroll percentage', err);
18498
18503
  }
18499
18504
  if (shouldTrack) {
18500
18505
  this.mp.track(MP_EV_SCROLL, props);
@@ -18508,7 +18513,7 @@
18508
18513
  if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
18509
18514
  return;
18510
18515
  }
18511
- logger.log('Initializing submit tracking');
18516
+ logger$1.log('Initializing submit tracking');
18512
18517
 
18513
18518
  this.listenerSubmit = win.addEventListener(EV_SUBMIT, function(ev) {
18514
18519
  if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
@@ -18521,6 +18526,193 @@
18521
18526
  // TODO integrate error_reporter from mixpanel instance
18522
18527
  safewrapClass(Autocapture);
18523
18528
 
18529
+ var fetch = win['fetch'];
18530
+ var logger = console_with_prefix('flags');
18531
+
18532
+ var FLAGS_CONFIG_KEY = 'flags';
18533
+
18534
+ var CONFIG_CONTEXT = 'context';
18535
+ var CONFIG_DEFAULTS = {};
18536
+ CONFIG_DEFAULTS[CONFIG_CONTEXT] = {};
18537
+
18538
+ /**
18539
+ * FeatureFlagManager: support for Mixpanel's feature flagging product
18540
+ * @constructor
18541
+ */
18542
+ var FeatureFlagManager = function(initOptions) {
18543
+ this.getMpConfig = initOptions.getConfigFunc;
18544
+ this.getDistinctId = initOptions.getDistinctIdFunc;
18545
+ this.track = initOptions.trackingFunc;
18546
+ };
18547
+
18548
+ FeatureFlagManager.prototype.init = function() {
18549
+ if (!minApisSupported()) {
18550
+ logger.critical('Feature Flags unavailable: missing minimum required APIs');
18551
+ return;
18552
+ }
18553
+
18554
+ this.flags = null;
18555
+ this.fetchFlags();
18556
+
18557
+ this.trackedFeatures = new Set();
18558
+ };
18559
+
18560
+ FeatureFlagManager.prototype.getFullConfig = function() {
18561
+ var ffConfig = this.getMpConfig(FLAGS_CONFIG_KEY);
18562
+ if (!ffConfig) {
18563
+ // flags are completely off
18564
+ return {};
18565
+ } else if (_.isObject(ffConfig)) {
18566
+ return _.extend({}, CONFIG_DEFAULTS, ffConfig);
18567
+ } else {
18568
+ // config is non-object truthy value, return default
18569
+ return CONFIG_DEFAULTS;
18570
+ }
18571
+ };
18572
+
18573
+ FeatureFlagManager.prototype.getConfig = function(key) {
18574
+ return this.getFullConfig()[key];
18575
+ };
18576
+
18577
+ FeatureFlagManager.prototype.isEnabled = function() {
18578
+ return !!this.getMpConfig(FLAGS_CONFIG_KEY);
18579
+ };
18580
+
18581
+ FeatureFlagManager.prototype.areFeaturesReady = function() {
18582
+ if (!this.isEnabled()) {
18583
+ logger.error('Feature Flags not enabled');
18584
+ }
18585
+ return !!this.flags;
18586
+ };
18587
+
18588
+ FeatureFlagManager.prototype.fetchFlags = function() {
18589
+ if (!this.isEnabled()) {
18590
+ return;
18591
+ }
18592
+
18593
+ var distinctId = this.getDistinctId();
18594
+ logger.log('Fetching flags for distinct ID: ' + distinctId);
18595
+ var reqParams = {
18596
+ 'context': _.extend({'distinct_id': distinctId}, this.getConfig(CONFIG_CONTEXT))
18597
+ };
18598
+ this.fetchPromise = win['fetch'](this.getMpConfig('api_host') + '/' + this.getMpConfig('api_routes')['flags'], {
18599
+ 'method': 'POST',
18600
+ 'headers': {
18601
+ 'Authorization': 'Basic ' + btoa(this.getMpConfig('token') + ':'),
18602
+ 'Content-Type': 'application/octet-stream'
18603
+ },
18604
+ 'body': JSON.stringify(reqParams)
18605
+ }).then(function(response) {
18606
+ return response.json().then(function(responseBody) {
18607
+ var responseFlags = responseBody['flags'];
18608
+ if (!responseFlags) {
18609
+ throw new Error('No flags in API response');
18610
+ }
18611
+ var flags = new Map();
18612
+ _.each(responseFlags, function(data, key) {
18613
+ flags.set(key, {
18614
+ 'key': data['variant_key'],
18615
+ 'data': data['variant_value']
18616
+ });
18617
+ });
18618
+ this.flags = flags;
18619
+ }.bind(this)).catch(function(error) {
18620
+ logger.error(error);
18621
+ });
18622
+ }.bind(this)).catch(function() {});
18623
+ };
18624
+
18625
+ FeatureFlagManager.prototype.getFeature = function(featureName, fallback) {
18626
+ if (!this.fetchPromise) {
18627
+ return new Promise(function(resolve) {
18628
+ logger.critical('Feature Flags not initialized');
18629
+ resolve(fallback);
18630
+ });
18631
+ }
18632
+
18633
+ return this.fetchPromise.then(function() {
18634
+ return this.getFeatureSync(featureName, fallback);
18635
+ }.bind(this)).catch(function(error) {
18636
+ logger.error(error);
18637
+ return fallback;
18638
+ });
18639
+ };
18640
+
18641
+ FeatureFlagManager.prototype.getFeatureSync = function(featureName, fallback) {
18642
+ if (!this.areFeaturesReady()) {
18643
+ logger.log('Flags not loaded yet');
18644
+ return fallback;
18645
+ }
18646
+ var feature = this.flags.get(featureName);
18647
+ if (!feature) {
18648
+ logger.log('No flag found: "' + featureName + '"');
18649
+ return fallback;
18650
+ }
18651
+ this.trackFeatureCheck(featureName, feature);
18652
+ return feature;
18653
+ };
18654
+
18655
+ FeatureFlagManager.prototype.getFeatureData = function(featureName, fallbackValue) {
18656
+ return this.getFeature(featureName, {'data': fallbackValue}).then(function(feature) {
18657
+ return feature['data'];
18658
+ }).catch(function(error) {
18659
+ logger.error(error);
18660
+ return fallbackValue;
18661
+ });
18662
+ };
18663
+
18664
+ FeatureFlagManager.prototype.getFeatureDataSync = function(featureName, fallbackValue) {
18665
+ return this.getFeatureSync(featureName, {'data': fallbackValue})['data'];
18666
+ };
18667
+
18668
+ FeatureFlagManager.prototype.isFeatureEnabled = function(featureName, fallbackValue) {
18669
+ return this.getFeatureData(featureName).then(function() {
18670
+ return this.isFeatureEnabledSync(featureName, fallbackValue);
18671
+ }.bind(this)).catch(function(error) {
18672
+ logger.error(error);
18673
+ return fallbackValue;
18674
+ });
18675
+ };
18676
+
18677
+ FeatureFlagManager.prototype.isFeatureEnabledSync = function(featureName, fallbackValue) {
18678
+ fallbackValue = fallbackValue || false;
18679
+ var val = this.getFeatureDataSync(featureName, fallbackValue);
18680
+ if (val !== true && val !== false) {
18681
+ logger.error('Feature flag "' + featureName + '" value: ' + val + ' is not a boolean; returning fallback value: ' + fallbackValue);
18682
+ val = fallbackValue;
18683
+ }
18684
+ return val;
18685
+ };
18686
+
18687
+ FeatureFlagManager.prototype.trackFeatureCheck = function(featureName, feature) {
18688
+ if (this.trackedFeatures.has(featureName)) {
18689
+ return;
18690
+ }
18691
+ this.trackedFeatures.add(featureName);
18692
+ this.track('$experiment_started', {
18693
+ 'Experiment name': featureName,
18694
+ 'Variant name': feature['key'],
18695
+ '$experiment_type': 'feature_flag'
18696
+ });
18697
+ };
18698
+
18699
+ function minApisSupported() {
18700
+ return !!fetch &&
18701
+ typeof Promise !== 'undefined' &&
18702
+ typeof Map !== 'undefined' &&
18703
+ typeof Set !== 'undefined';
18704
+ }
18705
+
18706
+ safewrapClass(FeatureFlagManager);
18707
+
18708
+ FeatureFlagManager.prototype['are_features_ready'] = FeatureFlagManager.prototype.areFeaturesReady;
18709
+ FeatureFlagManager.prototype['get_feature'] = FeatureFlagManager.prototype.getFeature;
18710
+ FeatureFlagManager.prototype['get_feature_data'] = FeatureFlagManager.prototype.getFeatureData;
18711
+ FeatureFlagManager.prototype['get_feature_data_sync'] = FeatureFlagManager.prototype.getFeatureDataSync;
18712
+ FeatureFlagManager.prototype['get_feature_sync'] = FeatureFlagManager.prototype.getFeatureSync;
18713
+ FeatureFlagManager.prototype['is_feature_enabled'] = FeatureFlagManager.prototype.isFeatureEnabled;
18714
+ FeatureFlagManager.prototype['is_feature_enabled_sync'] = FeatureFlagManager.prototype.isFeatureEnabledSync;
18715
+
18524
18716
  /* eslint camelcase: "off" */
18525
18717
 
18526
18718
 
@@ -19925,10 +20117,11 @@
19925
20117
  }
19926
20118
 
19927
20119
  var DEFAULT_API_ROUTES = {
19928
- 'track': 'track/',
20120
+ 'track': 'track/',
19929
20121
  'engage': 'engage/',
19930
20122
  'groups': 'groups/',
19931
- 'record': 'record/'
20123
+ 'record': 'record/',
20124
+ 'flags': 'flags/'
19932
20125
  };
19933
20126
 
19934
20127
  /*
@@ -19946,6 +20139,7 @@
19946
20139
  'cross_site_cookie': false,
19947
20140
  'cross_subdomain_cookie': true,
19948
20141
  'error_reporter': NOOP_FUNC,
20142
+ 'flags': false,
19949
20143
  'persistence': 'cookie',
19950
20144
  'persistence_name': '',
19951
20145
  'cookie_domain': '',
@@ -19986,6 +20180,7 @@
19986
20180
  'record_block_selector': 'img, video',
19987
20181
  'record_canvas': false,
19988
20182
  'record_collect_fonts': false,
20183
+ 'record_heatmap_data': false,
19989
20184
  'record_idle_timeout_ms': 30 * 60 * 1000, // 30 minutes
19990
20185
  'record_mask_text_class': new RegExp('^(mp-mask|fs-mask|amp-mask|rr-mask|ph-mask)$'),
19991
20186
  'record_mask_text_selector': '*',
@@ -20201,6 +20396,14 @@
20201
20396
  }, '');
20202
20397
  }
20203
20398
 
20399
+ this.flags = new FeatureFlagManager({
20400
+ getConfigFunc: _.bind(this.get_config, this),
20401
+ getDistinctIdFunc: _.bind(this.get_distinct_id, this),
20402
+ trackingFunc: _.bind(this.track, this)
20403
+ });
20404
+ this.flags.init();
20405
+ this['flags'] = this.flags;
20406
+
20204
20407
  this.autocapture = new Autocapture(this);
20205
20408
  this.autocapture.init();
20206
20409
 
@@ -20326,6 +20529,10 @@
20326
20529
  }
20327
20530
  };
20328
20531
 
20532
+ MixpanelLib.prototype.is_recording_heatmap_data = function () {
20533
+ return this._get_session_replay_id() && this.get_config('record_heatmap_data');
20534
+ };
20535
+
20329
20536
  MixpanelLib.prototype.get_session_recording_properties = function () {
20330
20537
  var props = {};
20331
20538
  var replay_id = this._get_session_replay_id();
@@ -21407,6 +21614,11 @@
21407
21614
  '$anon_distinct_id': previous_distinct_id
21408
21615
  }, {skip_hooks: true});
21409
21616
  }
21617
+
21618
+ // check feature flags again if distinct id has changed
21619
+ if (new_distinct_id !== previous_distinct_id) {
21620
+ this.flags.fetchFlags();
21621
+ }
21410
21622
  };
21411
21623
 
21412
21624
  /**
@@ -21681,7 +21893,7 @@
21681
21893
  }
21682
21894
  Config.DEBUG = Config.DEBUG || this.get_config('debug');
21683
21895
 
21684
- if ('autocapture' in config && this.autocapture) {
21896
+ if (('autocapture' in config || 'record_heatmap_data' in config) && this.autocapture) {
21685
21897
  this.autocapture.init();
21686
21898
  }
21687
21899
  }