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.
@@ -13948,7 +13948,7 @@
13948
13948
 
13949
13949
  var Config = {
13950
13950
  DEBUG: false,
13951
- LIB_VERSION: '2.63.0'
13951
+ LIB_VERSION: '2.64.0'
13952
13952
  };
13953
13953
 
13954
13954
  /* eslint camelcase: "off", eqeqeq: "off" */
@@ -16107,7 +16107,7 @@
16107
16107
  };
16108
16108
  }
16109
16109
 
16110
- var logger$5 = console_with_prefix('lock');
16110
+ var logger$6 = console_with_prefix('lock');
16111
16111
 
16112
16112
  /**
16113
16113
  * SharedLock: a mutex built on HTML5 localStorage, to ensure that only one browser
@@ -16159,7 +16159,7 @@
16159
16159
 
16160
16160
  var delay = function(cb) {
16161
16161
  if (new Date().getTime() - startTime > timeoutMS) {
16162
- 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 + ']');
16163
16163
  storage.removeItem(keyZ);
16164
16164
  storage.removeItem(keyY);
16165
16165
  loop();
@@ -16302,7 +16302,7 @@
16302
16302
  }, this));
16303
16303
  };
16304
16304
 
16305
- var logger$4 = console_with_prefix('batch');
16305
+ var logger$5 = console_with_prefix('batch');
16306
16306
 
16307
16307
  /**
16308
16308
  * RequestQueue: queue for batching API requests with localStorage backup for retries.
@@ -16331,7 +16331,7 @@
16331
16331
  timeoutMS: options.sharedLockTimeoutMS,
16332
16332
  });
16333
16333
  }
16334
- this.reportError = options.errorReporter || _.bind(logger$4.error, logger$4);
16334
+ this.reportError = options.errorReporter || _.bind(logger$5.error, logger$5);
16335
16335
 
16336
16336
  this.pid = options.pid || null; // pass pid to test out storage lock contention scenarios
16337
16337
 
@@ -16664,7 +16664,7 @@
16664
16664
  // maximum interval between request retries after exponential backoff
16665
16665
  var MAX_RETRY_INTERVAL_MS = 10 * 60 * 1000; // 10 minutes
16666
16666
 
16667
- var logger$3 = console_with_prefix('batch');
16667
+ var logger$4 = console_with_prefix('batch');
16668
16668
 
16669
16669
  /**
16670
16670
  * RequestBatcher: manages the queueing, flushing, retry etc of requests of one
@@ -16792,7 +16792,7 @@
16792
16792
  */
16793
16793
  RequestBatcher.prototype.flush = function(options) {
16794
16794
  if (this.requestInProgress) {
16795
- logger$3.log('Flush: Request already in progress');
16795
+ logger$4.log('Flush: Request already in progress');
16796
16796
  return PromisePolyfill.resolve();
16797
16797
  }
16798
16798
 
@@ -16969,7 +16969,7 @@
16969
16969
  if (options.unloading) {
16970
16970
  requestOptions.transport = 'sendBeacon';
16971
16971
  }
16972
- logger$3.log('MIXPANEL REQUEST:', dataForRequest);
16972
+ logger$4.log('MIXPANEL REQUEST:', dataForRequest);
16973
16973
  return this.sendRequestPromise(dataForRequest, requestOptions).then(batchSendCallback);
16974
16974
  }, this))
16975
16975
  .catch(_.bind(function(err) {
@@ -16982,7 +16982,7 @@
16982
16982
  * Log error to global logger and optional user-defined logger.
16983
16983
  */
16984
16984
  RequestBatcher.prototype.reportError = function(msg, err) {
16985
- logger$3.error.apply(logger$3.error, arguments);
16985
+ logger$4.error.apply(logger$4.error, arguments);
16986
16986
  if (this.errorReporter) {
16987
16987
  try {
16988
16988
  if (!(err instanceof Error)) {
@@ -16990,7 +16990,7 @@
16990
16990
  }
16991
16991
  this.errorReporter(msg, err);
16992
16992
  } catch(err) {
16993
- logger$3.error(err);
16993
+ logger$4.error(err);
16994
16994
  }
16995
16995
  }
16996
16996
  };
@@ -17006,7 +17006,7 @@
17006
17006
 
17007
17007
  var RECORD_ENQUEUE_THROTTLE_MS = 250;
17008
17008
 
17009
- var logger$2 = console_with_prefix('recorder');
17009
+ var logger$3 = console_with_prefix('recorder');
17010
17010
  var CompressionStream = win['CompressionStream'];
17011
17011
 
17012
17012
  var RECORDER_BATCHER_LIB_CONFIG = {
@@ -17143,14 +17143,14 @@
17143
17143
  }
17144
17144
 
17145
17145
  if (this._stopRecording !== null) {
17146
- logger$2.log('Recording already in progress, skipping startRecording.');
17146
+ logger$3.log('Recording already in progress, skipping startRecording.');
17147
17147
  return;
17148
17148
  }
17149
17149
 
17150
17150
  this.recordMaxMs = this.getConfig('record_max_ms');
17151
17151
  if (this.recordMaxMs > MAX_RECORDING_MS) {
17152
17152
  this.recordMaxMs = MAX_RECORDING_MS;
17153
- 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.');
17154
17154
  }
17155
17155
 
17156
17156
  if (!this.maxExpires) {
@@ -17160,7 +17160,7 @@
17160
17160
  this.recordMinMs = this.getConfig('record_min_ms');
17161
17161
  if (this.recordMinMs > MAX_VALUE_FOR_MIN_RECORDING_MS) {
17162
17162
  this.recordMinMs = MAX_VALUE_FOR_MIN_RECORDING_MS;
17163
- 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.');
17164
17164
  }
17165
17165
 
17166
17166
  if (!this.replayStartTime) {
@@ -17444,14 +17444,14 @@
17444
17444
 
17445
17445
 
17446
17446
  SessionRecording.prototype.reportError = function(msg, err) {
17447
- logger$2.error.apply(logger$2.error, arguments);
17447
+ logger$3.error.apply(logger$3.error, arguments);
17448
17448
  try {
17449
17449
  if (!err && !(msg instanceof Error)) {
17450
17450
  msg = new Error(msg);
17451
17451
  }
17452
17452
  this.getConfig('error_reporter')(msg, err);
17453
17453
  } catch(err) {
17454
- logger$2.error(err);
17454
+ logger$3.error(err);
17455
17455
  }
17456
17456
  };
17457
17457
 
@@ -17547,7 +17547,7 @@
17547
17547
  .catch(this.handleError.bind(this));
17548
17548
  };
17549
17549
 
17550
- var logger$1 = console_with_prefix('recorder');
17550
+ var logger$2 = console_with_prefix('recorder');
17551
17551
 
17552
17552
  /**
17553
17553
  * Recorder API: bundles rrweb and and exposes methods to start and stop recordings.
@@ -17563,7 +17563,7 @@
17563
17563
  */
17564
17564
  this.recordingRegistry = new RecordingRegistry({
17565
17565
  mixpanelInstance: this.mixpanelInstance,
17566
- errorReporter: logger$1.error,
17566
+ errorReporter: logger$2.error,
17567
17567
  sharedLockStorage: sharedLockStorage
17568
17568
  });
17569
17569
  this._flushInactivePromise = this.recordingRegistry.flushInactiveRecordings();
@@ -17574,17 +17574,17 @@
17574
17574
  MixpanelRecorder.prototype.startRecording = function(options) {
17575
17575
  options = options || {};
17576
17576
  if (this.activeRecording && !this.activeRecording.isRrwebStopped()) {
17577
- logger$1.log('Recording already in progress, skipping startRecording.');
17577
+ logger$2.log('Recording already in progress, skipping startRecording.');
17578
17578
  return;
17579
17579
  }
17580
17580
 
17581
17581
  var onIdleTimeout = function () {
17582
- logger$1.log('Idle timeout reached, restarting recording.');
17582
+ logger$2.log('Idle timeout reached, restarting recording.');
17583
17583
  this.resetRecording();
17584
17584
  }.bind(this);
17585
17585
 
17586
17586
  var onMaxLengthReached = function () {
17587
- logger$1.log('Max recording length reached, stopping recording.');
17587
+ logger$2.log('Max recording length reached, stopping recording.');
17588
17588
  this.resetRecording();
17589
17589
  }.bind(this);
17590
17590
 
@@ -17647,7 +17647,7 @@
17647
17647
  } else if (startNewIfInactive) {
17648
17648
  return this.startRecording({shouldStopBatcher: false});
17649
17649
  } else {
17650
- logger$1.log('No resumable recording found.');
17650
+ logger$2.log('No resumable recording found.');
17651
17651
  return null;
17652
17652
  }
17653
17653
  }.bind(this));
@@ -17705,7 +17705,7 @@
17705
17705
  'href', 'name', 'role', 'title', 'type'
17706
17706
  ];
17707
17707
 
17708
- var logger = console_with_prefix('autocapture');
17708
+ var logger$1 = console_with_prefix('autocapture');
17709
17709
 
17710
17710
 
17711
17711
  function getClasses(el) {
@@ -17791,6 +17791,7 @@
17791
17791
  var blockSelectors = config.blockSelectors || [];
17792
17792
  var captureTextContent = config.captureTextContent || false;
17793
17793
  var captureExtraAttrs = config.captureExtraAttrs || [];
17794
+ var capturedForHeatMap = config.capturedForHeatMap || false;
17794
17795
 
17795
17796
  // convert array to set every time, as the config may have changed
17796
17797
  var blockAttrsSet = {};
@@ -17869,6 +17870,9 @@
17869
17870
  props['$' + prop] = ev[prop];
17870
17871
  }
17871
17872
  });
17873
+ if (capturedForHeatMap) {
17874
+ props['$captured_for_heatmap'] = true;
17875
+ }
17872
17876
  target = guessRealClickTarget(ev);
17873
17877
  }
17874
17878
  // prioritize text content from "real" click target if different from original target
@@ -17963,7 +17967,7 @@
17963
17967
  return false;
17964
17968
  }
17965
17969
  } catch (err) {
17966
- logger.critical('Error while checking element in allowElementCallback', err);
17970
+ logger$1.critical('Error while checking element in allowElementCallback', err);
17967
17971
  return false;
17968
17972
  }
17969
17973
  }
@@ -17980,7 +17984,7 @@
17980
17984
  return true;
17981
17985
  }
17982
17986
  } catch (err) {
17983
- logger.critical('Error while checking selector: ' + sel, err);
17987
+ logger$1.critical('Error while checking selector: ' + sel, err);
17984
17988
  }
17985
17989
  }
17986
17990
  return false;
@@ -17995,7 +17999,7 @@
17995
17999
  return true;
17996
18000
  }
17997
18001
  } catch (err) {
17998
- logger.critical('Error while checking element in blockElementCallback', err);
18002
+ logger$1.critical('Error while checking element in blockElementCallback', err);
17999
18003
  return true;
18000
18004
  }
18001
18005
  }
@@ -18009,7 +18013,7 @@
18009
18013
  return true;
18010
18014
  }
18011
18015
  } catch (err) {
18012
- logger.critical('Error while checking selector: ' + sel, err);
18016
+ logger$1.critical('Error while checking selector: ' + sel, err);
18013
18017
  }
18014
18018
  }
18015
18019
  }
@@ -18215,22 +18219,22 @@
18215
18219
  var CONFIG_TRACK_SCROLL = 'scroll';
18216
18220
  var CONFIG_TRACK_SUBMIT = 'submit';
18217
18221
 
18218
- var CONFIG_DEFAULTS = {};
18219
- CONFIG_DEFAULTS[CONFIG_ALLOW_SELECTORS] = [];
18220
- CONFIG_DEFAULTS[CONFIG_ALLOW_URL_REGEXES] = [];
18221
- CONFIG_DEFAULTS[CONFIG_BLOCK_ATTRS] = [];
18222
- CONFIG_DEFAULTS[CONFIG_BLOCK_ELEMENT_CALLBACK] = null;
18223
- CONFIG_DEFAULTS[CONFIG_BLOCK_SELECTORS] = [];
18224
- CONFIG_DEFAULTS[CONFIG_BLOCK_URL_REGEXES] = [];
18225
- CONFIG_DEFAULTS[CONFIG_CAPTURE_EXTRA_ATTRS] = [];
18226
- CONFIG_DEFAULTS[CONFIG_CAPTURE_TEXT_CONTENT] = false;
18227
- CONFIG_DEFAULTS[CONFIG_SCROLL_CAPTURE_ALL] = false;
18228
- CONFIG_DEFAULTS[CONFIG_SCROLL_CHECKPOINTS] = [25, 50, 75, 100];
18229
- CONFIG_DEFAULTS[CONFIG_TRACK_CLICK] = true;
18230
- CONFIG_DEFAULTS[CONFIG_TRACK_INPUT] = true;
18231
- CONFIG_DEFAULTS[CONFIG_TRACK_PAGEVIEW] = PAGEVIEW_OPTION_FULL_URL;
18232
- CONFIG_DEFAULTS[CONFIG_TRACK_SCROLL] = true;
18233
- 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;
18234
18238
 
18235
18239
  var DEFAULT_PROPS = {
18236
18240
  '$mp_autocapture': true
@@ -18251,7 +18255,7 @@
18251
18255
 
18252
18256
  Autocapture.prototype.init = function() {
18253
18257
  if (!minDOMApisSupported()) {
18254
- logger.critical('Autocapture unavailable: missing required DOM APIs');
18258
+ logger$1.critical('Autocapture unavailable: missing required DOM APIs');
18255
18259
  return;
18256
18260
  }
18257
18261
 
@@ -18268,10 +18272,10 @@
18268
18272
  // Autocapture is completely off
18269
18273
  return {};
18270
18274
  } else if (_.isObject(autocaptureConfig)) {
18271
- return _.extend({}, CONFIG_DEFAULTS, autocaptureConfig);
18275
+ return _.extend({}, CONFIG_DEFAULTS$1, autocaptureConfig);
18272
18276
  } else {
18273
18277
  // Autocapture config is non-object truthy value, return default
18274
- return CONFIG_DEFAULTS;
18278
+ return CONFIG_DEFAULTS$1;
18275
18279
  }
18276
18280
  };
18277
18281
 
@@ -18295,7 +18299,7 @@
18295
18299
  break;
18296
18300
  }
18297
18301
  } catch (err) {
18298
- logger.critical('Error while checking block URL regex: ' + allowRegex, err);
18302
+ logger$1.critical('Error while checking block URL regex: ' + allowRegex, err);
18299
18303
  return true;
18300
18304
  }
18301
18305
  }
@@ -18316,7 +18320,7 @@
18316
18320
  return true;
18317
18321
  }
18318
18322
  } catch (err) {
18319
- logger.critical('Error while checking block URL regex: ' + blockUrlRegexes[i], err);
18323
+ logger$1.critical('Error while checking block URL regex: ' + blockUrlRegexes[i], err);
18320
18324
  return true;
18321
18325
  }
18322
18326
  }
@@ -18345,7 +18349,8 @@
18345
18349
  blockElementCallback: this.getConfig(CONFIG_BLOCK_ELEMENT_CALLBACK),
18346
18350
  blockSelectors: this.getConfig(CONFIG_BLOCK_SELECTORS),
18347
18351
  captureExtraAttrs: this.getConfig(CONFIG_CAPTURE_EXTRA_ATTRS),
18348
- 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(),
18349
18354
  });
18350
18355
  if (props) {
18351
18356
  _.extend(props, DEFAULT_PROPS);
@@ -18356,13 +18361,13 @@
18356
18361
  Autocapture.prototype.initClickTracking = function() {
18357
18362
  win.removeEventListener(EV_CLICK, this.listenerClick);
18358
18363
 
18359
- if (!this.getConfig(CONFIG_TRACK_CLICK)) {
18364
+ if (!this.getConfig(CONFIG_TRACK_CLICK) && !this.mp.get_config('record_heatmap_data')) {
18360
18365
  return;
18361
18366
  }
18362
- logger.log('Initializing click tracking');
18367
+ logger$1.log('Initializing click tracking');
18363
18368
 
18364
18369
  this.listenerClick = win.addEventListener(EV_CLICK, function(ev) {
18365
- if (!this.getConfig(CONFIG_TRACK_CLICK)) {
18370
+ if (!this.getConfig(CONFIG_TRACK_CLICK) && !this.mp.is_recording_heatmap_data()) {
18366
18371
  return;
18367
18372
  }
18368
18373
  this.trackDomEvent(ev, MP_EV_CLICK);
@@ -18375,7 +18380,7 @@
18375
18380
  if (!this.getConfig(CONFIG_TRACK_INPUT)) {
18376
18381
  return;
18377
18382
  }
18378
- logger.log('Initializing input tracking');
18383
+ logger$1.log('Initializing input tracking');
18379
18384
 
18380
18385
  this.listenerChange = win.addEventListener(EV_CHANGE, function(ev) {
18381
18386
  if (!this.getConfig(CONFIG_TRACK_INPUT)) {
@@ -18393,7 +18398,7 @@
18393
18398
  if (!this.pageviewTrackingConfig()) {
18394
18399
  return;
18395
18400
  }
18396
- logger.log('Initializing pageview tracking');
18401
+ logger$1.log('Initializing pageview tracking');
18397
18402
 
18398
18403
  var previousTrackedUrl = '';
18399
18404
  var tracked = false;
@@ -18448,7 +18453,7 @@
18448
18453
  }
18449
18454
  if (didPathChange) {
18450
18455
  this.lastScrollCheckpoint = 0;
18451
- logger.log('Path change: re-initializing scroll depth checkpoints');
18456
+ logger$1.log('Path change: re-initializing scroll depth checkpoints');
18452
18457
  }
18453
18458
  }
18454
18459
  }.bind(this)));
@@ -18460,7 +18465,7 @@
18460
18465
  if (!this.getConfig(CONFIG_TRACK_SCROLL)) {
18461
18466
  return;
18462
18467
  }
18463
- logger.log('Initializing scroll tracking');
18468
+ logger$1.log('Initializing scroll tracking');
18464
18469
  this.lastScrollCheckpoint = 0;
18465
18470
 
18466
18471
  this.listenerScroll = win.addEventListener(EV_SCROLLEND, safewrap(function() {
@@ -18497,7 +18502,7 @@
18497
18502
  }
18498
18503
  }
18499
18504
  } catch (err) {
18500
- logger.critical('Error while calculating scroll percentage', err);
18505
+ logger$1.critical('Error while calculating scroll percentage', err);
18501
18506
  }
18502
18507
  if (shouldTrack) {
18503
18508
  this.mp.track(MP_EV_SCROLL, props);
@@ -18511,7 +18516,7 @@
18511
18516
  if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
18512
18517
  return;
18513
18518
  }
18514
- logger.log('Initializing submit tracking');
18519
+ logger$1.log('Initializing submit tracking');
18515
18520
 
18516
18521
  this.listenerSubmit = win.addEventListener(EV_SUBMIT, function(ev) {
18517
18522
  if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
@@ -18524,6 +18529,193 @@
18524
18529
  // TODO integrate error_reporter from mixpanel instance
18525
18530
  safewrapClass(Autocapture);
18526
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.isEnabled = function() {
18581
+ return !!this.getMpConfig(FLAGS_CONFIG_KEY);
18582
+ };
18583
+
18584
+ FeatureFlagManager.prototype.areFeaturesReady = function() {
18585
+ if (!this.isEnabled()) {
18586
+ logger.error('Feature Flags not enabled');
18587
+ }
18588
+ return !!this.flags;
18589
+ };
18590
+
18591
+ FeatureFlagManager.prototype.fetchFlags = function() {
18592
+ if (!this.isEnabled()) {
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
+ 'data': 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.getFeature = 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.getFeatureSync(featureName, fallback);
18638
+ }.bind(this)).catch(function(error) {
18639
+ logger.error(error);
18640
+ return fallback;
18641
+ });
18642
+ };
18643
+
18644
+ FeatureFlagManager.prototype.getFeatureSync = function(featureName, fallback) {
18645
+ if (!this.areFeaturesReady()) {
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.getFeatureData = function(featureName, fallbackValue) {
18659
+ return this.getFeature(featureName, {'data': fallbackValue}).then(function(feature) {
18660
+ return feature['data'];
18661
+ }).catch(function(error) {
18662
+ logger.error(error);
18663
+ return fallbackValue;
18664
+ });
18665
+ };
18666
+
18667
+ FeatureFlagManager.prototype.getFeatureDataSync = function(featureName, fallbackValue) {
18668
+ return this.getFeatureSync(featureName, {'data': fallbackValue})['data'];
18669
+ };
18670
+
18671
+ FeatureFlagManager.prototype.isFeatureEnabled = function(featureName, fallbackValue) {
18672
+ return this.getFeatureData(featureName).then(function() {
18673
+ return this.isFeatureEnabledSync(featureName, fallbackValue);
18674
+ }.bind(this)).catch(function(error) {
18675
+ logger.error(error);
18676
+ return fallbackValue;
18677
+ });
18678
+ };
18679
+
18680
+ FeatureFlagManager.prototype.isFeatureEnabledSync = function(featureName, fallbackValue) {
18681
+ fallbackValue = fallbackValue || false;
18682
+ var val = this.getFeatureDataSync(featureName, fallbackValue);
18683
+ if (val !== true && val !== false) {
18684
+ logger.error('Feature flag "' + featureName + '" value: ' + val + ' is not a boolean; returning fallback value: ' + fallbackValue);
18685
+ val = fallbackValue;
18686
+ }
18687
+ return val;
18688
+ };
18689
+
18690
+ FeatureFlagManager.prototype.trackFeatureCheck = function(featureName, feature) {
18691
+ if (this.trackedFeatures.has(featureName)) {
18692
+ return;
18693
+ }
18694
+ this.trackedFeatures.add(featureName);
18695
+ this.track('$experiment_started', {
18696
+ 'Experiment name': featureName,
18697
+ 'Variant name': feature['key'],
18698
+ '$experiment_type': 'feature_flag'
18699
+ });
18700
+ };
18701
+
18702
+ function minApisSupported() {
18703
+ return !!fetch &&
18704
+ typeof Promise !== 'undefined' &&
18705
+ typeof Map !== 'undefined' &&
18706
+ typeof Set !== 'undefined';
18707
+ }
18708
+
18709
+ safewrapClass(FeatureFlagManager);
18710
+
18711
+ FeatureFlagManager.prototype['are_features_ready'] = FeatureFlagManager.prototype.areFeaturesReady;
18712
+ FeatureFlagManager.prototype['get_feature'] = FeatureFlagManager.prototype.getFeature;
18713
+ FeatureFlagManager.prototype['get_feature_data'] = FeatureFlagManager.prototype.getFeatureData;
18714
+ FeatureFlagManager.prototype['get_feature_data_sync'] = FeatureFlagManager.prototype.getFeatureDataSync;
18715
+ FeatureFlagManager.prototype['get_feature_sync'] = FeatureFlagManager.prototype.getFeatureSync;
18716
+ FeatureFlagManager.prototype['is_feature_enabled'] = FeatureFlagManager.prototype.isFeatureEnabled;
18717
+ FeatureFlagManager.prototype['is_feature_enabled_sync'] = FeatureFlagManager.prototype.isFeatureEnabledSync;
18718
+
18527
18719
  /* eslint camelcase: "off" */
18528
18720
 
18529
18721
 
@@ -19928,10 +20120,11 @@
19928
20120
  }
19929
20121
 
19930
20122
  var DEFAULT_API_ROUTES = {
19931
- 'track': 'track/',
20123
+ 'track': 'track/',
19932
20124
  'engage': 'engage/',
19933
20125
  'groups': 'groups/',
19934
- 'record': 'record/'
20126
+ 'record': 'record/',
20127
+ 'flags': 'flags/'
19935
20128
  };
19936
20129
 
19937
20130
  /*
@@ -19949,6 +20142,7 @@
19949
20142
  'cross_site_cookie': false,
19950
20143
  'cross_subdomain_cookie': true,
19951
20144
  'error_reporter': NOOP_FUNC,
20145
+ 'flags': false,
19952
20146
  'persistence': 'cookie',
19953
20147
  'persistence_name': '',
19954
20148
  'cookie_domain': '',
@@ -19989,6 +20183,7 @@
19989
20183
  'record_block_selector': 'img, video',
19990
20184
  'record_canvas': false,
19991
20185
  'record_collect_fonts': false,
20186
+ 'record_heatmap_data': false,
19992
20187
  'record_idle_timeout_ms': 30 * 60 * 1000, // 30 minutes
19993
20188
  'record_mask_text_class': new RegExp('^(mp-mask|fs-mask|amp-mask|rr-mask|ph-mask)$'),
19994
20189
  'record_mask_text_selector': '*',
@@ -20204,6 +20399,14 @@
20204
20399
  }, '');
20205
20400
  }
20206
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
+
20207
20410
  this.autocapture = new Autocapture(this);
20208
20411
  this.autocapture.init();
20209
20412
 
@@ -20329,6 +20532,10 @@
20329
20532
  }
20330
20533
  };
20331
20534
 
20535
+ MixpanelLib.prototype.is_recording_heatmap_data = function () {
20536
+ return this._get_session_replay_id() && this.get_config('record_heatmap_data');
20537
+ };
20538
+
20332
20539
  MixpanelLib.prototype.get_session_recording_properties = function () {
20333
20540
  var props = {};
20334
20541
  var replay_id = this._get_session_replay_id();
@@ -21410,6 +21617,11 @@
21410
21617
  '$anon_distinct_id': previous_distinct_id
21411
21618
  }, {skip_hooks: true});
21412
21619
  }
21620
+
21621
+ // check feature flags again if distinct id has changed
21622
+ if (new_distinct_id !== previous_distinct_id) {
21623
+ this.flags.fetchFlags();
21624
+ }
21413
21625
  };
21414
21626
 
21415
21627
  /**
@@ -21684,7 +21896,7 @@
21684
21896
  }
21685
21897
  Config.DEBUG = Config.DEBUG || this.get_config('debug');
21686
21898
 
21687
- if ('autocapture' in config && this.autocapture) {
21899
+ if (('autocapture' in config || 'record_heatmap_data' in config) && this.autocapture) {
21688
21900
  this.autocapture.init();
21689
21901
  }
21690
21902
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mixpanel-browser",
3
- "version": "2.63.0",
3
+ "version": "2.64.0",
4
4
  "description": "The official Mixpanel JavaScript browser client library",
5
5
  "main": "dist/mixpanel.cjs.js",
6
6
  "module": "dist/mixpanel.module.js",