mixpanel-browser 2.55.1 → 2.56.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.55.1'
5
+ LIB_VERSION: '2.56.0'
6
6
  };
7
7
 
8
8
  /* eslint camelcase: "off", eqeqeq: "off" */
@@ -2056,11 +2056,13 @@ var logger$1 = console_with_prefix('batch');
2056
2056
  var RequestQueue = function(storageKey, options) {
2057
2057
  options = options || {};
2058
2058
  this.storageKey = storageKey;
2059
- this.storage = options.storage || window.localStorage;
2059
+ this.usePersistence = options.usePersistence;
2060
+ if (this.usePersistence) {
2061
+ this.storage = options.storage || window.localStorage;
2062
+ this.lock = new SharedLock(storageKey, {storage: this.storage});
2063
+ }
2060
2064
  this.reportError = options.errorReporter || _.bind(logger$1.error, logger$1);
2061
- this.lock = new SharedLock(storageKey, {storage: this.storage});
2062
2065
 
2063
- this.usePersistence = options.usePersistence;
2064
2066
  this.pid = options.pid || null; // pass pid to test out storage lock contention scenarios
2065
2067
 
2066
2068
  this.memQueue = [];
@@ -4257,7 +4259,6 @@ var DEFAULT_CONFIG = {
4257
4259
  'record_block_selector': 'img, video',
4258
4260
  'record_collect_fonts': false,
4259
4261
  'record_idle_timeout_ms': 30 * 60 * 1000, // 30 minutes
4260
- 'record_inline_images': false,
4261
4262
  'record_mask_text_class': new RegExp('^(mp-mask|fs-mask|amp-mask|rr-mask|ph-mask)$'),
4262
4263
  'record_mask_text_selector': '*',
4263
4264
  'record_max_ms': MAX_RECORDING_MS,
@@ -4510,15 +4511,35 @@ MixpanelLib.prototype.stop_session_recording = function () {
4510
4511
 
4511
4512
  MixpanelLib.prototype.get_session_recording_properties = function () {
4512
4513
  var props = {};
4513
- if (this._recorder) {
4514
- var replay_id = this._recorder['replayId'];
4515
- if (replay_id) {
4516
- props['$mp_replay_id'] = replay_id;
4517
- }
4514
+ var replay_id = this._get_session_replay_id();
4515
+ if (replay_id) {
4516
+ props['$mp_replay_id'] = replay_id;
4518
4517
  }
4519
4518
  return props;
4520
4519
  };
4521
4520
 
4521
+ MixpanelLib.prototype.get_session_replay_url = function () {
4522
+ var replay_url = null;
4523
+ var replay_id = this._get_session_replay_id();
4524
+ if (replay_id) {
4525
+ var query_params = _.HTTPBuildQuery({
4526
+ 'replay_id': replay_id,
4527
+ 'distinct_id': this.get_distinct_id(),
4528
+ 'token': this.get_config('token')
4529
+ });
4530
+ replay_url = 'https://mixpanel.com/projects/replay-redirect?' + query_params;
4531
+ }
4532
+ return replay_url;
4533
+ };
4534
+
4535
+ MixpanelLib.prototype._get_session_replay_id = function () {
4536
+ var replay_id = null;
4537
+ if (this._recorder) {
4538
+ replay_id = this._recorder['replayId'];
4539
+ }
4540
+ return replay_id || null;
4541
+ };
4542
+
4522
4543
  // Private methods
4523
4544
 
4524
4545
  MixpanelLib.prototype._loaded = function() {
@@ -6245,6 +6266,7 @@ MixpanelLib.prototype['stop_batch_senders'] = MixpanelLib.protot
6245
6266
  MixpanelLib.prototype['start_session_recording'] = MixpanelLib.prototype.start_session_recording;
6246
6267
  MixpanelLib.prototype['stop_session_recording'] = MixpanelLib.prototype.stop_session_recording;
6247
6268
  MixpanelLib.prototype['get_session_recording_properties'] = MixpanelLib.prototype.get_session_recording_properties;
6269
+ MixpanelLib.prototype['get_session_replay_url'] = MixpanelLib.prototype.get_session_replay_url;
6248
6270
  MixpanelLib.prototype['DEFAULT_API_ROUTES'] = DEFAULT_API_ROUTES;
6249
6271
 
6250
6272
  // MixpanelPersistence Exports
@@ -4509,7 +4509,7 @@ define((function () { 'use strict';
4509
4509
 
4510
4510
  var Config = {
4511
4511
  DEBUG: false,
4512
- LIB_VERSION: '2.55.1'
4512
+ LIB_VERSION: '2.56.0'
4513
4513
  };
4514
4514
 
4515
4515
  /* eslint camelcase: "off", eqeqeq: "off" */
@@ -6546,7 +6546,7 @@ define((function () { 'use strict';
6546
6546
  };
6547
6547
  }
6548
6548
 
6549
- var logger$3 = console_with_prefix('lock');
6549
+ var logger$4 = console_with_prefix('lock');
6550
6550
 
6551
6551
  /**
6552
6552
  * SharedLock: a mutex built on HTML5 localStorage, to ensure that only one browser
@@ -6603,7 +6603,7 @@ define((function () { 'use strict';
6603
6603
 
6604
6604
  var delay = function(cb) {
6605
6605
  if (new Date().getTime() - startTime > timeoutMS) {
6606
- logger$3.error('Timeout waiting for mutex on ' + key + '; clearing lock. [' + i + ']');
6606
+ logger$4.error('Timeout waiting for mutex on ' + key + '; clearing lock. [' + i + ']');
6607
6607
  storage.removeItem(keyZ);
6608
6608
  storage.removeItem(keyY);
6609
6609
  loop();
@@ -6692,7 +6692,7 @@ define((function () { 'use strict';
6692
6692
  }
6693
6693
  };
6694
6694
 
6695
- var logger$2 = console_with_prefix('batch');
6695
+ var logger$3 = console_with_prefix('batch');
6696
6696
 
6697
6697
  /**
6698
6698
  * RequestQueue: queue for batching API requests with localStorage backup for retries.
@@ -6713,11 +6713,13 @@ define((function () { 'use strict';
6713
6713
  var RequestQueue = function(storageKey, options) {
6714
6714
  options = options || {};
6715
6715
  this.storageKey = storageKey;
6716
- this.storage = options.storage || window.localStorage;
6717
- this.reportError = options.errorReporter || _.bind(logger$2.error, logger$2);
6718
- this.lock = new SharedLock(storageKey, {storage: this.storage});
6719
-
6720
6716
  this.usePersistence = options.usePersistence;
6717
+ if (this.usePersistence) {
6718
+ this.storage = options.storage || window.localStorage;
6719
+ this.lock = new SharedLock(storageKey, {storage: this.storage});
6720
+ }
6721
+ this.reportError = options.errorReporter || _.bind(logger$3.error, logger$3);
6722
+
6721
6723
  this.pid = options.pid || null; // pass pid to test out storage lock contention scenarios
6722
6724
 
6723
6725
  this.memQueue = [];
@@ -6996,7 +6998,7 @@ define((function () { 'use strict';
6996
6998
  // maximum interval between request retries after exponential backoff
6997
6999
  var MAX_RETRY_INTERVAL_MS = 10 * 60 * 1000; // 10 minutes
6998
7000
 
6999
- var logger$1 = console_with_prefix('batch');
7001
+ var logger$2 = console_with_prefix('batch');
7000
7002
 
7001
7003
  /**
7002
7004
  * RequestBatcher: manages the queueing, flushing, retry etc of requests of one
@@ -7110,7 +7112,7 @@ define((function () { 'use strict';
7110
7112
  try {
7111
7113
 
7112
7114
  if (this.requestInProgress) {
7113
- logger$1.log('Flush: Request already in progress');
7115
+ logger$2.log('Flush: Request already in progress');
7114
7116
  return;
7115
7117
  }
7116
7118
 
@@ -7278,7 +7280,7 @@ define((function () { 'use strict';
7278
7280
  if (options.unloading) {
7279
7281
  requestOptions.transport = 'sendBeacon';
7280
7282
  }
7281
- logger$1.log('MIXPANEL REQUEST:', dataForRequest);
7283
+ logger$2.log('MIXPANEL REQUEST:', dataForRequest);
7282
7284
  this.sendRequest(dataForRequest, requestOptions, batchSendCallback);
7283
7285
  } catch(err) {
7284
7286
  this.reportError('Error flushing request queue', err);
@@ -7290,7 +7292,7 @@ define((function () { 'use strict';
7290
7292
  * Log error to global logger and optional user-defined logger.
7291
7293
  */
7292
7294
  RequestBatcher.prototype.reportError = function(msg, err) {
7293
- logger$1.error.apply(logger$1.error, arguments);
7295
+ logger$2.error.apply(logger$2.error, arguments);
7294
7296
  if (this.errorReporter) {
7295
7297
  try {
7296
7298
  if (!(err instanceof Error)) {
@@ -7298,12 +7300,12 @@ define((function () { 'use strict';
7298
7300
  }
7299
7301
  this.errorReporter(msg, err);
7300
7302
  } catch(err) {
7301
- logger$1.error(err);
7303
+ logger$2.error(err);
7302
7304
  }
7303
7305
  }
7304
7306
  };
7305
7307
 
7306
- var logger = console_with_prefix('recorder');
7308
+ var logger$1 = console_with_prefix('recorder');
7307
7309
  var CompressionStream = win['CompressionStream'];
7308
7310
 
7309
7311
  var RECORDER_BATCHER_LIB_CONFIG = {
@@ -7329,65 +7331,78 @@ define((function () { 'use strict';
7329
7331
  return ev.type === EventType.IncrementalSnapshot && ACTIVE_SOURCES.has(ev.data.source);
7330
7332
  }
7331
7333
 
7332
- var MixpanelRecorder = function(mixpanelInstance) {
7333
- this._mixpanel = mixpanelInstance;
7334
+ /**
7335
+ * This class encapsulates a single session recording and its lifecycle.
7336
+ * @param {Object} [options.mixpanelInstance] - reference to the core MixpanelLib
7337
+ * @param {String} [options.replayId] - unique uuid for a single replay
7338
+ * @param {Function} [options.onIdleTimeout] - callback when a recording reaches idle timeout
7339
+ * @param {Function} [options.onMaxLengthReached] - callback when a recording reaches its maximum length
7340
+ * @param {Function} [options.rrwebRecord] - rrweb's `record` function
7341
+ */
7342
+ var SessionRecording = function(options) {
7343
+ this._mixpanel = options.mixpanelInstance;
7344
+ this._onIdleTimeout = options.onIdleTimeout;
7345
+ this._onMaxLengthReached = options.onMaxLengthReached;
7346
+ this._rrwebRecord = options.rrwebRecord;
7347
+
7348
+ this.replayId = options.replayId;
7334
7349
 
7335
7350
  // internal rrweb stopRecording function
7336
7351
  this._stopRecording = null;
7337
7352
 
7338
- this.recEvents = [];
7339
7353
  this.seqNo = 0;
7340
- this.replayId = null;
7341
7354
  this.replayStartTime = null;
7342
- this.sendBatchId = null;
7355
+ this.batchStartUrl = null;
7343
7356
 
7344
7357
  this.idleTimeoutId = null;
7345
7358
  this.maxTimeoutId = null;
7346
7359
 
7347
7360
  this.recordMaxMs = MAX_RECORDING_MS;
7348
7361
  this.recordMinMs = 0;
7349
- this._initBatcher();
7350
- };
7351
7362
 
7352
-
7353
- MixpanelRecorder.prototype._initBatcher = function () {
7354
- this.batcher = new RequestBatcher('__mprec', {
7355
- libConfig: RECORDER_BATCHER_LIB_CONFIG,
7356
- sendRequestFunc: _.bind(this.flushEventsWithOptOut, this),
7363
+ // each replay has its own batcher key to avoid conflicts between rrweb events of different recordings
7364
+ // this will be important when persistence is introduced
7365
+ var batcherKey = '__mprec_' + this.getConfig('token') + '_' + this.replayId;
7366
+ this.batcher = new RequestBatcher(batcherKey, {
7357
7367
  errorReporter: _.bind(this.reportError, this),
7358
7368
  flushOnlyOnInterval: true,
7369
+ libConfig: RECORDER_BATCHER_LIB_CONFIG,
7370
+ sendRequestFunc: _.bind(this.flushEventsWithOptOut, this),
7359
7371
  usePersistence: false
7360
7372
  });
7361
7373
  };
7362
7374
 
7363
- // eslint-disable-next-line camelcase
7364
- MixpanelRecorder.prototype.get_config = function(configVar) {
7375
+ SessionRecording.prototype.getConfig = function(configVar) {
7365
7376
  return this._mixpanel.get_config(configVar);
7366
7377
  };
7367
7378
 
7368
- MixpanelRecorder.prototype.startRecording = function (shouldStopBatcher) {
7379
+ // Alias for getConfig, used by the common addOptOutCheckMixpanelLib function which
7380
+ // reaches into this class instance and expects the snake case version of the function.
7381
+ // eslint-disable-next-line camelcase
7382
+ SessionRecording.prototype.get_config = function(configVar) {
7383
+ return this.getConfig(configVar);
7384
+ };
7385
+
7386
+ SessionRecording.prototype.startRecording = function (shouldStopBatcher) {
7369
7387
  if (this._stopRecording !== null) {
7370
- logger.log('Recording already in progress, skipping startRecording.');
7388
+ logger$1.log('Recording already in progress, skipping startRecording.');
7371
7389
  return;
7372
7390
  }
7373
7391
 
7374
- this.recordMaxMs = this.get_config('record_max_ms');
7392
+ this.recordMaxMs = this.getConfig('record_max_ms');
7375
7393
  if (this.recordMaxMs > MAX_RECORDING_MS) {
7376
7394
  this.recordMaxMs = MAX_RECORDING_MS;
7377
- logger.critical('record_max_ms cannot be greater than ' + MAX_RECORDING_MS + 'ms. Capping value.');
7395
+ logger$1.critical('record_max_ms cannot be greater than ' + MAX_RECORDING_MS + 'ms. Capping value.');
7378
7396
  }
7379
7397
 
7380
- this.recordMinMs = this.get_config('record_min_ms');
7398
+ this.recordMinMs = this.getConfig('record_min_ms');
7381
7399
  if (this.recordMinMs > MAX_VALUE_FOR_MIN_RECORDING_MS) {
7382
7400
  this.recordMinMs = MAX_VALUE_FOR_MIN_RECORDING_MS;
7383
- logger.critical('record_min_ms cannot be greater than ' + MAX_VALUE_FOR_MIN_RECORDING_MS + 'ms. Capping value.');
7401
+ logger$1.critical('record_min_ms cannot be greater than ' + MAX_VALUE_FOR_MIN_RECORDING_MS + 'ms. Capping value.');
7384
7402
  }
7385
7403
 
7386
- this.recEvents = [];
7387
- this.seqNo = 0;
7388
7404
  this.replayStartTime = new Date().getTime();
7389
-
7390
- this.replayId = _.UUID();
7405
+ this.batchStartUrl = _.info.currentUrl();
7391
7406
 
7392
7407
  if (shouldStopBatcher || this.recordMinMs > 0) {
7393
7408
  // the primary case for shouldStopBatcher is when we're starting recording after a reset
@@ -7402,18 +7417,15 @@ define((function () { 'use strict';
7402
7417
 
7403
7418
  var resetIdleTimeout = _.bind(function () {
7404
7419
  clearTimeout(this.idleTimeoutId);
7405
- this.idleTimeoutId = setTimeout(_.bind(function () {
7406
- logger.log('Idle timeout reached, restarting recording.');
7407
- this.resetRecording();
7408
- }, this), this.get_config('record_idle_timeout_ms'));
7420
+ this.idleTimeoutId = setTimeout(this._onIdleTimeout, this.getConfig('record_idle_timeout_ms'));
7409
7421
  }, this);
7410
7422
 
7411
- var blockSelector = this.get_config('record_block_selector');
7423
+ var blockSelector = this.getConfig('record_block_selector');
7412
7424
  if (blockSelector === '' || blockSelector === null) {
7413
7425
  blockSelector = undefined;
7414
7426
  }
7415
7427
 
7416
- this._stopRecording = record({
7428
+ this._stopRecording = this._rrwebRecord({
7417
7429
  'emit': _.bind(function (ev) {
7418
7430
  this.batcher.enqueue(ev);
7419
7431
  if (isUserEvent(ev)) {
@@ -7424,28 +7436,33 @@ define((function () { 'use strict';
7424
7436
  resetIdleTimeout();
7425
7437
  }
7426
7438
  }, this),
7427
- 'blockClass': this.get_config('record_block_class'),
7439
+ 'blockClass': this.getConfig('record_block_class'),
7428
7440
  'blockSelector': blockSelector,
7429
- 'collectFonts': this.get_config('record_collect_fonts'),
7430
- 'inlineImages': this.get_config('record_inline_images'),
7441
+ 'collectFonts': this.getConfig('record_collect_fonts'),
7431
7442
  'maskAllInputs': true,
7432
- 'maskTextClass': this.get_config('record_mask_text_class'),
7433
- 'maskTextSelector': this.get_config('record_mask_text_selector')
7443
+ 'maskTextClass': this.getConfig('record_mask_text_class'),
7444
+ 'maskTextSelector': this.getConfig('record_mask_text_selector')
7434
7445
  });
7435
7446
 
7436
- resetIdleTimeout();
7447
+ if (typeof this._stopRecording !== 'function') {
7448
+ this.reportError('rrweb failed to start, skipping this recording.');
7449
+ this._stopRecording = null;
7450
+ this.stopRecording(); // stop batcher looping and any timeouts
7451
+ return;
7452
+ }
7437
7453
 
7438
- this.maxTimeoutId = setTimeout(_.bind(this.resetRecording, this), this.recordMaxMs);
7439
- };
7454
+ resetIdleTimeout();
7440
7455
 
7441
- MixpanelRecorder.prototype.resetRecording = function () {
7442
- this.stopRecording();
7443
- this.startRecording(true);
7456
+ this.maxTimeoutId = setTimeout(_.bind(this._onMaxLengthReached, this), this.recordMaxMs);
7444
7457
  };
7445
7458
 
7446
- MixpanelRecorder.prototype.stopRecording = function () {
7447
- if (this._stopRecording !== null) {
7448
- this._stopRecording();
7459
+ SessionRecording.prototype.stopRecording = function () {
7460
+ if (!this.isRrwebStopped()) {
7461
+ try {
7462
+ this._stopRecording();
7463
+ } catch (err) {
7464
+ this.reportError('Error with rrweb stopRecording', err);
7465
+ }
7449
7466
  this._stopRecording = null;
7450
7467
  }
7451
7468
 
@@ -7457,35 +7474,38 @@ define((function () { 'use strict';
7457
7474
  this.batcher.flush();
7458
7475
  this.batcher.stop();
7459
7476
  }
7460
- this.replayId = null;
7461
7477
 
7462
7478
  clearTimeout(this.idleTimeoutId);
7463
7479
  clearTimeout(this.maxTimeoutId);
7464
7480
  };
7465
7481
 
7482
+ SessionRecording.prototype.isRrwebStopped = function () {
7483
+ return this._stopRecording === null;
7484
+ };
7485
+
7466
7486
  /**
7467
7487
  * Flushes the current batch of events to the server, but passes an opt-out callback to make sure
7468
7488
  * we stop recording and dump any queued events if the user has opted out.
7469
7489
  */
7470
- MixpanelRecorder.prototype.flushEventsWithOptOut = function (data, options, cb) {
7490
+ SessionRecording.prototype.flushEventsWithOptOut = function (data, options, cb) {
7471
7491
  this._flushEvents(data, options, cb, _.bind(this._onOptOut, this));
7472
7492
  };
7473
7493
 
7474
- MixpanelRecorder.prototype._onOptOut = function (code) {
7494
+ SessionRecording.prototype._onOptOut = function (code) {
7475
7495
  // addOptOutCheckMixpanelLib invokes this function with code=0 when the user has opted out
7476
7496
  if (code === 0) {
7477
- this.recEvents = [];
7478
7497
  this.stopRecording();
7479
7498
  }
7480
7499
  };
7481
7500
 
7482
- MixpanelRecorder.prototype._sendRequest = function(currentReplayId, reqParams, reqBody, callback) {
7501
+ SessionRecording.prototype._sendRequest = function(currentReplayId, reqParams, reqBody, callback) {
7483
7502
  var onSuccess = _.bind(function (response, responseBody) {
7484
- // Increment sequence counter only if the request was successful to guarantee ordering.
7503
+ // Update batch specific props only if the request was successful to guarantee ordering.
7485
7504
  // RequestBatcher will always flush the next batch after the previous one succeeds.
7486
7505
  // extra check to see if the replay ID has changed so that we don't increment the seqNo on the wrong replay
7487
7506
  if (response.status === 200 && this.replayId === currentReplayId) {
7488
7507
  this.seqNo++;
7508
+ this.batchStartUrl = _.info.currentUrl();
7489
7509
  }
7490
7510
  callback({
7491
7511
  status: 0,
@@ -7495,10 +7515,10 @@ define((function () { 'use strict';
7495
7515
  });
7496
7516
  }, this);
7497
7517
 
7498
- win['fetch'](this.get_config('api_host') + '/' + this.get_config('api_routes')['record'] + '?' + new URLSearchParams(reqParams), {
7518
+ win['fetch'](this.getConfig('api_host') + '/' + this.getConfig('api_routes')['record'] + '?' + new URLSearchParams(reqParams), {
7499
7519
  'method': 'POST',
7500
7520
  'headers': {
7501
- 'Authorization': 'Basic ' + btoa(this.get_config('token') + ':'),
7521
+ 'Authorization': 'Basic ' + btoa(this.getConfig('token') + ':'),
7502
7522
  'Content-Type': 'application/octet-stream'
7503
7523
  },
7504
7524
  'body': reqBody,
@@ -7513,7 +7533,7 @@ define((function () { 'use strict';
7513
7533
  });
7514
7534
  };
7515
7535
 
7516
- MixpanelRecorder.prototype._flushEvents = addOptOutCheckMixpanelLib(function (data, options, callback) {
7536
+ SessionRecording.prototype._flushEvents = addOptOutCheckMixpanelLib(function (data, options, callback) {
7517
7537
  const numEvents = data.length;
7518
7538
 
7519
7539
  if (numEvents > 0) {
@@ -7531,12 +7551,15 @@ define((function () { 'use strict';
7531
7551
  var replayLengthMs = data[numEvents - 1].timestamp - this.replayStartTime;
7532
7552
 
7533
7553
  var reqParams = {
7534
- 'distinct_id': String(this._mixpanel.get_distinct_id()),
7535
- 'seq': this.seqNo,
7554
+ '$current_url': this.batchStartUrl,
7555
+ '$lib_version': Config.LIB_VERSION,
7536
7556
  'batch_start_time': batchStartTime / 1000,
7557
+ 'distinct_id': String(this._mixpanel.get_distinct_id()),
7558
+ 'mp_lib': 'web',
7537
7559
  'replay_id': replayId,
7538
7560
  'replay_length_ms': replayLengthMs,
7539
- 'replay_start_time': this.replayStartTime / 1000
7561
+ 'replay_start_time': this.replayStartTime / 1000,
7562
+ 'seq': this.seqNo
7540
7563
  };
7541
7564
  var eventsJson = _.JSONEncode(data);
7542
7565
 
@@ -7567,18 +7590,83 @@ define((function () { 'use strict';
7567
7590
  });
7568
7591
 
7569
7592
 
7570
- MixpanelRecorder.prototype.reportError = function(msg, err) {
7571
- logger.error.apply(logger.error, arguments);
7593
+ SessionRecording.prototype.reportError = function(msg, err) {
7594
+ logger$1.error.apply(logger$1.error, arguments);
7572
7595
  try {
7573
7596
  if (!err && !(msg instanceof Error)) {
7574
7597
  msg = new Error(msg);
7575
7598
  }
7576
- this.get_config('error_reporter')(msg, err);
7599
+ this.getConfig('error_reporter')(msg, err);
7577
7600
  } catch(err) {
7578
- logger.error(err);
7601
+ logger$1.error(err);
7602
+ }
7603
+ };
7604
+
7605
+ var logger = console_with_prefix('recorder');
7606
+
7607
+ /**
7608
+ * Recorder API: manages recordings and exposes methods public to the core Mixpanel library.
7609
+ * @param {Object} [options.mixpanelInstance] - reference to the core MixpanelLib
7610
+ */
7611
+ var MixpanelRecorder = function(mixpanelInstance) {
7612
+ this._mixpanel = mixpanelInstance;
7613
+ this.activeRecording = null;
7614
+ };
7615
+
7616
+ MixpanelRecorder.prototype.startRecording = function(shouldStopBatcher) {
7617
+ if (this.activeRecording && !this.activeRecording.isRrwebStopped()) {
7618
+ logger.log('Recording already in progress, skipping startRecording.');
7619
+ return;
7620
+ }
7621
+
7622
+ var onIdleTimeout = _.bind(function () {
7623
+ logger.log('Idle timeout reached, restarting recording.');
7624
+ this.resetRecording();
7625
+ }, this);
7626
+
7627
+ var onMaxLengthReached = _.bind(function () {
7628
+ logger.log('Max recording length reached, stopping recording.');
7629
+ this.resetRecording();
7630
+ }, this);
7631
+
7632
+ this.activeRecording = new SessionRecording({
7633
+ mixpanelInstance: this._mixpanel,
7634
+ onIdleTimeout: onIdleTimeout,
7635
+ onMaxLengthReached: onMaxLengthReached,
7636
+ replayId: _.UUID(),
7637
+ rrwebRecord: record
7638
+ });
7639
+
7640
+ this.activeRecording.startRecording(shouldStopBatcher);
7641
+ };
7642
+
7643
+ MixpanelRecorder.prototype.stopRecording = function() {
7644
+ if (this.activeRecording) {
7645
+ this.activeRecording.stopRecording();
7646
+ this.activeRecording = null;
7579
7647
  }
7580
7648
  };
7581
7649
 
7650
+ MixpanelRecorder.prototype.resetRecording = function () {
7651
+ this.stopRecording();
7652
+ this.startRecording(true);
7653
+ };
7654
+
7655
+ MixpanelRecorder.prototype.getActiveReplayId = function () {
7656
+ if (this.activeRecording && !this.activeRecording.isRrwebStopped()) {
7657
+ return this.activeRecording.replayId;
7658
+ } else {
7659
+ return null;
7660
+ }
7661
+ };
7662
+
7663
+ // getter so that older mixpanel-core versions can still retrieve the replay ID
7664
+ // when pulling the latest recorder bundle from the CDN
7665
+ Object.defineProperty(MixpanelRecorder.prototype, 'replayId', {
7666
+ get: function () {
7667
+ return this.getActiveReplayId();
7668
+ }
7669
+ });
7582
7670
 
7583
7671
  win['__mp_recorder'] = MixpanelRecorder;
7584
7672
 
@@ -9045,7 +9133,6 @@ define((function () { 'use strict';
9045
9133
  'record_block_selector': 'img, video',
9046
9134
  'record_collect_fonts': false,
9047
9135
  'record_idle_timeout_ms': 30 * 60 * 1000, // 30 minutes
9048
- 'record_inline_images': false,
9049
9136
  'record_mask_text_class': new RegExp('^(mp-mask|fs-mask|amp-mask|rr-mask|ph-mask)$'),
9050
9137
  'record_mask_text_selector': '*',
9051
9138
  'record_max_ms': MAX_RECORDING_MS,
@@ -9298,15 +9385,35 @@ define((function () { 'use strict';
9298
9385
 
9299
9386
  MixpanelLib.prototype.get_session_recording_properties = function () {
9300
9387
  var props = {};
9301
- if (this._recorder) {
9302
- var replay_id = this._recorder['replayId'];
9303
- if (replay_id) {
9304
- props['$mp_replay_id'] = replay_id;
9305
- }
9388
+ var replay_id = this._get_session_replay_id();
9389
+ if (replay_id) {
9390
+ props['$mp_replay_id'] = replay_id;
9306
9391
  }
9307
9392
  return props;
9308
9393
  };
9309
9394
 
9395
+ MixpanelLib.prototype.get_session_replay_url = function () {
9396
+ var replay_url = null;
9397
+ var replay_id = this._get_session_replay_id();
9398
+ if (replay_id) {
9399
+ var query_params = _.HTTPBuildQuery({
9400
+ 'replay_id': replay_id,
9401
+ 'distinct_id': this.get_distinct_id(),
9402
+ 'token': this.get_config('token')
9403
+ });
9404
+ replay_url = 'https://mixpanel.com/projects/replay-redirect?' + query_params;
9405
+ }
9406
+ return replay_url;
9407
+ };
9408
+
9409
+ MixpanelLib.prototype._get_session_replay_id = function () {
9410
+ var replay_id = null;
9411
+ if (this._recorder) {
9412
+ replay_id = this._recorder['replayId'];
9413
+ }
9414
+ return replay_id || null;
9415
+ };
9416
+
9310
9417
  // Private methods
9311
9418
 
9312
9419
  MixpanelLib.prototype._loaded = function() {
@@ -11033,6 +11140,7 @@ define((function () { 'use strict';
11033
11140
  MixpanelLib.prototype['start_session_recording'] = MixpanelLib.prototype.start_session_recording;
11034
11141
  MixpanelLib.prototype['stop_session_recording'] = MixpanelLib.prototype.stop_session_recording;
11035
11142
  MixpanelLib.prototype['get_session_recording_properties'] = MixpanelLib.prototype.get_session_recording_properties;
11143
+ MixpanelLib.prototype['get_session_replay_url'] = MixpanelLib.prototype.get_session_replay_url;
11036
11144
  MixpanelLib.prototype['DEFAULT_API_ROUTES'] = DEFAULT_API_ROUTES;
11037
11145
 
11038
11146
  // MixpanelPersistence Exports