@pendo/agent 2.280.0 → 2.281.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.
@@ -3901,8 +3901,8 @@ var SERVER = '';
3901
3901
  var ASSET_HOST = '';
3902
3902
  var ASSET_PATH = '';
3903
3903
  var DESIGNER_ENV = '';
3904
- var VERSION = '2.280.0_';
3905
- var PACKAGE_VERSION = '2.280.0';
3904
+ var VERSION = '2.281.0_';
3905
+ var PACKAGE_VERSION = '2.281.0';
3906
3906
  var LOADER = 'xhr';
3907
3907
  /* eslint-enable agent-eslint-rules/no-gulp-env-references */
3908
3908
  /**
@@ -8699,6 +8699,13 @@ function dom(selection, context) {
8699
8699
  });
8700
8700
  self.context = context;
8701
8701
  self.length = nodes.length;
8702
+ if (typeof Symbol !== 'undefined' && Symbol.iterator) {
8703
+ self[Symbol.iterator] = function* () {
8704
+ for (const node of nodes) {
8705
+ yield node;
8706
+ }
8707
+ };
8708
+ }
8702
8709
  return self;
8703
8710
  }
8704
8711
 
@@ -11264,6 +11271,7 @@ class ElementGetter {
11264
11271
  if (el && isInDoc)
11265
11272
  return el;
11266
11273
  if (el && !isInDoc) {
11274
+ this.teardown(el);
11267
11275
  return undefined;
11268
11276
  }
11269
11277
  el = dom(this.cssSelector)[0];
@@ -11288,15 +11296,14 @@ class ElementGetter {
11288
11296
  el.addEventListener(event, (e) => this.onEvent(e));
11289
11297
  }
11290
11298
  }
11291
- this.listeners[event] = this.listeners[event]
11292
- ? this.listeners[event].push(callback) : [].concat(callback);
11299
+ this.listeners[event] = this.listeners[event] || [];
11300
+ this.listeners[event].push(callback);
11293
11301
  }
11294
11302
  onEvent(evt) {
11295
11303
  const type = evt.type;
11296
11304
  _.each(this.listeners[type], (cb) => cb(evt));
11297
11305
  }
11298
- teardown() {
11299
- const el = this.get();
11306
+ teardown(el = this.get()) {
11300
11307
  if (el) {
11301
11308
  _.each(this.events, (evtType) => el.removeEventListener(evtType, this.onEvent));
11302
11309
  }
@@ -23862,13 +23869,16 @@ var onGuideDismissed = function (evt, step) {
23862
23869
  }
23863
23870
  };
23864
23871
  /**
23865
- * Snoozes the current guide. Guide will redisplay after snooze length, or default
23866
- * of one day. If another guide is eligible to be shown, it will show after snooze.
23872
+ * Snoozes the current guide. If another guide is eligible to be shown automatically, it will show after snooze.
23873
+ * Guide will redisplay after one day by default, or a custom `snoozeDuration` can be set in milliseconds as the third argument.
23867
23874
  *
23868
23875
  * @access public
23869
23876
  * @category Guides
23870
23877
  * @example
23871
23878
  * pendo.onGuideSnoozed()
23879
+ * @example
23880
+ * pendo.onGuideSnoozed(null, null, 30 * 60 * 1000)
23881
+ * // snoozes guide for 30 minutes
23872
23882
  */
23873
23883
  var onGuideSnoozed = function (evt, step, snoozeDuration) {
23874
23884
  log.info('onGuideSnoozed called', { 'contexts': ['guides'] });
@@ -33006,28 +33016,30 @@ var DesignerConnect = (function () {
33006
33016
  }
33007
33017
  }
33008
33018
  function onConnectMessage(data, msg) {
33009
- var messageOrigin = msg.origin;
33010
- globalPendo.stopGuides();
33011
- globalPendo.buffers.lock();
33012
- var designerWindow = msg.source;
33013
- setDesignerWindow(designerWindow);
33014
- // Special handling for classic designer (p1)
33015
- if (!globalPendo.designerv2.hostConfig) {
33016
- pluginAPI.store.dispatch('frames/leave'); // p1 still needs to stop the frame controller
33017
- // need an unload listener for classic designer to tell the designer to remove it from its frame list
33018
- unloadListener = globalPendo._.partial(disconnectDesigner, designerWindow, messageOrigin);
33019
- pluginAPI.Events.appUnloaded.on(unloadListener);
33020
- }
33021
- addDesignerFunctionality();
33022
- tellMaster(msg.source, {
33023
- 'status': 'success',
33024
- 'type': 'connect'
33025
- }, messageOrigin);
33026
- if (findModuleByName('selection.js')) {
33027
- globalPendo.log('Designer Modules already loaded.');
33028
- tellMaster({ 'type': 'ready' }, messageOrigin);
33029
- }
33030
- pluginAPI.Events.designerLaunched.trigger();
33019
+ if (!globalPendo.designer) {
33020
+ var messageOrigin = msg.origin;
33021
+ globalPendo.stopGuides();
33022
+ globalPendo.buffers.lock();
33023
+ var designerWindow = msg.source;
33024
+ setDesignerWindow(designerWindow);
33025
+ // Special handling for classic designer (p1)
33026
+ if (!globalPendo.designerv2.hostConfig) {
33027
+ pluginAPI.store.dispatch('frames/leave'); // p1 still needs to stop the frame controller
33028
+ // need an unload listener for classic designer to tell the designer to remove it from its frame list
33029
+ unloadListener = globalPendo._.partial(disconnectDesigner, designerWindow, messageOrigin);
33030
+ pluginAPI.Events.appUnloaded.on(unloadListener);
33031
+ }
33032
+ addDesignerFunctionality();
33033
+ tellMaster(msg.source, {
33034
+ 'status': 'success',
33035
+ 'type': 'connect'
33036
+ }, messageOrigin);
33037
+ if (findModuleByName('selection.js')) {
33038
+ globalPendo.log('Designer Modules already loaded.');
33039
+ tellMaster({ 'type': 'ready' }, messageOrigin);
33040
+ }
33041
+ pluginAPI.Events.designerLaunched.trigger();
33042
+ }
33031
33043
  }
33032
33044
  function disconnectDesigner(designerWindow, messageOrigin) {
33033
33045
  globalPendo.buffers.unlock();
@@ -36572,7 +36584,7 @@ class SessionManager {
36572
36584
  }
36573
36585
  isExpired(sessionInfo, timestamp) {
36574
36586
  const prevLastEmitTime = _.get(sessionInfo, 'timestamp');
36575
- if (prevLastEmitTime && timestamp - prevLastEmitTime > this.inactivityDuration) {
36587
+ if (!this.pendo._.isNumber(prevLastEmitTime) || timestamp - prevLastEmitTime > this.inactivityDuration) {
36576
36588
  return true;
36577
36589
  }
36578
36590
  return false;
@@ -36841,7 +36853,6 @@ function calculateReferrerValue({ referrer, currentHost }) {
36841
36853
  const referrerHost = referrerUrl.hostname;
36842
36854
  let referrerValue = 'self';
36843
36855
  if (referrerHost !== currentHost) {
36844
- // TODO: what about subdomains or other variations you own that you'd rather consider 'self' instead?
36845
36856
  referrerValue = referrerHost === null || referrerHost === void 0 ? void 0 : referrerHost.toLowerCase();
36846
36857
  }
36847
36858
  return referrerValue;
@@ -36855,60 +36866,65 @@ const paidMediumRegex = new RegExp('^(.*cp.*|ppc|retargeting)$');
36855
36866
  // add other email-related values here, if needed
36856
36867
  const emailRegex = new RegExp('(.*email.*|.*e_mail.*|.*e mail.*|.*e-mail.*)', 'i');
36857
36868
  const channels = [
36858
- ['Email', ({ source, medium, referrer }) => {
36859
- return referrer === 'direct' &&
36860
- (medium.match(emailRegex) !== null ||
36861
- source.match(emailRegex) !== null ||
36862
- medium === 'newsletter');
36869
+ [undefined, ({ medium, source, referrer, gclid, fbclid }) => {
36870
+ return !medium && !source && referrer === 'self' && !gclid && !fbclid;
36871
+ }],
36872
+ ['Direct', ({ source, medium, referrer }) => {
36873
+ return !medium && (!source || source === 'direct') && referrer === 'direct';
36874
+ }],
36875
+ ['Cross-network', ({ source, medium }) => {
36876
+ return medium === 'cross-network' || source.match(crossNetworkPlatformsRegex);
36863
36877
  }],
36864
36878
  ['Paid Search', ({ source, medium, gclid, _ }) => {
36865
36879
  return gclid ||
36866
36880
  medium === 'paid-search' ||
36867
36881
  (medium.match(paidMediumRegex) && _.contains(searchProviders, source));
36868
36882
  }],
36869
- ['Organic Search', ({ source, medium, referrer, _ }) => {
36870
- return (medium === 'organic' && _.contains(searchProviders, source)) || (!medium && isReferral(searchProviderRegex, referrer));
36871
- }],
36872
- ['Display', ({ medium, _ }) => {
36873
- return _.contains(['display', 'cpm', 'banner', 'interstitial', 'expandable', 'programmatic'], medium);
36874
- }],
36875
36883
  ['Paid Social', ({ source, medium, fbclid, _ }) => {
36876
36884
  return fbclid ||
36877
36885
  medium === 'paid-social' ||
36878
36886
  (medium.match(paidMediumRegex) && _.contains(socialMediaSources, source));
36879
36887
  }],
36888
+ ['Paid Video', ({ medium, source, _ }) => {
36889
+ return medium == 'paid-video' ||
36890
+ (!medium.match(paidMediumRegex) && _.contains(videoSites, source));
36891
+ }],
36892
+ ['Paid Shopping', ({ medium }) => {
36893
+ return medium == 'shopping' || medium == 'paid-shopping';
36894
+ }],
36895
+ ['Display', ({ medium, _ }) => {
36896
+ return _.contains(['display', 'cpm', 'banner', 'interstitial', 'expandable', 'programmatic'], medium);
36897
+ }],
36898
+ ['Paid Other', ({ medium, source, _ }) => {
36899
+ // Must be after all other paid channels in the list
36900
+ return medium == 'paid-other' ||
36901
+ medium.match(paidMediumRegex);
36902
+ }],
36903
+ ['Organic Search', ({ source, medium, referrer, _ }) => {
36904
+ return (medium === 'organic' && _.contains(searchProviders, source)) || (!medium && isReferral(searchProviderRegex, referrer));
36905
+ }],
36880
36906
  ['Organic Social', ({ source, medium, referrer, _ }) => {
36881
36907
  return (!medium && isReferral(socialMediaRegex, referrer)) ||
36882
36908
  medium === 'social' ||
36883
36909
  _.contains(socialMediaSources, source);
36884
36910
  }],
36885
- ['Paid Shopping', ({ medium }) => {
36886
- return medium == 'shopping' || medium == 'paid-shopping';
36911
+ ['Organic Video', ({ medium, referrer }) => {
36912
+ return medium === 'video' ||
36913
+ (!medium && isReferral(videoRegex, referrer));
36887
36914
  }],
36888
36915
  ['Organic Shopping', ({ medium, referrer }) => {
36889
36916
  return medium === 'organic-shopping' ||
36890
36917
  (!medium && isReferral(shoppingRegex, referrer));
36891
36918
  }],
36892
- ['Paid Video', ({ medium, source, _ }) => {
36893
- return medium == 'paid-video' ||
36894
- (!medium.match(paidMediumRegex) && _.contains(videoSites, source));
36895
- }],
36896
- ['Organic Video', ({ medium, referrer }) => {
36897
- return medium === 'video' ||
36898
- (!medium && isReferral(videoRegex, referrer));
36919
+ ['Email', ({ source, medium, referrer }) => {
36920
+ return referrer === 'direct' &&
36921
+ (medium.match(emailRegex) !== null ||
36922
+ source.match(emailRegex) !== null ||
36923
+ medium === 'newsletter');
36899
36924
  }],
36900
36925
  ['Affiliates', ({ medium }) => {
36901
36926
  return medium === 'affiliate' || medium === 'partner';
36902
36927
  }],
36903
- ['Audio', ({ medium }) => {
36904
- return medium === 'podcast' || medium === 'audio';
36905
- }],
36906
- ['Cross-network', ({ source, medium }) => {
36907
- return medium === 'cross-network' || source.match(crossNetworkPlatformsRegex);
36908
- }],
36909
- ['Other Advertising', ({ medium }) => {
36910
- return medium === 'cpv' || medium === 'cpa';
36911
- }],
36912
36928
  ['SMS', ({ source, medium }) => {
36913
36929
  return source === 'sms' || medium === 'sms';
36914
36930
  }],
@@ -36917,20 +36933,15 @@ const channels = [
36917
36933
  medium.indexOf('notification') >= 0 ||
36918
36934
  source === 'firebase';
36919
36935
  }],
36936
+ ['Audio', ({ medium }) => {
36937
+ return medium === 'podcast' || medium === 'audio';
36938
+ }],
36920
36939
  ['Referral', ({ medium, referrer }) => {
36921
36940
  return medium === 'referral' ||
36922
36941
  (!!referrer && referrer !== 'self' && referrer !== 'direct');
36923
36942
  }],
36924
- ['Paid Other', ({ medium, source, _ }) => {
36925
- // Must be after all other paid channels in the list
36926
- return medium == 'paid-other' ||
36927
- medium.match(paidMediumRegex);
36928
- }],
36929
- ['Other', ({ medium }) => {
36930
- return !!medium;
36931
- }],
36932
- ['Direct', ({ source, medium }) => {
36933
- return !medium && !source;
36943
+ ['Other', ({ medium, source }) => {
36944
+ return !!medium || !!source;
36934
36945
  }]
36935
36946
  ];
36936
36947
  const WEB_ANALYTICS_STORAGE_KEY = 'utm';
@@ -36972,39 +36983,35 @@ class WebAnalytics {
36972
36983
  const locationUrl = new URL(url);
36973
36984
  const currentHost = locationUrl.hostname;
36974
36985
  const referrerValue = calculateReferrerValue({ referrer, currentHost });
36975
- // Check if this is a self-referral.
36976
- if (referrerValue !== 'self') {
36977
- const parsedUrl = this.api.util.parseUrl(url);
36978
- if (!parsedUrl)
36979
- return {};
36980
- const { query } = parsedUrl;
36981
- // It is not a self referral, therefore we should calculate the channel and track the visit
36982
- const source = query.utm_source;
36983
- const medium = query.utm_medium;
36984
- const campaign = query.utm_campaign;
36985
- const term = query.utm_term;
36986
- const content = query.utm_content;
36987
- const gclid = query.gclid;
36988
- const fbclid = query.fbclid;
36989
- if (this.pendo._.compact([source, medium, campaign, term, content, gclid]).length) {
36990
- const utm = {
36991
- source,
36992
- medium,
36993
- campaign,
36994
- term,
36995
- content
36996
- };
36997
- utm.channel = this.calculateUtmChannel({ referrerValue, utm, gclid, fbclid });
36998
- if (referrer && this.api.ConfigReader.get(CAPTURE_REFERRER_CONFIG_KEY)) {
36999
- utm.referrer = referrer;
37000
- }
37001
- else if (referrerValue !== 'direct') {
37002
- utm.referrer = referrerValue;
37003
- }
37004
- return utm;
37005
- }
36986
+ const parsedUrl = this.api.util.parseUrl(url);
36987
+ if (!parsedUrl)
36988
+ return {};
36989
+ const { query } = parsedUrl;
36990
+ const source = query.utm_source;
36991
+ const medium = query.utm_medium;
36992
+ const campaign = query.utm_campaign;
36993
+ const term = query.utm_term;
36994
+ const content = query.utm_content;
36995
+ const gclid = query.gclid;
36996
+ const fbclid = query.fbclid;
36997
+ const utm = {
36998
+ source,
36999
+ medium,
37000
+ campaign,
37001
+ term,
37002
+ content
37003
+ };
37004
+ utm.channel = this.calculateUtmChannel({ referrerValue, utm, gclid, fbclid });
37005
+ if (referrer && this.api.ConfigReader.get(CAPTURE_REFERRER_CONFIG_KEY)) {
37006
+ utm.referrer = referrer;
37006
37007
  }
37007
- return {};
37008
+ else if (referrerValue === 'self') {
37009
+ utm.referrer = currentHost;
37010
+ }
37011
+ else if (referrerValue !== 'direct') {
37012
+ utm.referrer = referrerValue;
37013
+ }
37014
+ return utm;
37008
37015
  }
37009
37016
  calculateUtmChannel({ referrerValue, utm, gclid, fbclid }) {
37010
37017
  var _a, _b;
@@ -37022,11 +37029,9 @@ class WebAnalytics {
37022
37029
  if (channelPair) {
37023
37030
  return channelPair[0];
37024
37031
  }
37025
- return 'Direct';
37026
37032
  }
37027
37033
  storeParameters(params, storage) {
37028
37034
  if (this.pendo._.size(params) > 0) {
37029
- this.utm = params;
37030
37035
  storage.write(WEB_ANALYTICS_STORAGE_KEY, JSON.stringify(params), undefined, false, true, this.suffix);
37031
37036
  }
37032
37037
  }
@@ -37042,16 +37047,20 @@ class WebAnalytics {
37042
37047
  unsubscribe();
37043
37048
  }
37044
37049
  this.subscriptions.length = 0;
37045
- this.utm = null;
37046
37050
  }
37047
37051
  addUtmToEvent(event) {
37048
- if (this.utm) {
37052
+ let utm;
37053
+ try {
37054
+ utm = this.loadParameters(this.api.agentStorage);
37055
+ }
37056
+ catch (e) {
37057
+ }
37058
+ if (utm) {
37049
37059
  const capturedEvent = event.data[0];
37050
- capturedEvent.utm = this.utm;
37060
+ capturedEvent.utm = utm;
37051
37061
  }
37052
37062
  }
37053
37063
  sessionChanged() {
37054
- this.utm = null;
37055
37064
  const agentStorage = this.api.agentStorage;
37056
37065
  agentStorage.clear(WEB_ANALYTICS_STORAGE_KEY);
37057
37066
  agentStorage.clear(WEB_ANALYTICS_STORAGE_KEY, false, this.suffix);
@@ -37615,7 +37624,7 @@ class DOMPrompt {
37615
37624
  // capture value from copy / paste
37616
37625
  this.capturePromptValue();
37617
37626
  }, true);
37618
- this.inputEl.addEventListener('keydown', (evt) => {
37627
+ this.inputEl.addEventListener('keyup', (evt) => {
37619
37628
  const wasEnterKey = evt.code === 'Enter';
37620
37629
  this.capturePromptValue(wasEnterKey);
37621
37630
  if (wasEnterKey) {
@@ -37623,6 +37632,7 @@ class DOMPrompt {
37623
37632
  }
37624
37633
  }, true);
37625
37634
  this.submitEl.addEventListener('click', () => {
37635
+ this.capturePromptValue();
37626
37636
  this.submit(this.latestPromptValue);
37627
37637
  }, true);
37628
37638
  this.promptContainer = new this.dom.Observer();
@@ -37636,14 +37646,15 @@ class DOMPrompt {
37636
37646
  }
37637
37647
  /*
37638
37648
  * Genernally we want to capture the value from "input" but there can be implementation
37639
- * dependent scenarios where the input's value has already been cleared by the time we
37640
- * get the event handler is called. So, in that case, we don't want to throw our saved value out.
37649
+ * dependent scenarios where the input's value has already been cleared by the time
37650
+ * the event handler is called. So, in that case, we don't want to throw our saved value out.
37641
37651
  */
37642
37652
  capturePromptValue(onlyUpdateIfNotEmpty = false) {
37643
37653
  const tmp = this.getPromptValue();
37644
37654
  if (tmp || !onlyUpdateIfNotEmpty) {
37645
37655
  this.latestPromptValue = tmp;
37646
37656
  }
37657
+ return tmp;
37647
37658
  }
37648
37659
  getPromptValue() {
37649
37660
  return this.inputEl.getText();
@@ -49308,6 +49319,11 @@ function initCanvasWebGLMutationObserver(cb, win, blockClass, blockSelector) {
49308
49319
  handlers.forEach((h) => h());
49309
49320
  };
49310
49321
  }
49322
+ function WorkerWrapper(options) {
49323
+ return function() {
49324
+ return { onerror: null, onmessage: null };
49325
+ };
49326
+ }
49311
49327
  class CanvasManager {
49312
49328
  constructor(options) {
49313
49329
  __publicField(this, "pendingCanvasMutations", /* @__PURE__ */ new Map());
@@ -49338,6 +49354,10 @@ class CanvasManager {
49338
49354
  this.mirror = options.mirror;
49339
49355
  if (recordCanvas && sampling === "all")
49340
49356
  this.initCanvasMutationObserver(win, blockClass, blockSelector);
49357
+ if (recordCanvas && typeof sampling === "number")
49358
+ this.initCanvasFPSObserver(sampling, win, blockClass, blockSelector, {
49359
+ dataURLOptions
49360
+ });
49341
49361
  }
49342
49362
  reset() {
49343
49363
  this.pendingCanvasMutations.clear();
@@ -49355,6 +49375,100 @@ class CanvasManager {
49355
49375
  unlock() {
49356
49376
  this.locked = false;
49357
49377
  }
49378
+ initCanvasFPSObserver(fps, win, blockClass, blockSelector, options) {
49379
+ const canvasContextReset = initCanvasContextObserver(
49380
+ win,
49381
+ blockClass,
49382
+ blockSelector,
49383
+ true
49384
+ );
49385
+ const snapshotInProgressMap = /* @__PURE__ */ new Map();
49386
+ const worker = new WorkerWrapper();
49387
+ worker.onmessage = (e2) => {
49388
+ const { id } = e2.data;
49389
+ snapshotInProgressMap.set(id, false);
49390
+ if (!("base64" in e2.data)) return;
49391
+ const { base64, type, width, height } = e2.data;
49392
+ this.mutationCb({
49393
+ id,
49394
+ type: CanvasContext["2D"],
49395
+ commands: [
49396
+ {
49397
+ property: "clearRect",
49398
+ // wipe canvas
49399
+ args: [0, 0, width, height]
49400
+ },
49401
+ {
49402
+ property: "drawImage",
49403
+ // draws (semi-transparent) image
49404
+ args: [
49405
+ {
49406
+ rr_type: "ImageBitmap",
49407
+ args: [
49408
+ {
49409
+ rr_type: "Blob",
49410
+ data: [{ rr_type: "ArrayBuffer", base64 }],
49411
+ type
49412
+ }
49413
+ ]
49414
+ },
49415
+ 0,
49416
+ 0
49417
+ ]
49418
+ }
49419
+ ]
49420
+ });
49421
+ };
49422
+ const timeBetweenSnapshots = 1e3 / fps;
49423
+ let lastSnapshotTime = 0;
49424
+ let rafId;
49425
+ const getCanvas = () => {
49426
+ const matchedCanvas = [];
49427
+ win.document.querySelectorAll("canvas").forEach((canvas) => {
49428
+ if (!isBlocked(canvas, blockClass, blockSelector, true)) {
49429
+ matchedCanvas.push(canvas);
49430
+ }
49431
+ });
49432
+ return matchedCanvas;
49433
+ };
49434
+ const takeCanvasSnapshots = (timestamp) => {
49435
+ if (lastSnapshotTime && timestamp - lastSnapshotTime < timeBetweenSnapshots) {
49436
+ rafId = requestAnimationFrame(takeCanvasSnapshots);
49437
+ return;
49438
+ }
49439
+ lastSnapshotTime = timestamp;
49440
+ getCanvas().forEach(async (canvas) => {
49441
+ var _a2;
49442
+ const id = this.mirror.getId(canvas);
49443
+ if (snapshotInProgressMap.get(id)) return;
49444
+ if (canvas.width === 0 || canvas.height === 0) return;
49445
+ snapshotInProgressMap.set(id, true);
49446
+ if (["webgl", "webgl2"].includes(canvas.__context)) {
49447
+ const context = canvas.getContext(canvas.__context);
49448
+ if (((_a2 = context == null ? void 0 : context.getContextAttributes()) == null ? void 0 : _a2.preserveDrawingBuffer) === false) {
49449
+ context.clear(context.COLOR_BUFFER_BIT);
49450
+ }
49451
+ }
49452
+ const bitmap = await createImageBitmap(canvas);
49453
+ worker.postMessage(
49454
+ {
49455
+ id,
49456
+ bitmap,
49457
+ width: canvas.width,
49458
+ height: canvas.height,
49459
+ dataURLOptions: options.dataURLOptions
49460
+ },
49461
+ [bitmap]
49462
+ );
49463
+ });
49464
+ rafId = requestAnimationFrame(takeCanvasSnapshots);
49465
+ };
49466
+ rafId = requestAnimationFrame(takeCanvasSnapshots);
49467
+ this.resetObservers = () => {
49468
+ canvasContextReset();
49469
+ cancelAnimationFrame(rafId);
49470
+ };
49471
+ }
49358
49472
  initCanvasMutationObserver(win, blockClass, blockSelector) {
49359
49473
  this.startRAFTimestamping();
49360
49474
  this.startPendingCanvasMutationFlusher();