mixpanel-browser 2.62.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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  var Config = {
4
4
  DEBUG: false,
5
- LIB_VERSION: '2.62.0'
5
+ LIB_VERSION: '2.64.0'
6
6
  };
7
7
 
8
8
  // since es6 imports are static and we run unit tests from the console, window won't be defined when importing this file
@@ -1238,7 +1238,7 @@ _.UUID = function() {
1238
1238
  uuid[i] = Math.floor(Math.random() * 16);
1239
1239
  }
1240
1240
  uuid[14] = 4; // set bits 12-15 of time-high-and-version to 0100
1241
- uuid[19] = uuid[19] &= ~(1 << 2); // set bit 6 of clock-seq-and-reserved to zero
1241
+ uuid[19] = uuid[19] &= -5; // set bit 6 of clock-seq-and-reserved to zero
1242
1242
  uuid[19] = uuid[19] |= (1 << 3); // set bit 7 of clock-seq-and-reserved to one
1243
1243
  uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
1244
1244
 
@@ -2131,6 +2131,8 @@ var isRecordingExpired = function(serializedRecording) {
2131
2131
  };
2132
2132
 
2133
2133
  // stateless utils
2134
+ // mostly from https://github.com/mixpanel/mixpanel-js/blob/989ada50f518edab47b9c4fd9535f9fbd5ec5fc0/src/autotrack-utils.js
2135
+
2134
2136
 
2135
2137
  var EV_CHANGE = 'change';
2136
2138
  var EV_CLICK = 'click';
@@ -2156,7 +2158,7 @@ var TRACKED_ATTRS = [
2156
2158
  'href', 'name', 'role', 'title', 'type'
2157
2159
  ];
2158
2160
 
2159
- var logger$3 = console_with_prefix('autocapture');
2161
+ var logger$4 = console_with_prefix('autocapture');
2160
2162
 
2161
2163
 
2162
2164
  function getClasses(el) {
@@ -2242,6 +2244,7 @@ function getPropsForDOMEvent(ev, config) {
2242
2244
  var blockSelectors = config.blockSelectors || [];
2243
2245
  var captureTextContent = config.captureTextContent || false;
2244
2246
  var captureExtraAttrs = config.captureExtraAttrs || [];
2247
+ var capturedForHeatMap = config.capturedForHeatMap || false;
2245
2248
 
2246
2249
  // convert array to set every time, as the config may have changed
2247
2250
  var blockAttrsSet = {};
@@ -2320,6 +2323,9 @@ function getPropsForDOMEvent(ev, config) {
2320
2323
  props['$' + prop] = ev[prop];
2321
2324
  }
2322
2325
  });
2326
+ if (capturedForHeatMap) {
2327
+ props['$captured_for_heatmap'] = true;
2328
+ }
2323
2329
  target = guessRealClickTarget(ev);
2324
2330
  }
2325
2331
  // prioritize text content from "real" click target if different from original target
@@ -2414,7 +2420,7 @@ function isElementAllowed(el, ev, allowElementCallback, allowSelectors) {
2414
2420
  return false;
2415
2421
  }
2416
2422
  } catch (err) {
2417
- logger$3.critical('Error while checking element in allowElementCallback', err);
2423
+ logger$4.critical('Error while checking element in allowElementCallback', err);
2418
2424
  return false;
2419
2425
  }
2420
2426
  }
@@ -2431,7 +2437,7 @@ function isElementAllowed(el, ev, allowElementCallback, allowSelectors) {
2431
2437
  return true;
2432
2438
  }
2433
2439
  } catch (err) {
2434
- logger$3.critical('Error while checking selector: ' + sel, err);
2440
+ logger$4.critical('Error while checking selector: ' + sel, err);
2435
2441
  }
2436
2442
  }
2437
2443
  return false;
@@ -2446,7 +2452,7 @@ function isElementBlocked(el, ev, blockElementCallback, blockSelectors) {
2446
2452
  return true;
2447
2453
  }
2448
2454
  } catch (err) {
2449
- logger$3.critical('Error while checking element in blockElementCallback', err);
2455
+ logger$4.critical('Error while checking element in blockElementCallback', err);
2450
2456
  return true;
2451
2457
  }
2452
2458
  }
@@ -2460,7 +2466,7 @@ function isElementBlocked(el, ev, blockElementCallback, blockSelectors) {
2460
2466
  return true;
2461
2467
  }
2462
2468
  } catch (err) {
2463
- logger$3.critical('Error while checking selector: ' + sel, err);
2469
+ logger$4.critical('Error while checking selector: ' + sel, err);
2464
2470
  }
2465
2471
  }
2466
2472
  }
@@ -2666,22 +2672,22 @@ var CONFIG_TRACK_PAGEVIEW = 'pageview';
2666
2672
  var CONFIG_TRACK_SCROLL = 'scroll';
2667
2673
  var CONFIG_TRACK_SUBMIT = 'submit';
2668
2674
 
2669
- var CONFIG_DEFAULTS = {};
2670
- CONFIG_DEFAULTS[CONFIG_ALLOW_SELECTORS] = [];
2671
- CONFIG_DEFAULTS[CONFIG_ALLOW_URL_REGEXES] = [];
2672
- CONFIG_DEFAULTS[CONFIG_BLOCK_ATTRS] = [];
2673
- CONFIG_DEFAULTS[CONFIG_BLOCK_ELEMENT_CALLBACK] = null;
2674
- CONFIG_DEFAULTS[CONFIG_BLOCK_SELECTORS] = [];
2675
- CONFIG_DEFAULTS[CONFIG_BLOCK_URL_REGEXES] = [];
2676
- CONFIG_DEFAULTS[CONFIG_CAPTURE_EXTRA_ATTRS] = [];
2677
- CONFIG_DEFAULTS[CONFIG_CAPTURE_TEXT_CONTENT] = false;
2678
- CONFIG_DEFAULTS[CONFIG_SCROLL_CAPTURE_ALL] = false;
2679
- CONFIG_DEFAULTS[CONFIG_SCROLL_CHECKPOINTS] = [25, 50, 75, 100];
2680
- CONFIG_DEFAULTS[CONFIG_TRACK_CLICK] = true;
2681
- CONFIG_DEFAULTS[CONFIG_TRACK_INPUT] = true;
2682
- CONFIG_DEFAULTS[CONFIG_TRACK_PAGEVIEW] = PAGEVIEW_OPTION_FULL_URL;
2683
- CONFIG_DEFAULTS[CONFIG_TRACK_SCROLL] = true;
2684
- CONFIG_DEFAULTS[CONFIG_TRACK_SUBMIT] = true;
2675
+ var CONFIG_DEFAULTS$1 = {};
2676
+ CONFIG_DEFAULTS$1[CONFIG_ALLOW_SELECTORS] = [];
2677
+ CONFIG_DEFAULTS$1[CONFIG_ALLOW_URL_REGEXES] = [];
2678
+ CONFIG_DEFAULTS$1[CONFIG_BLOCK_ATTRS] = [];
2679
+ CONFIG_DEFAULTS$1[CONFIG_BLOCK_ELEMENT_CALLBACK] = null;
2680
+ CONFIG_DEFAULTS$1[CONFIG_BLOCK_SELECTORS] = [];
2681
+ CONFIG_DEFAULTS$1[CONFIG_BLOCK_URL_REGEXES] = [];
2682
+ CONFIG_DEFAULTS$1[CONFIG_CAPTURE_EXTRA_ATTRS] = [];
2683
+ CONFIG_DEFAULTS$1[CONFIG_CAPTURE_TEXT_CONTENT] = false;
2684
+ CONFIG_DEFAULTS$1[CONFIG_SCROLL_CAPTURE_ALL] = false;
2685
+ CONFIG_DEFAULTS$1[CONFIG_SCROLL_CHECKPOINTS] = [25, 50, 75, 100];
2686
+ CONFIG_DEFAULTS$1[CONFIG_TRACK_CLICK] = true;
2687
+ CONFIG_DEFAULTS$1[CONFIG_TRACK_INPUT] = true;
2688
+ CONFIG_DEFAULTS$1[CONFIG_TRACK_PAGEVIEW] = PAGEVIEW_OPTION_FULL_URL;
2689
+ CONFIG_DEFAULTS$1[CONFIG_TRACK_SCROLL] = true;
2690
+ CONFIG_DEFAULTS$1[CONFIG_TRACK_SUBMIT] = true;
2685
2691
 
2686
2692
  var DEFAULT_PROPS = {
2687
2693
  '$mp_autocapture': true
@@ -2702,7 +2708,7 @@ var Autocapture = function(mp) {
2702
2708
 
2703
2709
  Autocapture.prototype.init = function() {
2704
2710
  if (!minDOMApisSupported()) {
2705
- logger$3.critical('Autocapture unavailable: missing required DOM APIs');
2711
+ logger$4.critical('Autocapture unavailable: missing required DOM APIs');
2706
2712
  return;
2707
2713
  }
2708
2714
 
@@ -2719,10 +2725,10 @@ Autocapture.prototype.getFullConfig = function() {
2719
2725
  // Autocapture is completely off
2720
2726
  return {};
2721
2727
  } else if (_.isObject(autocaptureConfig)) {
2722
- return _.extend({}, CONFIG_DEFAULTS, autocaptureConfig);
2728
+ return _.extend({}, CONFIG_DEFAULTS$1, autocaptureConfig);
2723
2729
  } else {
2724
2730
  // Autocapture config is non-object truthy value, return default
2725
- return CONFIG_DEFAULTS;
2731
+ return CONFIG_DEFAULTS$1;
2726
2732
  }
2727
2733
  };
2728
2734
 
@@ -2746,7 +2752,7 @@ Autocapture.prototype.currentUrlBlocked = function() {
2746
2752
  break;
2747
2753
  }
2748
2754
  } catch (err) {
2749
- logger$3.critical('Error while checking block URL regex: ' + allowRegex, err);
2755
+ logger$4.critical('Error while checking block URL regex: ' + allowRegex, err);
2750
2756
  return true;
2751
2757
  }
2752
2758
  }
@@ -2767,7 +2773,7 @@ Autocapture.prototype.currentUrlBlocked = function() {
2767
2773
  return true;
2768
2774
  }
2769
2775
  } catch (err) {
2770
- logger$3.critical('Error while checking block URL regex: ' + blockUrlRegexes[i], err);
2776
+ logger$4.critical('Error while checking block URL regex: ' + blockUrlRegexes[i], err);
2771
2777
  return true;
2772
2778
  }
2773
2779
  }
@@ -2796,7 +2802,8 @@ Autocapture.prototype.trackDomEvent = function(ev, mpEventName) {
2796
2802
  blockElementCallback: this.getConfig(CONFIG_BLOCK_ELEMENT_CALLBACK),
2797
2803
  blockSelectors: this.getConfig(CONFIG_BLOCK_SELECTORS),
2798
2804
  captureExtraAttrs: this.getConfig(CONFIG_CAPTURE_EXTRA_ATTRS),
2799
- captureTextContent: this.getConfig(CONFIG_CAPTURE_TEXT_CONTENT)
2805
+ captureTextContent: this.getConfig(CONFIG_CAPTURE_TEXT_CONTENT),
2806
+ capturedForHeatMap: mpEventName === MP_EV_CLICK && !this.getConfig(CONFIG_TRACK_CLICK) && this.mp.is_recording_heatmap_data(),
2800
2807
  });
2801
2808
  if (props) {
2802
2809
  _.extend(props, DEFAULT_PROPS);
@@ -2807,13 +2814,13 @@ Autocapture.prototype.trackDomEvent = function(ev, mpEventName) {
2807
2814
  Autocapture.prototype.initClickTracking = function() {
2808
2815
  win.removeEventListener(EV_CLICK, this.listenerClick);
2809
2816
 
2810
- if (!this.getConfig(CONFIG_TRACK_CLICK)) {
2817
+ if (!this.getConfig(CONFIG_TRACK_CLICK) && !this.mp.get_config('record_heatmap_data')) {
2811
2818
  return;
2812
2819
  }
2813
- logger$3.log('Initializing click tracking');
2820
+ logger$4.log('Initializing click tracking');
2814
2821
 
2815
2822
  this.listenerClick = win.addEventListener(EV_CLICK, function(ev) {
2816
- if (!this.getConfig(CONFIG_TRACK_CLICK)) {
2823
+ if (!this.getConfig(CONFIG_TRACK_CLICK) && !this.mp.is_recording_heatmap_data()) {
2817
2824
  return;
2818
2825
  }
2819
2826
  this.trackDomEvent(ev, MP_EV_CLICK);
@@ -2826,7 +2833,7 @@ Autocapture.prototype.initInputTracking = function() {
2826
2833
  if (!this.getConfig(CONFIG_TRACK_INPUT)) {
2827
2834
  return;
2828
2835
  }
2829
- logger$3.log('Initializing input tracking');
2836
+ logger$4.log('Initializing input tracking');
2830
2837
 
2831
2838
  this.listenerChange = win.addEventListener(EV_CHANGE, function(ev) {
2832
2839
  if (!this.getConfig(CONFIG_TRACK_INPUT)) {
@@ -2844,7 +2851,7 @@ Autocapture.prototype.initPageviewTracking = function() {
2844
2851
  if (!this.pageviewTrackingConfig()) {
2845
2852
  return;
2846
2853
  }
2847
- logger$3.log('Initializing pageview tracking');
2854
+ logger$4.log('Initializing pageview tracking');
2848
2855
 
2849
2856
  var previousTrackedUrl = '';
2850
2857
  var tracked = false;
@@ -2899,7 +2906,7 @@ Autocapture.prototype.initPageviewTracking = function() {
2899
2906
  }
2900
2907
  if (didPathChange) {
2901
2908
  this.lastScrollCheckpoint = 0;
2902
- logger$3.log('Path change: re-initializing scroll depth checkpoints');
2909
+ logger$4.log('Path change: re-initializing scroll depth checkpoints');
2903
2910
  }
2904
2911
  }
2905
2912
  }.bind(this)));
@@ -2911,7 +2918,7 @@ Autocapture.prototype.initScrollTracking = function() {
2911
2918
  if (!this.getConfig(CONFIG_TRACK_SCROLL)) {
2912
2919
  return;
2913
2920
  }
2914
- logger$3.log('Initializing scroll tracking');
2921
+ logger$4.log('Initializing scroll tracking');
2915
2922
  this.lastScrollCheckpoint = 0;
2916
2923
 
2917
2924
  this.listenerScroll = win.addEventListener(EV_SCROLLEND, safewrap(function() {
@@ -2948,7 +2955,7 @@ Autocapture.prototype.initScrollTracking = function() {
2948
2955
  }
2949
2956
  }
2950
2957
  } catch (err) {
2951
- logger$3.critical('Error while calculating scroll percentage', err);
2958
+ logger$4.critical('Error while calculating scroll percentage', err);
2952
2959
  }
2953
2960
  if (shouldTrack) {
2954
2961
  this.mp.track(MP_EV_SCROLL, props);
@@ -2962,7 +2969,7 @@ Autocapture.prototype.initSubmitTracking = function() {
2962
2969
  if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
2963
2970
  return;
2964
2971
  }
2965
- logger$3.log('Initializing submit tracking');
2972
+ logger$4.log('Initializing submit tracking');
2966
2973
 
2967
2974
  this.listenerSubmit = win.addEventListener(EV_SUBMIT, function(ev) {
2968
2975
  if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
@@ -2975,8 +2982,196 @@ Autocapture.prototype.initSubmitTracking = function() {
2975
2982
  // TODO integrate error_reporter from mixpanel instance
2976
2983
  safewrapClass(Autocapture);
2977
2984
 
2985
+ var fetch = win['fetch'];
2986
+ var logger$3 = console_with_prefix('flags');
2987
+
2988
+ var FLAGS_CONFIG_KEY = 'flags';
2989
+
2990
+ var CONFIG_CONTEXT = 'context';
2991
+ var CONFIG_DEFAULTS = {};
2992
+ CONFIG_DEFAULTS[CONFIG_CONTEXT] = {};
2993
+
2994
+ /**
2995
+ * FeatureFlagManager: support for Mixpanel's feature flagging product
2996
+ * @constructor
2997
+ */
2998
+ var FeatureFlagManager = function(initOptions) {
2999
+ this.getMpConfig = initOptions.getConfigFunc;
3000
+ this.getDistinctId = initOptions.getDistinctIdFunc;
3001
+ this.track = initOptions.trackingFunc;
3002
+ };
3003
+
3004
+ FeatureFlagManager.prototype.init = function() {
3005
+ if (!minApisSupported()) {
3006
+ logger$3.critical('Feature Flags unavailable: missing minimum required APIs');
3007
+ return;
3008
+ }
3009
+
3010
+ this.flags = null;
3011
+ this.fetchFlags();
3012
+
3013
+ this.trackedFeatures = new Set();
3014
+ };
3015
+
3016
+ FeatureFlagManager.prototype.getFullConfig = function() {
3017
+ var ffConfig = this.getMpConfig(FLAGS_CONFIG_KEY);
3018
+ if (!ffConfig) {
3019
+ // flags are completely off
3020
+ return {};
3021
+ } else if (_.isObject(ffConfig)) {
3022
+ return _.extend({}, CONFIG_DEFAULTS, ffConfig);
3023
+ } else {
3024
+ // config is non-object truthy value, return default
3025
+ return CONFIG_DEFAULTS;
3026
+ }
3027
+ };
3028
+
3029
+ FeatureFlagManager.prototype.getConfig = function(key) {
3030
+ return this.getFullConfig()[key];
3031
+ };
3032
+
3033
+ FeatureFlagManager.prototype.isEnabled = function() {
3034
+ return !!this.getMpConfig(FLAGS_CONFIG_KEY);
3035
+ };
3036
+
3037
+ FeatureFlagManager.prototype.areFeaturesReady = function() {
3038
+ if (!this.isEnabled()) {
3039
+ logger$3.error('Feature Flags not enabled');
3040
+ }
3041
+ return !!this.flags;
3042
+ };
3043
+
3044
+ FeatureFlagManager.prototype.fetchFlags = function() {
3045
+ if (!this.isEnabled()) {
3046
+ return;
3047
+ }
3048
+
3049
+ var distinctId = this.getDistinctId();
3050
+ logger$3.log('Fetching flags for distinct ID: ' + distinctId);
3051
+ var reqParams = {
3052
+ 'context': _.extend({'distinct_id': distinctId}, this.getConfig(CONFIG_CONTEXT))
3053
+ };
3054
+ this.fetchPromise = win['fetch'](this.getMpConfig('api_host') + '/' + this.getMpConfig('api_routes')['flags'], {
3055
+ 'method': 'POST',
3056
+ 'headers': {
3057
+ 'Authorization': 'Basic ' + btoa(this.getMpConfig('token') + ':'),
3058
+ 'Content-Type': 'application/octet-stream'
3059
+ },
3060
+ 'body': JSON.stringify(reqParams)
3061
+ }).then(function(response) {
3062
+ return response.json().then(function(responseBody) {
3063
+ var responseFlags = responseBody['flags'];
3064
+ if (!responseFlags) {
3065
+ throw new Error('No flags in API response');
3066
+ }
3067
+ var flags = new Map();
3068
+ _.each(responseFlags, function(data, key) {
3069
+ flags.set(key, {
3070
+ 'key': data['variant_key'],
3071
+ 'data': data['variant_value']
3072
+ });
3073
+ });
3074
+ this.flags = flags;
3075
+ }.bind(this)).catch(function(error) {
3076
+ logger$3.error(error);
3077
+ });
3078
+ }.bind(this)).catch(function() {});
3079
+ };
3080
+
3081
+ FeatureFlagManager.prototype.getFeature = function(featureName, fallback) {
3082
+ if (!this.fetchPromise) {
3083
+ return new Promise(function(resolve) {
3084
+ logger$3.critical('Feature Flags not initialized');
3085
+ resolve(fallback);
3086
+ });
3087
+ }
3088
+
3089
+ return this.fetchPromise.then(function() {
3090
+ return this.getFeatureSync(featureName, fallback);
3091
+ }.bind(this)).catch(function(error) {
3092
+ logger$3.error(error);
3093
+ return fallback;
3094
+ });
3095
+ };
3096
+
3097
+ FeatureFlagManager.prototype.getFeatureSync = function(featureName, fallback) {
3098
+ if (!this.areFeaturesReady()) {
3099
+ logger$3.log('Flags not loaded yet');
3100
+ return fallback;
3101
+ }
3102
+ var feature = this.flags.get(featureName);
3103
+ if (!feature) {
3104
+ logger$3.log('No flag found: "' + featureName + '"');
3105
+ return fallback;
3106
+ }
3107
+ this.trackFeatureCheck(featureName, feature);
3108
+ return feature;
3109
+ };
3110
+
3111
+ FeatureFlagManager.prototype.getFeatureData = function(featureName, fallbackValue) {
3112
+ return this.getFeature(featureName, {'data': fallbackValue}).then(function(feature) {
3113
+ return feature['data'];
3114
+ }).catch(function(error) {
3115
+ logger$3.error(error);
3116
+ return fallbackValue;
3117
+ });
3118
+ };
3119
+
3120
+ FeatureFlagManager.prototype.getFeatureDataSync = function(featureName, fallbackValue) {
3121
+ return this.getFeatureSync(featureName, {'data': fallbackValue})['data'];
3122
+ };
3123
+
3124
+ FeatureFlagManager.prototype.isFeatureEnabled = function(featureName, fallbackValue) {
3125
+ return this.getFeatureData(featureName).then(function() {
3126
+ return this.isFeatureEnabledSync(featureName, fallbackValue);
3127
+ }.bind(this)).catch(function(error) {
3128
+ logger$3.error(error);
3129
+ return fallbackValue;
3130
+ });
3131
+ };
3132
+
3133
+ FeatureFlagManager.prototype.isFeatureEnabledSync = function(featureName, fallbackValue) {
3134
+ fallbackValue = fallbackValue || false;
3135
+ var val = this.getFeatureDataSync(featureName, fallbackValue);
3136
+ if (val !== true && val !== false) {
3137
+ logger$3.error('Feature flag "' + featureName + '" value: ' + val + ' is not a boolean; returning fallback value: ' + fallbackValue);
3138
+ val = fallbackValue;
3139
+ }
3140
+ return val;
3141
+ };
3142
+
3143
+ FeatureFlagManager.prototype.trackFeatureCheck = function(featureName, feature) {
3144
+ if (this.trackedFeatures.has(featureName)) {
3145
+ return;
3146
+ }
3147
+ this.trackedFeatures.add(featureName);
3148
+ this.track('$experiment_started', {
3149
+ 'Experiment name': featureName,
3150
+ 'Variant name': feature['key'],
3151
+ '$experiment_type': 'feature_flag'
3152
+ });
3153
+ };
3154
+
3155
+ function minApisSupported() {
3156
+ return !!fetch &&
3157
+ typeof Promise !== 'undefined' &&
3158
+ typeof Map !== 'undefined' &&
3159
+ typeof Set !== 'undefined';
3160
+ }
3161
+
3162
+ safewrapClass(FeatureFlagManager);
3163
+
3164
+ FeatureFlagManager.prototype['are_features_ready'] = FeatureFlagManager.prototype.areFeaturesReady;
3165
+ FeatureFlagManager.prototype['get_feature'] = FeatureFlagManager.prototype.getFeature;
3166
+ FeatureFlagManager.prototype['get_feature_data'] = FeatureFlagManager.prototype.getFeatureData;
3167
+ FeatureFlagManager.prototype['get_feature_data_sync'] = FeatureFlagManager.prototype.getFeatureDataSync;
3168
+ FeatureFlagManager.prototype['get_feature_sync'] = FeatureFlagManager.prototype.getFeatureSync;
3169
+ FeatureFlagManager.prototype['is_feature_enabled'] = FeatureFlagManager.prototype.isFeatureEnabled;
3170
+ FeatureFlagManager.prototype['is_feature_enabled_sync'] = FeatureFlagManager.prototype.isFeatureEnabledSync;
3171
+
2978
3172
  /* eslint camelcase: "off" */
2979
3173
 
3174
+
2980
3175
  /**
2981
3176
  * DomTracker Object
2982
3177
  * @constructor
@@ -3280,7 +3475,7 @@ SharedLock.prototype.withLock = function(lockedCB, pid) {
3280
3475
  * @type {import('./wrapper').StorageWrapper}
3281
3476
  */
3282
3477
  var LocalStorageWrapper = function (storageOverride) {
3283
- this.storage = storageOverride || localStorage;
3478
+ this.storage = storageOverride || win.localStorage;
3284
3479
  };
3285
3480
 
3286
3481
  LocalStorageWrapper.prototype.init = function () {
@@ -4027,6 +4222,7 @@ RequestBatcher.prototype.reportError = function(msg, err) {
4027
4222
  * These functions are used internally by the SDK and are not intended to be publicly exposed.
4028
4223
  */
4029
4224
 
4225
+
4030
4226
  /**
4031
4227
  * A function used to track a Mixpanel event (e.g. MixpanelLib.track)
4032
4228
  * @callback trackFunction
@@ -4315,6 +4511,7 @@ function _addOptOutCheck(method, getConfigValue) {
4315
4511
 
4316
4512
  /* eslint camelcase: "off" */
4317
4513
 
4514
+
4318
4515
  /** @const */ var SET_ACTION = '$set';
4319
4516
  /** @const */ var SET_ONCE_ACTION = '$set_once';
4320
4517
  /** @const */ var UNSET_ACTION = '$unset';
@@ -5074,6 +5271,7 @@ MixpanelPeople.prototype['toString'] = MixpanelPeople.prototype.toString;
5074
5271
 
5075
5272
  /* eslint camelcase: "off" */
5076
5273
 
5274
+
5077
5275
  /*
5078
5276
  * Constants
5079
5277
  */
@@ -5686,10 +5884,11 @@ if (navigator['sendBeacon']) {
5686
5884
  }
5687
5885
 
5688
5886
  var DEFAULT_API_ROUTES = {
5689
- 'track': 'track/',
5887
+ 'track': 'track/',
5690
5888
  'engage': 'engage/',
5691
5889
  'groups': 'groups/',
5692
- 'record': 'record/'
5890
+ 'record': 'record/',
5891
+ 'flags': 'flags/'
5693
5892
  };
5694
5893
 
5695
5894
  /*
@@ -5707,6 +5906,7 @@ var DEFAULT_CONFIG = {
5707
5906
  'cross_site_cookie': false,
5708
5907
  'cross_subdomain_cookie': true,
5709
5908
  'error_reporter': NOOP_FUNC,
5909
+ 'flags': false,
5710
5910
  'persistence': 'cookie',
5711
5911
  'persistence_name': '',
5712
5912
  'cookie_domain': '',
@@ -5747,6 +5947,7 @@ var DEFAULT_CONFIG = {
5747
5947
  'record_block_selector': 'img, video',
5748
5948
  'record_canvas': false,
5749
5949
  'record_collect_fonts': false,
5950
+ 'record_heatmap_data': false,
5750
5951
  'record_idle_timeout_ms': 30 * 60 * 1000, // 30 minutes
5751
5952
  'record_mask_text_class': new RegExp('^(mp-mask|fs-mask|amp-mask|rr-mask|ph-mask)$'),
5752
5953
  'record_mask_text_selector': '*',
@@ -5962,6 +6163,14 @@ MixpanelLib.prototype._init = function(token, config, name) {
5962
6163
  }, '');
5963
6164
  }
5964
6165
 
6166
+ this.flags = new FeatureFlagManager({
6167
+ getConfigFunc: _.bind(this.get_config, this),
6168
+ getDistinctIdFunc: _.bind(this.get_distinct_id, this),
6169
+ trackingFunc: _.bind(this.track, this)
6170
+ });
6171
+ this.flags.init();
6172
+ this['flags'] = this.flags;
6173
+
5965
6174
  this.autocapture = new Autocapture(this);
5966
6175
  this.autocapture.init();
5967
6176
 
@@ -6087,6 +6296,10 @@ MixpanelLib.prototype.resume_session_recording = function () {
6087
6296
  }
6088
6297
  };
6089
6298
 
6299
+ MixpanelLib.prototype.is_recording_heatmap_data = function () {
6300
+ return this._get_session_replay_id() && this.get_config('record_heatmap_data');
6301
+ };
6302
+
6090
6303
  MixpanelLib.prototype.get_session_recording_properties = function () {
6091
6304
  var props = {};
6092
6305
  var replay_id = this._get_session_replay_id();
@@ -7168,6 +7381,11 @@ MixpanelLib.prototype.identify = function(
7168
7381
  '$anon_distinct_id': previous_distinct_id
7169
7382
  }, {skip_hooks: true});
7170
7383
  }
7384
+
7385
+ // check feature flags again if distinct id has changed
7386
+ if (new_distinct_id !== previous_distinct_id) {
7387
+ this.flags.fetchFlags();
7388
+ }
7171
7389
  };
7172
7390
 
7173
7391
  /**
@@ -7442,7 +7660,7 @@ MixpanelLib.prototype.set_config = function(config) {
7442
7660
  }
7443
7661
  Config.DEBUG = Config.DEBUG || this.get_config('debug');
7444
7662
 
7445
- if ('autocapture' in config && this.autocapture) {
7663
+ if (('autocapture' in config || 'record_heatmap_data' in config) && this.autocapture) {
7446
7664
  this.autocapture.init();
7447
7665
  }
7448
7666
  }