@pendo/agent 2.293.1 → 2.295.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.
@@ -3658,7 +3658,6 @@ var ConfigReader = (function () {
3658
3658
  /* eslint-enable no-console */
3659
3659
  })();
3660
3660
 
3661
- var PROD = 'prod';
3662
3661
  var EXTENSION_INSTALL_TYPE = 'extension';
3663
3662
  var NATIVE_INSTALL_TYPE = 'native';
3664
3663
  function getInstallType() {
@@ -3668,23 +3667,6 @@ function getInstallType() {
3668
3667
  function isExtensionAgent() {
3669
3668
  return getInstallType() === EXTENSION_INSTALL_TYPE;
3670
3669
  }
3671
- function subscriptionBucketPrefix(env) {
3672
- if (env === PROD) {
3673
- return 'pendo-static';
3674
- }
3675
- return globalBucket(env);
3676
- }
3677
- function globalBucket(env) {
3678
- if (env === PROD) {
3679
- return 'pendo-io-static';
3680
- }
3681
- if (env === 'prod-jp') {
3682
- return 'pendo-jp-prod-static';
3683
- }
3684
- var match = /^prod-(.+)$/.exec(env);
3685
- var envId = match && match.length > 1 ? match[1] : env;
3686
- return 'pendo-' + envId + '-static';
3687
- }
3688
3670
 
3689
3671
  function isNativeCode(fn) { return /native/.test(fn); }
3690
3672
  const detectNativeBrowserAPI = (name) => {
@@ -3692,11 +3674,11 @@ const detectNativeBrowserAPI = (name) => {
3692
3674
  return (fn && isNativeCode(fn));
3693
3675
  };
3694
3676
 
3695
- var isOldIE = function (olderThan, tVersion) {
3677
+ function isOldIE(olderThan, tVersion) {
3696
3678
  olderThan = olderThan || 10;
3697
3679
  tVersion = isNaN(trident) ? false : (tVersion ? (trident < tVersion) : true);
3698
3680
  return (tVersion && (msie < olderThan));
3699
- };
3681
+ }
3700
3682
  // "borrowed" from angular.js
3701
3683
  // https://github.com/angular/angular.js/blob/v1.2.27/src/ng/sniffer.js
3702
3684
  // NOTE:
@@ -3710,11 +3692,12 @@ var isOldIE = function (olderThan, tVersion) {
3710
3692
  // we've added trident to track this actual version of IE so we can
3711
3693
  // attempt to handle these cases too.
3712
3694
  // holds major version number for IE or NaN for real browsers
3713
- var msie, trident;
3695
+ const msie = determineMSIE(getUA());
3696
+ const trident = determineTrident(getUA(), msie);
3714
3697
  function pint(str) { return parseInt(str, 10); }
3715
- var lowercase = function (string) { return _.isString(string) ? string.toLowerCase() : string; };
3698
+ function lowercase(string) { return _.isString(string) ? string.toLowerCase() : string; }
3716
3699
  function isMinimumIEVersion(minVersion) {
3717
- var sniffer = this;
3700
+ const sniffer = this;
3718
3701
  if (isNaN(sniffer.msie) || sniffer.msie == null)
3719
3702
  return true;
3720
3703
  return sniffer.msie >= minVersion;
@@ -3724,36 +3707,34 @@ function getUA() {
3724
3707
  }
3725
3708
  // IE 11 changed the format of the UserAgent string.
3726
3709
  // See http://msdn.microsoft.com/en-us/library/ms537503.aspx
3727
- var determineMSIE = function (ua) {
3728
- var v = pint((/msie (\d+)/.exec(lowercase(ua)) || [])[1]);
3729
- if (isNaN(v)) {
3730
- v = pint((/trident\/.*; rv:(\d+)/.exec(lowercase(ua)) || [])[1]);
3731
- }
3732
- return v;
3733
- };
3734
- msie = determineMSIE(getUA());
3735
- var determineTrident = function (ua, ieV) {
3736
- var v = pint((/trident\/(\d+)/.exec(lowercase(ua)) || [])[1]);
3737
- if (isNaN(v) && ieV == 7) {
3738
- v = 3;
3739
- }
3740
- return v;
3741
- };
3742
- trident = determineTrident(getUA(), msie);
3743
- var eventSupport = {};
3744
- var android = pint((/android (\d+)/.exec(lowercase(getUA())) || [])[1]);
3745
- var boxee = /Boxee/i.test(getUA());
3746
- var pdocument = window.document || {};
3747
- var documentMode = pdocument.documentMode;
3748
- var vendorPrefix;
3749
- var vendorRegex = /^(Moz|webkit|O|ms)(?=[A-Z])/;
3750
- var bodyStyle = pdocument.body && pdocument.body.style;
3751
- var transitions = false;
3752
- var animations = false;
3753
- var match;
3710
+ function determineMSIE(userAgent) {
3711
+ let version = pint((/msie (\d+)/.exec(lowercase(userAgent)) || [])[1]);
3712
+ if (isNaN(version)) {
3713
+ version = pint((/trident\/.*; rv:(\d+)/.exec(lowercase(userAgent)) || [])[1]);
3714
+ }
3715
+ return version;
3716
+ }
3717
+ function determineTrident(userAgent, ieVersion) {
3718
+ let version = pint((/trident\/(\d+)/.exec(lowercase(userAgent)) || [])[1]);
3719
+ if (isNaN(version) && ieVersion == 7) {
3720
+ version = 3;
3721
+ }
3722
+ return version;
3723
+ }
3724
+ const eventSupport = {};
3725
+ const android = pint((/android (\d+)/.exec(lowercase(getUA())) || [])[1]);
3726
+ const boxee = /Boxee/i.test(getUA());
3727
+ const pdocument = window.document || {};
3728
+ const documentMode = pdocument.documentMode;
3729
+ let vendorPrefix;
3730
+ const vendorRegex = /^(Moz|webkit|O|ms)(?=[A-Z])/;
3731
+ const bodyStyle = pdocument.body && pdocument.body.style;
3732
+ let transitions = false;
3733
+ let animations = false;
3734
+ let match;
3754
3735
  if (bodyStyle) {
3755
3736
  // eslint-disable-next-line guard-for-in
3756
- for (var prop in bodyStyle) {
3737
+ for (const prop in bodyStyle) {
3757
3738
  match = vendorRegex.exec(prop);
3758
3739
  if (match) {
3759
3740
  vendorPrefix = match[0];
@@ -3771,7 +3752,7 @@ if (bodyStyle) {
3771
3752
  animations = _.isString(pdocument.body.style.webkitAnimation);
3772
3753
  }
3773
3754
  }
3774
- var isChromeExtension = window.chrome && window.chrome.runtime && window.chrome.runtime.id;
3755
+ const isChromeExtension = window.chrome && window.chrome.runtime && window.chrome.runtime.id;
3775
3756
  // Android has history.pushState, but it does not update location correctly
3776
3757
  // so let's not use the history API at all.
3777
3758
  // http://code.google.com/p/android/issues/detail?id=17471
@@ -3779,28 +3760,28 @@ var isChromeExtension = window.chrome && window.chrome.runtime && window.chrome.
3779
3760
  // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
3780
3761
  // so let's not use the history API also
3781
3762
  // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
3782
- var shouldWrapNativeHistory = _.memoize(function shouldWrapNativeHistory() {
3783
- var hasNativeHistory = window.history && window.history.pushState && window.history.replaceState;
3784
- var isHistoryFrozen = window.history && _.isFunction(Object.isFrozen) && Object.isFrozen(window.history);
3785
- var historyDescriptors = window.history && _.isFunction(Object.getOwnPropertyDescriptors) && Object.getOwnPropertyDescriptors(window.history);
3786
- var isHistoryWriteable = historyDescriptors && _.get(historyDescriptors, 'pushState.writable', true) && _.get(historyDescriptors, 'replaceState.writable', true);
3787
- var hasAndroidSupport = !(android < 4);
3763
+ const shouldWrapNativeHistory = _.memoize(function shouldWrapNativeHistory() {
3764
+ const hasNativeHistory = window.history && window.history.pushState && window.history.replaceState;
3765
+ const isHistoryFrozen = window.history && _.isFunction(Object.isFrozen) && Object.isFrozen(window.history);
3766
+ const historyDescriptors = window.history && _.isFunction(Object.getOwnPropertyDescriptors) && Object.getOwnPropertyDescriptors(window.history);
3767
+ const isHistoryWriteable = historyDescriptors && _.get(historyDescriptors, 'pushState.writable', true) && _.get(historyDescriptors, 'replaceState.writable', true);
3768
+ const hasAndroidSupport = !(android < 4);
3788
3769
  return !!(hasNativeHistory && !isHistoryFrozen && isHistoryWriteable && hasAndroidSupport && !boxee && !isExtensionAgent());
3789
3770
  });
3790
3771
  function shouldWrapHashChange() {
3791
- var hasNativeHashChange = 'onhashchange' in window;
3772
+ const hasNativeHashChange = 'onhashchange' in window;
3792
3773
  // IE8 compatible mode lies
3793
- var hasSupportedDocMode = (!documentMode || documentMode > 7);
3774
+ const hasSupportedDocMode = (!documentMode || documentMode > 7);
3794
3775
  return !!(hasNativeHashChange && hasSupportedDocMode && !isExtensionAgent());
3795
3776
  }
3796
3777
  function isMobileUserAgent() {
3797
3778
  return (/Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i).test(getUA());
3798
3779
  }
3799
- // exports
3800
- var sniffer = {
3801
- // jshint -W018
3780
+ function isSafari(userAgent = getUA()) {
3781
+ return userAgent.indexOf('Safari') >= 0 && !userAgent.indexOf('Chrome') >= 0;
3782
+ }
3783
+ const sniffer = {
3802
3784
  supportsHistoryApi: shouldWrapNativeHistory,
3803
- // jshint +W018
3804
3785
  supportsHashChange: shouldWrapHashChange,
3805
3786
  hasEvent(event) {
3806
3787
  // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
@@ -3809,7 +3790,7 @@ var sniffer = {
3809
3790
  if (event == 'input' && msie == 9)
3810
3791
  return false;
3811
3792
  if (_.isUndefined(eventSupport[event])) {
3812
- var divElm = pdocument.createElement('div');
3793
+ const divElm = pdocument.createElement('div');
3813
3794
  eventSupport[event] = 'on' + event in divElm;
3814
3795
  }
3815
3796
  return eventSupport[event];
@@ -3820,7 +3801,7 @@ var sniffer = {
3820
3801
  android,
3821
3802
  msie,
3822
3803
  msieDocumentMode: documentMode,
3823
- safari: /apple/i.test(navigator.vendor),
3804
+ safari: isSafari(),
3824
3805
  sri: ('integrity' in document.createElement('script')),
3825
3806
  addEventListener: _.isFunction(window.addEventListener),
3826
3807
  MutationObserver: isNativeCode(window.MutationObserver),
@@ -3903,14 +3884,16 @@ var buildArrowDimensionsQM = function (dim, elementPos) {
3903
3884
  // If in module mode, all injected values will be empty.
3904
3885
  // In that case, we'll overwrite them on initAgent via overwriteBuiltConfigWithPendoConfig
3905
3886
  /* eslint-disable agent-eslint-rules/no-gulp-env-references */
3906
- var ENV = '';
3907
- var SERVER = '';
3908
- var ASSET_HOST = '';
3909
- var ASSET_PATH = '';
3910
- var DESIGNER_SERVER = '';
3911
- var VERSION = '2.293.1_';
3912
- var PACKAGE_VERSION = '2.293.1';
3913
- var LOADER = 'xhr';
3887
+ let ENV = '';
3888
+ let GLOBAL_BUCKET = '';
3889
+ let SUBSCRIPTION_BUCKET = '';
3890
+ let SERVER = '';
3891
+ let ASSET_HOST = '';
3892
+ let ASSET_PATH = '';
3893
+ let DESIGNER_SERVER = '';
3894
+ let VERSION = '2.295.0_';
3895
+ let PACKAGE_VERSION = '2.295.0';
3896
+ let LOADER = 'xhr';
3914
3897
  /* eslint-enable agent-eslint-rules/no-gulp-env-references */
3915
3898
  /**
3916
3899
  * Returns current version of the Agent as a string. Also directly accessible at `pendo.VERSION`.
@@ -3921,26 +3904,31 @@ var LOADER = 'xhr';
3921
3904
  * @example
3922
3905
  * pendo.getVersion() => '2.150.0'
3923
3906
  */
3924
- var getVersion = function () {
3907
+ const getVersion = function () {
3925
3908
  if (isBrowserInQuirksmode()) {
3926
3909
  return VERSION + '+quirksmode';
3927
3910
  }
3928
3911
  return VERSION;
3929
3912
  };
3930
- var overwriteBuiltConfigWithPendoConfig = function () {
3913
+ const overwriteBuiltConfigWithPendoConfig = function () {
3931
3914
  ENV = getPendoConfigValue('env') || ENV;
3932
3915
  SERVER = getPendoConfigValue('server') || SERVER;
3933
3916
  ASSET_HOST = getPendoConfigValue('assetHost') || ASSET_HOST;
3934
3917
  ASSET_PATH = getPendoConfigValue('assetPath') || ASSET_PATH;
3935
3918
  DESIGNER_SERVER = getPendoConfigValue('designerServer') || DESIGNER_SERVER;
3936
- var versionSuffix = isExtensionAgent() ? ENV + '_ext' : ENV;
3919
+ GLOBAL_BUCKET = getPendoConfigValue('globalBucket') || GLOBAL_BUCKET;
3920
+ SUBSCRIPTION_BUCKET = getPendoConfigValue('subscriptionBucket') || SUBSCRIPTION_BUCKET;
3921
+ const versionSuffix = isExtensionAgent() ? ENV + '_ext' : ENV;
3937
3922
  VERSION = PACKAGE_VERSION + '_' + versionSuffix;
3938
3923
  };
3939
3924
  function isProdAgent() {
3940
3925
  return ENV.indexOf('prod') !== -1;
3941
3926
  }
3942
- function isBetaAgent(env, config) {
3943
- return !/prod/.test(env) || isStagingServer(config);
3927
+ function isRCAgent() {
3928
+ return ENV.indexOf('rc') !== -1;
3929
+ }
3930
+ function isBetaAgent(config) {
3931
+ return !isProdAgent() || isStagingServer(config);
3944
3932
  }
3945
3933
 
3946
3934
  var rtrim = /^\s+|\s+$/g;
@@ -11424,25 +11412,17 @@ function isTrustedOrigin(host) {
11424
11412
  return true;
11425
11413
  if (host === getAssetHost())
11426
11414
  return true;
11427
- // Domains that Pendo owns
11428
- var patterns = [
11429
- /^https:\/\/(app|via|adopt)(\.eu|\.us|\.gov|\.jpn|\.hsbc|\.au)?\.pendo\.io$/,
11430
- /^https:\/\/((adopt\.)?us1\.)?(app|via|adopt)\.pendo\.io$/,
11431
- /^https:\/\/([0-9]{8}t[0-9]{4}-dot-)pendo-(io|eu|us1|govramp|jp-prod|hsbc|au)\.appspot\.com$/,
11432
- /^https:\/\/hotfix-(ops|app)-([0-9]+-dot-)pendo-(io|eu|us1|govramp|jp-prod|hsbc|au)\.appspot\.com$/,
11433
- /^https:\/\/pendo-(io|eu|us1|govramp|jp-prod|hsbc|au)-static\.storage\.googleapis\.com$/,
11434
- /^https:\/\/(us1\.)?cdn(\.eu|\.jpn|\.gov|\.hsbc|\.au)?\.pendo\.io$/
11435
- ];
11436
- if (!isProdAgent()) {
11437
- patterns = patterns.concat([
11438
- /^https:\/\/([a-zA-Z0-9-]+\.)*pendo-dev\.com$/,
11439
- /^https:\/\/([a-zA-Z0-9-]+-dot-)?pendo-(dev|test|io|us1|govramp|jp-prod|hsbc|au|batman|magic|atlas|wildlings|ionchef|mobile-guides|mobile-hummus|mobile-fbi|mobile-plat|eu|eu-dev|apollo|security|perfserf|freeze|armada|voc|mcfly|calypso|dap|scrum-ops|ml|helix|uat)\.appspot\.com$/,
11440
- /^https:\/\/via\.pendo\.local:\d{4}$/,
11441
- /^https:\/\/adopt\.pendo\.local:\d{4}$/,
11442
- /^https:\/\/local\.pendo\.io:\d{4}$/,
11443
- new RegExp('^https://pendo-' + ENV + '-static\\.storage\\.googleapis\\.com$')
11444
- ]);
11445
- }
11415
+ // Domains that Pendo owns, will be swapped in by build patches
11416
+ const patterns = [
11417
+ /^https:\/\/(adopt\.)?((us1)\.)?(app|via|adopt|cdn)(\.(au|eu|gov|hsbc|jpn))?\.pendo\.io$/,
11418
+ /^https:\/\/([0-9]{8}t[0-9]{4}-dot-)pendo-(au|eu|govramp|hsbc|io|jp-prod|us1)\.appspot\.com$/,
11419
+ /^https:\/\/hotfix-(ops|app)-([0-9]+-dot-)pendo-(au|eu|govramp|hsbc|io|jp-prod|us1)\.appspot\.com$/,
11420
+ /^https:\/\/pendo-(au|eu|govramp|hsbc|io|jp-prod|us1)-static\.storage\.googleapis\.com$/,
11421
+ /^https:\/\/([a-zA-Z0-9-]+\.?)*\.pendo-dev\.com$/,
11422
+ /^https:\/\/([a-zA-Z0-9-]+-dot-)?pendo-(apollo|armada|atlas|batman|calypso|dap|dev|dr-us-east1|eu-dev|freeze|helix|link|magic|mcfly|ml|mobile-fbi|mobile-guides|mobile-hummus|mobile-plat|perfserf|voc|wildlings|ionchef|scrum-ops|security|test|uat|au|eu|govramp|hsbc|io|jp-prod|us1)\.appspot\.com$/,
11423
+ /^https:\/\/(via|adopt|local)\.pendo\.(local|io):\d{4}$/,
11424
+ /^https:\/\/pendo-(apollo|armada|atlas|batman|calypso|dap|dev|dr-us-east1|eu-dev|freeze|helix|link|magic|mcfly|ml|mobile-fbi|mobile-guides|mobile-hummus|mobile-plat|perfserf|voc|wildlings|ionchef|scrum-ops|security|test|uat)-static\.storage\.googleapis\.com$/
11425
+ ]; // eslint-disable-line agent-eslint-rules/no-gulp-env-references
11446
11426
  var adoptHost = ConfigReader.get('adoptHost');
11447
11427
  if (adoptHost) {
11448
11428
  var fullHostname = 'https://' + adoptHost;
@@ -11491,7 +11471,7 @@ function addIntegrityAttribute(element, url) {
11491
11471
  var trustedOriginForLoadResource = function (origin) {
11492
11472
  var noAllowedOriginServers = ConfigReader.get('allowedOriginServers', []).length === 0;
11493
11473
  var isAdoptPartner = treatAsAdoptPartner();
11494
- if (noAllowedOriginServers || isAdoptPartner || !isBetaAgent(ENV, getPendoConfig())) {
11474
+ if (noAllowedOriginServers || isAdoptPartner || !isBetaAgent(getPendoConfig())) {
11495
11475
  return true;
11496
11476
  }
11497
11477
  return isTrustedOrigin(origin);
@@ -12231,6 +12211,7 @@ class PerformanceMonitor {
12231
12211
  return detectNativeBrowserAPI('performance.mark') &&
12232
12212
  detectNativeBrowserAPI('performance.measure') &&
12233
12213
  detectNativeBrowserAPI('performance.getEntries') &&
12214
+ detectNativeBrowserAPI('performance.getEntriesByName') &&
12234
12215
  detectNativeBrowserAPI('performance.clearMarks') &&
12235
12216
  detectNativeBrowserAPI('performance.clearMeasures');
12236
12217
  }
@@ -12250,8 +12231,10 @@ class PerformanceMonitor {
12250
12231
  name = `pendo-${name}`;
12251
12232
  const startMark = `${name}-start`;
12252
12233
  const stopMark = `${name}-stop`;
12253
- performance.measure(name, startMark, stopMark);
12254
- this._count(this._measures, name);
12234
+ if (performance.getEntriesByName(startMark).length && performance.getEntriesByName(stopMark).length) {
12235
+ performance.measure(name, startMark, stopMark);
12236
+ this._count(this._measures, name);
12237
+ }
12255
12238
  }
12256
12239
  _count(registry, name) {
12257
12240
  if (!registry[name]) {
@@ -13304,6 +13287,7 @@ var getValidTarget = function (node) {
13304
13287
  */
13305
13288
  var handle_event = function (evt) {
13306
13289
  try {
13290
+ PerformanceMonitor$1.startTimer('event-captured');
13307
13291
  if (dom.data.get(evt, 'counted'))
13308
13292
  return;
13309
13293
  dom.data.set(evt, 'counted', true);
@@ -13343,6 +13327,9 @@ var handle_event = function (evt) {
13343
13327
  catch (e) {
13344
13328
  log.critical('pendo.io while handling event', { error: e });
13345
13329
  }
13330
+ finally {
13331
+ PerformanceMonitor$1.stopTimer('event-captured');
13332
+ }
13346
13333
  };
13347
13334
  function getClickEventProperties(target) {
13348
13335
  const eventPropertyHandler = getEventPropertyHandler(target);
@@ -14608,11 +14595,11 @@ function replaceWithContentHost(str) {
14608
14595
  var contentHost = ConfigReader.getLocalConfig('contentHost');
14609
14596
  if (!contentHost || !str)
14610
14597
  return str;
14611
- var subBucketPattern = new RegExp('(https:)?\\/\\/' + subscriptionBucketPrefix(ENV) + '-\\d+\\.storage\\.googleapis\\.com', 'g');
14612
- var globalBucketPattern = new RegExp('(https:)?\\/\\/' + globalBucket(ENV) + '\\.storage\\.googleapis\\.com', 'g');
14598
+ var subBucketPattern = new RegExp(`(https:)?\\/\\/${SUBSCRIPTION_BUCKET}-\\d+\\.storage\\.googleapis\\.com`, 'g');
14599
+ var globalBucketPattern = new RegExp(`(https:)?\\/\\/${GLOBAL_BUCKET}\\.storage\\.googleapis\\.com`, 'g');
14613
14600
  return str.replace(subBucketPattern, contentHost)
14614
14601
  .replace(globalBucketPattern, contentHost)
14615
- .replace(new RegExp('(https:)?\\/\\/' + escapeRegExp(ASSET_HOST), 'g'), contentHost);
14602
+ .replace(new RegExp(`(https:)?\\/\\/${escapeRegExp(ASSET_HOST)}`, 'g'), contentHost);
14616
14603
  }
14617
14604
  function replaceObjectWithContentHost(obj) {
14618
14605
  var contentHost = ConfigReader.getLocalConfig('contentHost');
@@ -17866,6 +17853,9 @@ function getAllowedAttributes(attributeKeyValueMap, stepId, guideId, type) {
17866
17853
  function buildNodeFromJSON(json, step, guides) {
17867
17854
  step = step || { id: 'unknown', guideId: 'unknown' };
17868
17855
  json.props = getAllowedAttributes(json.props, step.id, step.guideId, json.type);
17856
+ if (step.isDarkMode && json.darkModeProps) {
17857
+ json.darkModeProps = getAllowedAttributes(json.darkModeProps, step.id, step.guideId, json.type);
17858
+ }
17869
17859
  var curNode = dom(document.createElement(json.type));
17870
17860
  // APP-81040 calling code in the pendo app (and possibly elsewhere) depends on
17871
17861
  // curNode.getParent() returning non-null in some cases. The fact that it used to
@@ -17885,7 +17875,11 @@ function buildNodeFromJSON(json, step, guides) {
17885
17875
  }
17886
17876
  _.each(json.props, function (propValue, propKey) {
17887
17877
  if (propKey === 'style') {
17888
- curNode.css(json.props.style);
17878
+ let nodeStyle = json.props.style;
17879
+ if (step.isDarkMode && json.darkModeProps) {
17880
+ nodeStyle = Object.assign(Object.assign({}, json.props.style), json.darkModeProps.style);
17881
+ }
17882
+ curNode.css(nodeStyle);
17889
17883
  }
17890
17884
  else if (propKey === 'data-pendo-code-block' && propValue === true && !ConfigReader.get('preventCodeInjection')) {
17891
17885
  const htmlString = step.getContent();
@@ -17910,13 +17904,23 @@ function buildNodeFromJSON(json, step, guides) {
17910
17904
  if (nonce) {
17911
17905
  curNode.attr('nonce', nonce);
17912
17906
  }
17907
+ let css = json.css;
17908
+ if (json.forDarkMode) {
17909
+ const darkModeSelector = _.get(step, 'attributes.darkMode.selector', '');
17910
+ css = _.map(css, function (rule) {
17911
+ return {
17912
+ styles: rule.styles,
17913
+ selector: `${darkModeSelector} ${rule.selector}`
17914
+ };
17915
+ });
17916
+ }
17913
17917
  // TODO: make this render building-block pseudo-styles properly for IE7-8. This current functionality allows guides to render in IE but there are lots of styling problems.
17914
17918
  // `curNode.text` specifically breaks in IE8 since style tags text attributes are read only. From researching `node.styleSheet.cssText` is the correct way to do it.
17915
17919
  if (curNode.styleSheet) {
17916
- curNode.styleSheet.cssText = buildStyleTagContent(json.css);
17920
+ curNode.styleSheet.cssText = buildStyleTagContent(css);
17917
17921
  }
17918
17922
  else {
17919
- curNode.text(buildStyleTagContent(json.css));
17923
+ curNode.text(buildStyleTagContent(css));
17920
17924
  }
17921
17925
  }
17922
17926
  if (json.svgWidgetId) {
@@ -20614,6 +20618,21 @@ class AutoDisplayPhase {
20614
20618
  }
20615
20619
  }
20616
20620
 
20621
+ function syncColorMode(step, rerender = false) {
20622
+ const darkModeSelector = _.get(step, 'attributes.darkMode.selector', '');
20623
+ if (!darkModeSelector)
20624
+ return;
20625
+ const isDarkMode = pendo$1.Sizzle(darkModeSelector).length > 0;
20626
+ const darkModeChanged = step.isDarkMode !== isDarkMode;
20627
+ if (darkModeChanged) {
20628
+ step.isDarkMode = isDarkMode;
20629
+ if (rerender) {
20630
+ step.hide();
20631
+ step.show(step.seenReason);
20632
+ }
20633
+ }
20634
+ }
20635
+
20617
20636
  /*
20618
20637
  * Guide Loop
20619
20638
  *
@@ -20812,6 +20831,7 @@ function stepShowingProc(guide, step) {
20812
20831
  step.attributes.currentTextZoomFontSize = currentBrowserFontSize;
20813
20832
  }
20814
20833
  }
20834
+ syncColorMode(step, true);
20815
20835
  if (step.elementPathRule && targetElement && !SizzleProxy.matchesSelector(targetElement, step.elementPathRule)) {
20816
20836
  step.hide();
20817
20837
  return;
@@ -21887,6 +21907,11 @@ function startPreviewMode(window) {
21887
21907
  enableCookies(false);
21888
21908
  buffers.lock();
21889
21909
  setGuideLoader(PreviewGuideLoader);
21910
+ // This is to allow for data urls when previewing from the designer
21911
+ if (isInDesignerPreviewMode()) {
21912
+ const pendoConfig = getPendoConfig();
21913
+ pendoConfig.allowedOriginServers = [];
21914
+ }
21890
21915
  Events.startPreview.trigger();
21891
21916
  Events.leaderChanged.on(function (e) {
21892
21917
  store.dispatch('preview/write');
@@ -26340,53 +26365,152 @@ class NetworkRequestIntercept {
26340
26365
  extractXHRHeaders(xhr) {
26341
26366
  return xhr[PENDO_HEADERS_KEY] || {};
26342
26367
  }
26343
- patchNetwork() {
26368
+ generateRequestId() {
26369
+ const $stringLength = 8;
26370
+ return 'req_' + Date.now() + '_' + pendo$1.randomString($stringLength);
26371
+ }
26372
+ safelyReadResponse(response) {
26373
+ return __awaiter(this, void 0, void 0, function* () {
26374
+ try {
26375
+ const contentType = response.headers.get('content-type') || '';
26376
+ if (contentType.indexOf('application/json') !== -1) {
26377
+ return yield response.json();
26378
+ }
26379
+ else if (contentType.indexOf('text/') !== -1) {
26380
+ return yield response.text();
26381
+ }
26382
+ else {
26383
+ // For binary or unknown content types, just capture metadata
26384
+ return `[Binary content: ${contentType}]`;
26385
+ }
26386
+ }
26387
+ catch (e) {
26388
+ return '[Unable to read response body]';
26389
+ }
26390
+ });
26391
+ }
26392
+ extractResponseHeaders(response) {
26393
+ let headers = {};
26394
+ if (response.headers) {
26395
+ const headerEntries = Array.from(response.headers.entries());
26396
+ headers = this.entriesToObject(headerEntries);
26397
+ }
26398
+ return headers;
26399
+ }
26400
+ parseXHRResponseHeaders(headerString) {
26401
+ const headers = {};
26402
+ if (!headerString)
26403
+ return headers;
26404
+ const lines = headerString.split('\r\n');
26405
+ _.each(lines, line => {
26406
+ const parts = line.split(': ');
26407
+ if (parts.length === 2) {
26408
+ headers[parts[0]] = parts[1];
26409
+ }
26410
+ });
26411
+ return headers;
26412
+ }
26413
+ patchFetch() {
26344
26414
  const networkInterceptor = this;
26345
- if (networkInterceptor._networkPatched)
26346
- return;
26347
- networkInterceptor._networkPatched = true;
26348
- networkInterceptor._originalSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
26349
- XMLHttpRequest.prototype.setRequestHeader = function (header, value) {
26350
- this[PENDO_HEADERS_KEY] = this[PENDO_HEADERS_KEY] || {};
26351
- this[PENDO_HEADERS_KEY][header] = value;
26352
- return networkInterceptor._originalSetRequestHeader.apply(this, arguments);
26353
- };
26354
26415
  networkInterceptor._originalFetch = window.fetch;
26355
26416
  window.fetch = function (...args) {
26356
26417
  return __awaiter(this, void 0, void 0, function* () {
26357
26418
  const [url, config = {}] = args;
26358
26419
  const method = config.method || 'GET';
26420
+ const requestId = networkInterceptor.generateRequestId();
26421
+ // Capture request data
26359
26422
  try {
26360
26423
  const headers = networkInterceptor.extractHeaders(config.headers);
26361
26424
  const body = networkInterceptor.safelySerializeBody(config.body);
26362
26425
  _.each(networkInterceptor.callbackFns, ({ request }) => {
26363
26426
  if (_.isFunction(request))
26364
- request({ method, url, body, headers });
26427
+ request({ requestId, method, url, body, headers });
26365
26428
  });
26366
26429
  }
26367
26430
  catch (e) {
26368
26431
  _.each(networkInterceptor.callbackFns, ({ error }) => {
26369
26432
  if (_.isFunction(error))
26370
- error({ context: 'fetch', error: e });
26433
+ error({ context: 'fetchRequest', error: e });
26371
26434
  });
26372
26435
  }
26373
- return networkInterceptor._originalFetch.apply(this, args);
26436
+ // Make the actual fetch call and capture response
26437
+ try {
26438
+ const res = yield networkInterceptor._originalFetch.apply(this, args);
26439
+ // Clone the response to avoid consuming the body
26440
+ const responseClone = res.clone();
26441
+ try {
26442
+ // Capture response data
26443
+ const responseBody = yield networkInterceptor.safelyReadResponse(responseClone);
26444
+ const { status, statusText } = res;
26445
+ const headers = networkInterceptor.extractResponseHeaders(res);
26446
+ _.each(networkInterceptor.callbackFns, ({ response }) => {
26447
+ if (_.isFunction(response)) {
26448
+ response({
26449
+ requestId,
26450
+ status,
26451
+ statusText,
26452
+ body: responseBody,
26453
+ headers,
26454
+ url,
26455
+ method
26456
+ });
26457
+ }
26458
+ });
26459
+ }
26460
+ catch (e) {
26461
+ _.each(networkInterceptor.callbackFns, ({ error }) => {
26462
+ if (_.isFunction(error))
26463
+ error({ context: 'fetchResponse', error: e });
26464
+ });
26465
+ }
26466
+ return res;
26467
+ }
26468
+ catch (error) {
26469
+ // Handle network errors
26470
+ _.each(networkInterceptor.callbackFns, ({ response }) => {
26471
+ if (_.isFunction(response)) {
26472
+ response({
26473
+ requestId,
26474
+ status: 0,
26475
+ statusText: 'Network Error',
26476
+ headers: {},
26477
+ body: null,
26478
+ url,
26479
+ method,
26480
+ error: error.message
26481
+ });
26482
+ }
26483
+ });
26484
+ throw error;
26485
+ }
26374
26486
  });
26375
26487
  };
26488
+ }
26489
+ patchXHR() {
26490
+ const networkInterceptor = this;
26491
+ networkInterceptor._originalSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
26376
26492
  networkInterceptor._originalXHROpen = XMLHttpRequest.prototype.open;
26377
26493
  networkInterceptor._originalXHRSend = XMLHttpRequest.prototype.send;
26494
+ XMLHttpRequest.prototype.setRequestHeader = function (header, value) {
26495
+ this[PENDO_HEADERS_KEY] = this[PENDO_HEADERS_KEY] || {};
26496
+ this[PENDO_HEADERS_KEY][header] = value;
26497
+ return networkInterceptor._originalSetRequestHeader.apply(this, arguments);
26498
+ };
26378
26499
  XMLHttpRequest.prototype.open = function (method, url) {
26379
- this._method = method; // Store data per XHR instance
26500
+ this._method = method;
26380
26501
  this._url = url;
26502
+ this._requestId = networkInterceptor.generateRequestId();
26381
26503
  return networkInterceptor._originalXHROpen.apply(this, arguments);
26382
26504
  };
26383
26505
  XMLHttpRequest.prototype.send = function (body) {
26506
+ const xhr = this;
26507
+ const headers = networkInterceptor.extractXHRHeaders(this);
26508
+ const safeBody = networkInterceptor.safelySerializeBody(body);
26384
26509
  try {
26385
- const headers = networkInterceptor.extractXHRHeaders(this);
26386
- const safeBody = networkInterceptor.safelySerializeBody(body);
26387
26510
  _.each(networkInterceptor.callbackFns, ({ request }) => {
26388
26511
  if (_.isFunction(request)) {
26389
26512
  request({
26513
+ requestId: this._requestId,
26390
26514
  method: this._method || 'GET',
26391
26515
  url: this._url,
26392
26516
  body: safeBody,
@@ -26398,7 +26522,7 @@ class NetworkRequestIntercept {
26398
26522
  catch (e) {
26399
26523
  _.each(networkInterceptor.callbackFns, ({ error }) => {
26400
26524
  if (_.isFunction(error))
26401
- error({ context: 'xhr', error: e });
26525
+ error({ context: 'xhrRequest', error: e });
26402
26526
  });
26403
26527
  }
26404
26528
  // Clean up custom headers after the request completes
@@ -26407,9 +26531,49 @@ class NetworkRequestIntercept {
26407
26531
  delete this[PENDO_HEADERS_KEY];
26408
26532
  }, { once: true });
26409
26533
  }
26534
+ // Set up response tracking
26535
+ const originalOnReadyStateChange = xhr.onreadystatechange;
26536
+ xhr.onreadystatechange = function () {
26537
+ if (xhr.readyState === 4) { // Request completed
26538
+ try {
26539
+ const headers = networkInterceptor.parseXHRResponseHeaders(xhr.getAllResponseHeaders());
26540
+ const { status, statusText, responseText: body, _url } = xhr;
26541
+ _.each(networkInterceptor.callbackFns, ({ response }) => {
26542
+ if (_.isFunction(response)) {
26543
+ response({
26544
+ requestId: xhr._requestId,
26545
+ status,
26546
+ statusText,
26547
+ headers,
26548
+ body,
26549
+ url: _url,
26550
+ method: xhr._method || 'GET'
26551
+ });
26552
+ }
26553
+ });
26554
+ }
26555
+ catch (e) {
26556
+ _.each(networkInterceptor.callbackFns, ({ error }) => {
26557
+ if (_.isFunction(error))
26558
+ error({ context: 'xhrResponse', error: e });
26559
+ });
26560
+ }
26561
+ }
26562
+ if (originalOnReadyStateChange) {
26563
+ originalOnReadyStateChange.apply(this, arguments);
26564
+ }
26565
+ };
26410
26566
  return networkInterceptor._originalXHRSend.apply(this, arguments);
26411
26567
  };
26412
26568
  }
26569
+ patchNetwork() {
26570
+ const networkInterceptor = this;
26571
+ if (networkInterceptor._networkPatched)
26572
+ return;
26573
+ networkInterceptor._networkPatched = true;
26574
+ networkInterceptor.patchFetch();
26575
+ networkInterceptor.patchXHR();
26576
+ }
26413
26577
  on({ request, response, error } = {}) {
26414
26578
  if (!this.callbackFns) {
26415
26579
  this.callbackFns = [];
@@ -26417,7 +26581,7 @@ class NetworkRequestIntercept {
26417
26581
  if (_.isFunction(request) || _.isFunction(response) || _.isFunction(error)) {
26418
26582
  this.callbackFns.push({ request, response, error });
26419
26583
  }
26420
- if (!this._networkPatched && !this._patching) {
26584
+ if (!this._networkPatched) {
26421
26585
  this.patchNetwork();
26422
26586
  }
26423
26587
  return this;
@@ -27023,6 +27187,14 @@ var GuideActivity = (function () {
27023
27187
  function isEventOnTextLink(appData) {
27024
27188
  return !!getTextLink(appData);
27025
27189
  }
27190
+ function getStepIdFromAppData(appData) {
27191
+ if (!appData)
27192
+ return;
27193
+ if (/^pendo-g-/.test(appData.id)) {
27194
+ return appData.id.replace(/^pendo-g-/, '');
27195
+ }
27196
+ return getStepIdFromAppData(appData.parentElem);
27197
+ }
27026
27198
  function getTextLink(appData) {
27027
27199
  if (appData.tag === 'A') {
27028
27200
  return { type: appData.tag, id: appData.id };
@@ -27220,7 +27392,19 @@ var GuideActivity = (function () {
27220
27392
  if (guide)
27221
27393
  return guide;
27222
27394
  }
27223
- return getActiveGuide();
27395
+ const stepId = getStepIdFromAppData(appData);
27396
+ const activeGuide = getActiveGuide();
27397
+ if (stepId && activeGuide && activeGuide.step && stepId != activeGuide.step.id) {
27398
+ let step;
27399
+ const guide = _.find(getLocalActiveGuides(), function (guide) {
27400
+ step = _.find(guide.steps, (step) => step.id === stepId);
27401
+ return !!step;
27402
+ });
27403
+ if (guide && step) {
27404
+ return { guide, step };
27405
+ }
27406
+ }
27407
+ return activeGuide;
27224
27408
  }
27225
27409
  })();
27226
27410
 
@@ -27237,6 +27421,8 @@ const PluginAPI = {
27237
27421
  collectEvent
27238
27422
  },
27239
27423
  attachEvent,
27424
+ attachEventInternal,
27425
+ detachEventInternal,
27240
27426
  ConfigReader,
27241
27427
  Events,
27242
27428
  EventTracer,
@@ -28201,6 +28387,9 @@ var BuildingBlockGuides = (function () {
28201
28387
  function renderGuideFromJSON(json, step, guides, options) {
28202
28388
  options = options || {};
28203
28389
  var guide = step.getGuide();
28390
+ if (step.isDarkMode === undefined) {
28391
+ syncColorMode(step);
28392
+ }
28204
28393
  var containerJSON = findGuideContainerJSON(json);
28205
28394
  var isResourceCenter = _.get(guide, 'attributes.resourceCenter');
28206
28395
  if (isResourceCenter) {
@@ -28733,13 +28922,13 @@ var logHistory = [];
28733
28922
  var errorHistory = [];
28734
28923
  const LOG_ENABLED = 'log-enabled';
28735
28924
  const ACTIVE_CONTEXTS = 'active-contexts';
28736
- var getDefaultLogOverride = function (env) {
28925
+ var getDefaultLogOverride = function () {
28737
28926
  var isEnabledCookie = agentStorage.read(LOG_ENABLED, true);
28738
28927
  if (isEnabledCookie !== null) {
28739
28928
  return isEnabledCookie == 'true';
28740
28929
  }
28741
28930
  // add welcome message and list logging status + contexts
28742
- return !_.contains(['prod', 'prod-eu', 'prod-us1', 'prod-jp', 'prod-hsbc', 'prod-au', 'prod-gov', 'rc'], env);
28931
+ return !isProdAgent() && !isRCAgent();
28743
28932
  };
28744
28933
  var getDefaultActiveContexts = function () {
28745
28934
  var ac = agentStorage.read(ACTIVE_CONTEXTS, true);
@@ -28784,7 +28973,7 @@ var disableLogging = function () {
28784
28973
  };
28785
28974
  function initLogging() {
28786
28975
  activeContexts = getDefaultActiveContexts();
28787
- logOverride = getDefaultLogOverride(ENV);
28976
+ logOverride = getDefaultLogOverride();
28788
28977
  log.addEventListener('log', writeLog);
28789
28978
  /**
28790
28979
  * Stores console logging status, set with `pendo.enableLogging()` or `pendo.disableLogging()`.
@@ -29777,7 +29966,7 @@ function LocationPublic(cmd) {
29777
29966
  }
29778
29967
  _.extend(LocationPublic, {
29779
29968
  getHref() {
29780
- return store.getters['location/href']();
29969
+ return pendoDotUrl.get();
29781
29970
  },
29782
29971
  clearTransforms() {
29783
29972
  store.commit('location/clearTransforms');
@@ -31068,6 +31257,10 @@ var GuideDisplay = (function () {
31068
31257
  if (step.type !== 'whatsnew' && !step.isShown()) {
31069
31258
  return false;
31070
31259
  }
31260
+ if (isGuideRequestPending()) {
31261
+ log.info('guides are loading.', { contexts: ['guides', 'loading'] });
31262
+ return false;
31263
+ }
31071
31264
  step.unlock();
31072
31265
  step._show(reason);
31073
31266
  return step.isShown();
@@ -31552,6 +31745,7 @@ function GuideStep(guide) {
31552
31745
  step.seenState = 'active';
31553
31746
  setSeenTime(getNow());
31554
31747
  var seenProps = {
31748
+ color_mode: step.isDarkMode ? 'dark' : 'default',
31555
31749
  last_updated_at: step.lastUpdatedAt
31556
31750
  };
31557
31751
  var pollTypes = this.getStepPollTypes(guide, step);
@@ -33243,7 +33437,7 @@ const DebuggerModule = (() => {
33243
33437
  location: { type: SYNC_TYPES.BOTTOM_UP, defaultValue: {} }
33244
33438
  };
33245
33439
  const actions = {
33246
- init: (context) => {
33440
+ init: (context, buffer) => {
33247
33441
  context.commit('setFrameId', EventTracer.getFrameId());
33248
33442
  context.commit('setTabId', EventTracer.getTabId());
33249
33443
  context.commit('setInstallType', getInstallType());
@@ -33263,6 +33457,7 @@ const DebuggerModule = (() => {
33263
33457
  frameId: context.state.frameId
33264
33458
  });
33265
33459
  }
33460
+ _.each(buffer.events, (event) => eventCapturedFn(event));
33266
33461
  },
33267
33462
  join: (context, data) => {
33268
33463
  if (context.state.frameId === data.frameId)
@@ -33418,6 +33613,9 @@ const DebuggerModule = (() => {
33418
33613
  // Unlike eligibility, here we're collecting all events into a singular list
33419
33614
  // this list is reverse chronological order
33420
33615
  state.eventsCaptured.unshift(event);
33616
+ if (state.eventsCaptured.length > 100) {
33617
+ state.eventsCaptured.pop();
33618
+ }
33421
33619
  },
33422
33620
  enableEventLogging(state, enabled) {
33423
33621
  state.enableEventLogging = enabled;
@@ -36956,12 +37154,18 @@ function disableDebugging() {
36956
37154
  _.extend(debug, debugging);
36957
37155
 
36958
37156
  const waitForLeader = (Events, store, q) => {
37157
+ const buffer = {
37158
+ events: []
37159
+ };
37160
+ Events.on('eventCaptured', (evt) => {
37161
+ buffer.events.push(evt);
37162
+ });
36959
37163
  if (store.getters['frames/leaderExists']()) {
36960
- return q.resolve();
37164
+ return q.resolve(buffer);
36961
37165
  }
36962
37166
  const deferred = q.defer();
36963
37167
  Events.on('leaderChanged', () => {
36964
- deferred.resolve();
37168
+ deferred.resolve(buffer);
36965
37169
  });
36966
37170
  return deferred.promise;
36967
37171
  };
@@ -36976,8 +37180,8 @@ const DebuggerLauncher = (function () {
36976
37180
  function initialize(pendo, PluginAPI) {
36977
37181
  const { Events, q } = PluginAPI;
36978
37182
  store = PluginAPI.store;
36979
- waitForLeader(Events, store, q).then(() => {
36980
- store.dispatch('debugger/init');
37183
+ waitForLeader(Events, store, q).then((buffer) => {
37184
+ store.dispatch('debugger/init', buffer);
36981
37185
  if (store.getters['frames/isLeader']()) {
36982
37186
  startDebuggingModuleIfEnabled();
36983
37187
  }
@@ -44343,13 +44547,15 @@ class MutationBuffer {
44343
44547
  index.childNodes(textarea),
44344
44548
  (cn) => index.textContent(cn) || ""
44345
44549
  ).join("");
44550
+ const needsMask = needMaskingText(textarea, this.maskTextClass, this.maskTextSelector, true);
44346
44551
  item.attributes.value = maskInputValue({
44347
44552
  element: textarea,
44348
44553
  maskInputOptions: this.maskInputOptions,
44349
44554
  tagName: textarea.tagName,
44350
44555
  type: getInputType(textarea),
44351
44556
  value,
44352
- maskInputFn: this.maskInputFn
44557
+ maskInputFn: this.maskInputFn,
44558
+ needsMask
44353
44559
  });
44354
44560
  });
44355
44561
  __publicField(this, "processMutation", (m) => {
@@ -52555,6 +52761,12 @@ class SessionRecorder {
52555
52761
  return;
52556
52762
  clearTimeout(this._changeIdentityTimer);
52557
52763
  if (this.isRecording()) {
52764
+ this.logStopReason('IDENTITY_CHANGED', {
52765
+ previousVisitorId: this.visitorId,
52766
+ newVisitorId: identifyEvent.data[0].visitor_id,
52767
+ previousAccountId: this.accountId,
52768
+ newAccountId: identifyEvent.data[0].account_id
52769
+ });
52558
52770
  this.stop();
52559
52771
  }
52560
52772
  this.visitorId = identifyEvent.data[0].visitor_id;
@@ -52650,7 +52862,7 @@ class SessionRecorder {
52650
52862
  this._start();
52651
52863
  }
52652
52864
  _markEvents(events) {
52653
- if (!this.recordingId)
52865
+ if (!this.recordingId || !this.isRecording())
52654
52866
  return;
52655
52867
  for (var e of events) {
52656
52868
  if ((e.visitor_id === this.visitorId || e.visitorId === this.visitorId) && e.type !== 'identify') {
@@ -52968,6 +53180,8 @@ class SessionRecorder {
52968
53180
  }
52969
53181
  }
52970
53182
  addRecordingId(event) {
53183
+ if (!this.isRecording())
53184
+ return;
52971
53185
  if (!this.recordingId || !event || !event.data || !event.data.length)
52972
53186
  return;
52973
53187
  var capturedEvent = event.data[0];
@@ -52976,6 +53190,10 @@ class SessionRecorder {
52976
53190
  if (capturedEvent.visitor_id !== this.visitorId && capturedEvent.visitorId !== this.visitorId) {
52977
53191
  // visitor id has already diverged from the agent, we'll stop sending events and just return here
52978
53192
  this.api.log.warn('Visitor id has diverged from agent');
53193
+ this.logStopReason('VISITOR_ID_DIVERGED', {
53194
+ agentVisitorId: capturedEvent.visitor_id || capturedEvent.visitorId,
53195
+ recordingVisitorId: this.visitorId
53196
+ });
52979
53197
  this.stop();
52980
53198
  return;
52981
53199
  }
@@ -54155,6 +54373,168 @@ function scrubPII(string) {
54155
54373
  return Object.values(PII_PATTERN).reduce((str, pattern) => str.replace(pattern, PII_REPLACEMENT), string);
54156
54374
  }
54157
54375
 
54376
+ /**
54377
+ * Determines the type of resource that was blocked based on the blocked URI and CSP directive.
54378
+ *
54379
+ * @param {string} blockedURI - The URI that was blocked by the CSP policy (can be 'inline', 'eval', or a URL)
54380
+ * @param {string} directive - The CSP directive that caused the violation (e.g., 'script-src', 'style-src')
54381
+ * @returns {string} A human-readable description of the resource type
54382
+ *
54383
+ * @example
54384
+ * getResourceType('inline', 'script-src') // returns 'inline script'
54385
+ * getResourceType('https://example.com/script.js', 'script-src') // returns 'script'
54386
+ * getResourceType('https://example.com/image.jpg', 'img-src') // returns 'image'
54387
+ * getResourceType('https://example.com/worker.js', 'worker-src') // returns 'worker'
54388
+ */
54389
+ function getResourceType(blockedURI, directive) {
54390
+ if (!directive || typeof directive !== 'string')
54391
+ return 'resource';
54392
+ const d = directive.toLowerCase();
54393
+ if (blockedURI === 'inline') {
54394
+ if (d.includes('script-src-attr')) {
54395
+ return 'inline event handler';
54396
+ }
54397
+ if (d.includes('style-src-attr')) {
54398
+ return 'inline style attribute';
54399
+ }
54400
+ if (d.includes('script')) {
54401
+ return 'inline script';
54402
+ }
54403
+ if (d.includes('style')) {
54404
+ return 'inline style';
54405
+ }
54406
+ }
54407
+ if (blockedURI === 'eval') {
54408
+ return 'eval script execution';
54409
+ }
54410
+ if (d.includes('worker'))
54411
+ return 'worker';
54412
+ if (d.includes('script')) {
54413
+ return d.includes('elem') ? 'script element' : 'script';
54414
+ }
54415
+ if (d.includes('style')) {
54416
+ return d.includes('elem') ? 'style element' : 'stylesheet';
54417
+ }
54418
+ if (d.includes('img'))
54419
+ return 'image';
54420
+ if (d.includes('font'))
54421
+ return 'font';
54422
+ if (d.includes('connect'))
54423
+ return 'network request';
54424
+ if (d.includes('media'))
54425
+ return 'media resource';
54426
+ if (d.includes('frame-ancestors'))
54427
+ return 'display of your page in a frame';
54428
+ if (d.includes('frame'))
54429
+ return 'frame';
54430
+ if (d.includes('manifest'))
54431
+ return 'manifest';
54432
+ if (d.includes('base-uri'))
54433
+ return 'base URI';
54434
+ if (d.includes('form-action'))
54435
+ return 'form submission';
54436
+ return 'resource';
54437
+ }
54438
+ /**
54439
+ * Finds a specific directive in a CSP policy string and returns it with its value.
54440
+ *
54441
+ * @param {string} policy - The complete original CSP policy string (semicolon-separated directives)
54442
+ * @param {string} directiveName - The name of the directive to find (e.g., 'script-src', 'style-src')
54443
+ * @returns {string} The complete directive with its value if found, empty string otherwise
54444
+ *
54445
+ * @example
54446
+ * getDirective('script-src \'self\'; style-src \'self\'', 'script-src') // returns 'script-src \'self\''
54447
+ */
54448
+ function getDirective(policy, directiveName) {
54449
+ if (!policy || !directiveName)
54450
+ return '';
54451
+ const needle = directiveName.toLowerCase();
54452
+ for (const directive of policy.split(';')) {
54453
+ const trimmed = directive.trim();
54454
+ const lower = trimmed.toLowerCase();
54455
+ if (lower === needle || lower.startsWith(`${needle} `)) {
54456
+ return trimmed;
54457
+ }
54458
+ }
54459
+ return '';
54460
+ }
54461
+ /**
54462
+ * Determines the appropriate article (A/An) for a given phrase based on its first letter.
54463
+ *
54464
+ * @param {string} phrase - The phrase to determine the article for
54465
+ * @returns {string} Either 'A' or 'An' based on whether the phrase starts with a vowel sound
54466
+ *
54467
+ * @example
54468
+ * getArticle('script') // returns 'A'
54469
+ * getArticle('image') // returns 'An'
54470
+ * getArticle('stylesheet') // returns 'A'
54471
+ * getArticle('') // returns 'A' (fallback for empty string)
54472
+ * getArticle(undefined) // returns 'A' (fallback for undefined)
54473
+ */
54474
+ function getArticle(phrase) {
54475
+ if (!phrase || typeof phrase !== 'string')
54476
+ return 'A';
54477
+ const c = phrase.trim().charAt(0).toLowerCase();
54478
+ return 'aeiou'.includes(c) ? 'An' : 'A';
54479
+ }
54480
+ /**
54481
+ * Returns the original blocked URI when it looks like a URL or scheme,
54482
+ * otherwise returns an empty string for special cases.
54483
+ *
54484
+ * @param {string} blockedURI - The URI that was blocked (can be 'inline', 'eval', or a URL/scheme)
54485
+ * @returns {string} The blocked URI or an empty string for special cases like 'inline' or 'eval'
54486
+ *
54487
+ * @example
54488
+ * formatBlockedUri('https://example.com/script.js') // returns 'https://example.com/script.js'
54489
+ * formatBlockedUri('inline') // returns ''
54490
+ * formatBlockedUri('blob') // returns 'blob'
54491
+ */
54492
+ function formatBlockedUri(blockedURI) {
54493
+ if (!blockedURI || blockedURI === 'inline' || blockedURI === 'eval')
54494
+ return '';
54495
+ return blockedURI;
54496
+ }
54497
+ /**
54498
+ * Formats a CSP violation message for console output.
54499
+ *
54500
+ * @param {string} blockedURI - The URI that was blocked by the CSP policy (can be 'inline', 'eval', or a URL)
54501
+ * @param {string} directive - The CSP directive that caused the violation (e.g., 'script-src', 'frame-ancestors')
54502
+ * @param {boolean} isReportOnly - Whether this is a report-only policy violation (adds " (Report-Only)" to message)
54503
+ * @param {string} originalPolicy - The complete CSP policy that was violated
54504
+ * @returns {string} A formatted readable CSP violation message
54505
+ *
54506
+ * @example
54507
+ * createCspViolationMessage(
54508
+ * 'https://example.com/script.js',
54509
+ * 'script-src',
54510
+ * false,
54511
+ * 'script-src \'self\'; style-src \'self\''
54512
+ * )
54513
+ * // returns: "Content Security Policy: A script from https://example.com/script.js was blocked by your site's `script-src 'self'` policy.\nCurrent CSP: \"script-src 'self'; style-src 'self'\"."
54514
+ */
54515
+ function createCspViolationMessage(blockedURI, directive, isReportOnly, originalPolicy) {
54516
+ if (!directive || typeof directive !== 'string') {
54517
+ return 'Content Security Policy: Unknown violation occurred.';
54518
+ }
54519
+ try {
54520
+ const reportOnlyText = isReportOnly ? ' (Report-Only)' : '';
54521
+ // special case for frame-ancestors since it doesn't fit our template at all
54522
+ if ((directive === null || directive === void 0 ? void 0 : directive.toLowerCase()) === 'frame-ancestors') {
54523
+ return `Content Security Policy${reportOnlyText}: The display of ${blockedURI} in a frame was blocked because an ancestor violates your site's frame-ancestors policy.\nCurrent CSP: "${originalPolicy}".`;
54524
+ }
54525
+ const resourceType = getResourceType(blockedURI, directive);
54526
+ const article = getArticle(resourceType);
54527
+ const source = formatBlockedUri(blockedURI);
54528
+ const directiveValue = getDirective(originalPolicy, directive);
54529
+ const policyDisplay = directiveValue || directive;
54530
+ const resourceDescription = `${article} ${resourceType}${source ? ` from ${source}` : ''}`;
54531
+ return `Content Security Policy${reportOnlyText}: ${resourceDescription} was blocked by your site's \`${policyDisplay}\` policy.\nCurrent CSP: "${originalPolicy}".`;
54532
+ }
54533
+ catch (error) {
54534
+ return `Content Security Policy: Error formatting violation message: ${error.message}`;
54535
+ }
54536
+ }
54537
+
54158
54538
  const DEV_LOG_LEVELS = ['info', 'warn', 'error'];
54159
54539
  const TOKEN_MAX_SIZE = 100;
54160
54540
  const TOKEN_REFILL_RATE = 10;
@@ -55699,11 +56079,12 @@ function isNativeCode(fn) { return /native/.test(fn); }
55699
56079
  // we've added trident to track this actual version of IE so we can
55700
56080
  // attempt to handle these cases too.
55701
56081
  // holds major version number for IE or NaN for real browsers
55702
- var msie;
56082
+ const msie = determineMSIE(getUA());
56083
+ determineTrident(getUA(), msie);
55703
56084
  function pint(str) { return parseInt(str, 10); }
55704
- var lowercase = function (string) { return _.isString(string) ? string.toLowerCase() : string; };
56085
+ function lowercase(string) { return _.isString(string) ? string.toLowerCase() : string; }
55705
56086
  function isMinimumIEVersion(minVersion) {
55706
- var sniffer = this;
56087
+ const sniffer = this;
55707
56088
  if (isNaN(sniffer.msie) || sniffer.msie == null)
55708
56089
  return true;
55709
56090
  return sniffer.msie >= minVersion;
@@ -55713,36 +56094,34 @@ function getUA() {
55713
56094
  }
55714
56095
  // IE 11 changed the format of the UserAgent string.
55715
56096
  // See http://msdn.microsoft.com/en-us/library/ms537503.aspx
55716
- var determineMSIE = function (ua) {
55717
- var v = pint((/msie (\d+)/.exec(lowercase(ua)) || [])[1]);
55718
- if (isNaN(v)) {
55719
- v = pint((/trident\/.*; rv:(\d+)/.exec(lowercase(ua)) || [])[1]);
55720
- }
55721
- return v;
55722
- };
55723
- msie = determineMSIE(getUA());
55724
- var determineTrident = function (ua, ieV) {
55725
- var v = pint((/trident\/(\d+)/.exec(lowercase(ua)) || [])[1]);
55726
- if (isNaN(v) && ieV == 7) {
55727
- v = 3;
55728
- }
55729
- return v;
55730
- };
55731
- determineTrident(getUA(), msie);
55732
- var eventSupport = {};
55733
- var android = pint((/android (\d+)/.exec(lowercase(getUA())) || [])[1]);
55734
- var boxee = /Boxee/i.test(getUA());
55735
- var pdocument = window.document || {};
55736
- var documentMode = pdocument.documentMode;
55737
- var vendorPrefix;
55738
- var vendorRegex = /^(Moz|webkit|O|ms)(?=[A-Z])/;
55739
- var bodyStyle = pdocument.body && pdocument.body.style;
55740
- var transitions = false;
55741
- var animations = false;
55742
- var match;
56097
+ function determineMSIE(userAgent) {
56098
+ let version = pint((/msie (\d+)/.exec(lowercase(userAgent)) || [])[1]);
56099
+ if (isNaN(version)) {
56100
+ version = pint((/trident\/.*; rv:(\d+)/.exec(lowercase(userAgent)) || [])[1]);
56101
+ }
56102
+ return version;
56103
+ }
56104
+ function determineTrident(userAgent, ieVersion) {
56105
+ let version = pint((/trident\/(\d+)/.exec(lowercase(userAgent)) || [])[1]);
56106
+ if (isNaN(version) && ieVersion == 7) {
56107
+ version = 3;
56108
+ }
56109
+ return version;
56110
+ }
56111
+ const eventSupport = {};
56112
+ const android = pint((/android (\d+)/.exec(lowercase(getUA())) || [])[1]);
56113
+ const boxee = /Boxee/i.test(getUA());
56114
+ const pdocument = window.document || {};
56115
+ const documentMode = pdocument.documentMode;
56116
+ let vendorPrefix;
56117
+ const vendorRegex = /^(Moz|webkit|O|ms)(?=[A-Z])/;
56118
+ const bodyStyle = pdocument.body && pdocument.body.style;
56119
+ let transitions = false;
56120
+ let animations = false;
56121
+ let match;
55743
56122
  if (bodyStyle) {
55744
56123
  // eslint-disable-next-line guard-for-in
55745
- for (var prop in bodyStyle) {
56124
+ for (const prop in bodyStyle) {
55746
56125
  match = vendorRegex.exec(prop);
55747
56126
  if (match) {
55748
56127
  vendorPrefix = match[0];
@@ -55760,7 +56139,7 @@ if (bodyStyle) {
55760
56139
  animations = _.isString(pdocument.body.style.webkitAnimation);
55761
56140
  }
55762
56141
  }
55763
- var isChromeExtension = window.chrome && window.chrome.runtime && window.chrome.runtime.id;
56142
+ const isChromeExtension = window.chrome && window.chrome.runtime && window.chrome.runtime.id;
55764
56143
  // Android has history.pushState, but it does not update location correctly
55765
56144
  // so let's not use the history API at all.
55766
56145
  // http://code.google.com/p/android/issues/detail?id=17471
@@ -55768,28 +56147,28 @@ var isChromeExtension = window.chrome && window.chrome.runtime && window.chrome.
55768
56147
  // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
55769
56148
  // so let's not use the history API also
55770
56149
  // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
55771
- var shouldWrapNativeHistory = _.memoize(function shouldWrapNativeHistory() {
55772
- var hasNativeHistory = window.history && window.history.pushState && window.history.replaceState;
55773
- var isHistoryFrozen = window.history && _.isFunction(Object.isFrozen) && Object.isFrozen(window.history);
55774
- var historyDescriptors = window.history && _.isFunction(Object.getOwnPropertyDescriptors) && Object.getOwnPropertyDescriptors(window.history);
55775
- var isHistoryWriteable = historyDescriptors && _.get(historyDescriptors, 'pushState.writable', true) && _.get(historyDescriptors, 'replaceState.writable', true);
55776
- var hasAndroidSupport = !(android < 4);
56150
+ const shouldWrapNativeHistory = _.memoize(function shouldWrapNativeHistory() {
56151
+ const hasNativeHistory = window.history && window.history.pushState && window.history.replaceState;
56152
+ const isHistoryFrozen = window.history && _.isFunction(Object.isFrozen) && Object.isFrozen(window.history);
56153
+ const historyDescriptors = window.history && _.isFunction(Object.getOwnPropertyDescriptors) && Object.getOwnPropertyDescriptors(window.history);
56154
+ const isHistoryWriteable = historyDescriptors && _.get(historyDescriptors, 'pushState.writable', true) && _.get(historyDescriptors, 'replaceState.writable', true);
56155
+ const hasAndroidSupport = !(android < 4);
55777
56156
  return !!(hasNativeHistory && !isHistoryFrozen && isHistoryWriteable && hasAndroidSupport && !boxee && !isExtensionAgent());
55778
56157
  });
55779
56158
  function shouldWrapHashChange() {
55780
- var hasNativeHashChange = 'onhashchange' in window;
56159
+ const hasNativeHashChange = 'onhashchange' in window;
55781
56160
  // IE8 compatible mode lies
55782
- var hasSupportedDocMode = (!documentMode || documentMode > 7);
56161
+ const hasSupportedDocMode = (!documentMode || documentMode > 7);
55783
56162
  return !!(hasNativeHashChange && hasSupportedDocMode && !isExtensionAgent());
55784
56163
  }
55785
56164
  function isMobileUserAgent() {
55786
56165
  return (/Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i).test(getUA());
55787
56166
  }
55788
- // exports
55789
- var sniffer = {
55790
- // jshint -W018
56167
+ function isSafari(userAgent = getUA()) {
56168
+ return userAgent.indexOf('Safari') >= 0 && !userAgent.indexOf('Chrome') >= 0;
56169
+ }
56170
+ const sniffer = {
55791
56171
  supportsHistoryApi: shouldWrapNativeHistory,
55792
- // jshint +W018
55793
56172
  supportsHashChange: shouldWrapHashChange,
55794
56173
  hasEvent(event) {
55795
56174
  // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
@@ -55798,7 +56177,7 @@ var sniffer = {
55798
56177
  if (event == 'input' && msie == 9)
55799
56178
  return false;
55800
56179
  if (_.isUndefined(eventSupport[event])) {
55801
- var divElm = pdocument.createElement('div');
56180
+ const divElm = pdocument.createElement('div');
55802
56181
  eventSupport[event] = 'on' + event in divElm;
55803
56182
  }
55804
56183
  return eventSupport[event];
@@ -55809,7 +56188,7 @@ var sniffer = {
55809
56188
  android,
55810
56189
  msie,
55811
56190
  msieDocumentMode: documentMode,
55812
- safari: /apple/i.test(navigator.vendor),
56191
+ safari: isSafari(),
55813
56192
  sri: ('integrity' in document.createElement('script')),
55814
56193
  addEventListener: _.isFunction(window.addEventListener),
55815
56194
  MutationObserver: isNativeCode(window.MutationObserver),
@@ -56289,6 +56668,7 @@ function ConsoleCapture() {
56289
56668
  onAppUnloaded,
56290
56669
  onPtmPaused,
56291
56670
  onPtmUnpaused,
56671
+ securityPolicyViolationFn,
56292
56672
  get buffer() {
56293
56673
  return buffer;
56294
56674
  },
@@ -56317,7 +56697,7 @@ function ConsoleCapture() {
56317
56697
  }
56318
56698
  }, SEND_INTERVAL);
56319
56699
  sendQueue.start();
56320
- pluginAPI.Events.ready.on(addIntercepts);
56700
+ pluginAPI.Events.ready.on(readyHandler);
56321
56701
  pluginAPI.Events.appUnloaded.on(onAppUnloaded);
56322
56702
  pluginAPI.Events.appHidden.on(onAppHidden);
56323
56703
  pluginAPI.Events['ptm:paused'].on(onPtmPaused);
@@ -56352,6 +56732,10 @@ function ConsoleCapture() {
56352
56732
  url: globalPendo.url.get()
56353
56733
  };
56354
56734
  }
56735
+ function readyHandler() {
56736
+ addIntercepts();
56737
+ pluginAPI.attachEventInternal(window, 'securitypolicyviolation', securityPolicyViolationFn);
56738
+ }
56355
56739
  function addIntercepts() {
56356
56740
  _.each(CONSOLE_METHODS, function (methodName) {
56357
56741
  const originalMethod = console[methodName];
@@ -56368,7 +56752,21 @@ function ConsoleCapture() {
56368
56752
  };
56369
56753
  });
56370
56754
  }
56371
- function createConsoleEvent(args, methodName) {
56755
+ function securityPolicyViolationFn(evt) {
56756
+ if (!evt || typeof evt !== 'object')
56757
+ return;
56758
+ const { blockedURI, violatedDirective, effectiveDirective, disposition, originalPolicy } = evt;
56759
+ const directive = violatedDirective || effectiveDirective;
56760
+ const isReportOnly = disposition === 'report';
56761
+ const message = createCspViolationMessage(blockedURI, directive, isReportOnly, originalPolicy);
56762
+ if (isReportOnly) {
56763
+ createConsoleEvent([message], 'warn', { skipStackTrace: true, skipScrubPII: true });
56764
+ }
56765
+ else {
56766
+ createConsoleEvent([message], 'error', { skipStackTrace: true, skipScrubPII: true });
56767
+ }
56768
+ }
56769
+ function createConsoleEvent(args, methodName, { skipStackTrace = false, skipScrubPII = false } = {}) {
56372
56770
  if (!args || args.length === 0)
56373
56771
  return;
56374
56772
  // stringify args
@@ -56385,8 +56783,11 @@ function ConsoleCapture() {
56385
56783
  if (!message)
56386
56784
  return;
56387
56785
  // capture stack trace
56388
- const maxStackFrames = methodName === 'error' ? 4 : 1;
56389
- let stackTrace = captureStackTrace(maxStackFrames);
56786
+ let stackTrace = '';
56787
+ if (!skipStackTrace) {
56788
+ const maxStackFrames = methodName === 'error' ? 4 : 1;
56789
+ stackTrace = captureStackTrace(maxStackFrames);
56790
+ }
56390
56791
  // truncate message and stack trace
56391
56792
  message = truncate(message);
56392
56793
  stackTrace = truncate(stackTrace);
@@ -56397,7 +56798,7 @@ function ConsoleCapture() {
56397
56798
  return;
56398
56799
  }
56399
56800
  const devLogEnvelope = createDevLogEnvelope();
56400
- const consoleEvent = Object.assign(Object.assign({}, devLogEnvelope), { devLogLevel: methodName === 'log' ? 'info' : methodName, devLogMessage: scrubPII(message), devLogTrace: stackTrace, devLogCount: 1 });
56801
+ const consoleEvent = Object.assign(Object.assign({}, devLogEnvelope), { devLogLevel: methodName === 'log' ? 'info' : methodName, devLogMessage: skipScrubPII ? message : scrubPII(message), devLogTrace: stackTrace, devLogCount: 1 });
56401
56802
  const wasAccepted = buffer.push(consoleEvent);
56402
56803
  if (wasAccepted) {
56403
56804
  if (!isPtmPaused) {
@@ -56459,6 +56860,7 @@ function ConsoleCapture() {
56459
56860
  pluginAPI.Events.appUnloaded.off(onAppUnloaded);
56460
56861
  pluginAPI.Events['ptm:paused'].off(onPtmPaused);
56461
56862
  pluginAPI.Events['ptm:unpaused'].off(onPtmUnpaused);
56863
+ pluginAPI.detachEventInternal(window, 'securitypolicyviolation', securityPolicyViolationFn);
56462
56864
  _.each(CONSOLE_METHODS, function (methodName) {
56463
56865
  if (!console[methodName])
56464
56866
  return _.noop;