@pendo/agent 2.338.0 → 2.339.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.
- package/dist/debugger-plugin.min.js +1 -1
- package/dist/dom.esm.js +12 -21
- package/dist/pendo.debugger.min.js +10 -10
- package/dist/pendo.module.js +1350 -858
- package/dist/pendo.module.min.js +11 -11
- package/dist/replay.worker.min.js +1 -1
- package/dist/servers.json +8 -8
- package/package.json +1 -1
package/dist/pendo.module.js
CHANGED
|
@@ -657,10 +657,13 @@ function getPendoConfigValue(key) {
|
|
|
657
657
|
}
|
|
658
658
|
|
|
659
659
|
// Resolve the global object without relying on a bare `window` identifier.
|
|
660
|
-
// `window` is only a bound global in a normal browser realm;
|
|
661
|
-
//
|
|
662
|
-
//
|
|
663
|
-
//
|
|
660
|
+
// `window` is only a bound global in a normal browser realm; the compat builds
|
|
661
|
+
// bind it via their `(function(window, document){...})` wrapper, but the npm
|
|
662
|
+
// module build has no wrapper, so its module scope can be evaluated in realms
|
|
663
|
+
// where `window` is undeclared (a Web Worker, or a customer bundler re-chunking
|
|
664
|
+
// the module into worker-reachable code) and a bare reference throws a
|
|
665
|
+
// ReferenceError at module-load. `globalThis` resolves in every realm; the
|
|
666
|
+
// `self`/`window` fallbacks cover engines that predate it. APP-160669, APP-165809.
|
|
664
667
|
function getGlobalScope() {
|
|
665
668
|
if (typeof globalThis !== 'undefined') {
|
|
666
669
|
return globalThis;
|
|
@@ -4043,8 +4046,8 @@ let SERVER = '';
|
|
|
4043
4046
|
let ASSET_HOST = '';
|
|
4044
4047
|
let ASSET_PATH = '';
|
|
4045
4048
|
let DESIGNER_SERVER = '';
|
|
4046
|
-
let VERSION = '2.
|
|
4047
|
-
let PACKAGE_VERSION = '2.
|
|
4049
|
+
let VERSION = '2.339.0_';
|
|
4050
|
+
let PACKAGE_VERSION = '2.339.0';
|
|
4048
4051
|
let LOADER = 'xhr';
|
|
4049
4052
|
/* eslint-enable web-sdk-eslint-rules/no-gulp-env-references */
|
|
4050
4053
|
/**
|
|
@@ -4349,9 +4352,9 @@ function unwrapFirefoxObject(obj) {
|
|
|
4349
4352
|
return obj;
|
|
4350
4353
|
}
|
|
4351
4354
|
|
|
4352
|
-
var decodeURIComponent = _.isFunction(
|
|
4355
|
+
var decodeURIComponent = _.isFunction(globalScope.decodeURIComponent) ? globalScope.decodeURIComponent : _.identity;
|
|
4353
4356
|
|
|
4354
|
-
var encodeURIComponent = _.isFunction(
|
|
4357
|
+
var encodeURIComponent = _.isFunction(globalScope.encodeURIComponent) ? globalScope.encodeURIComponent : _.identity;
|
|
4355
4358
|
|
|
4356
4359
|
class EventTarget {
|
|
4357
4360
|
constructor() {
|
|
@@ -4525,7 +4528,7 @@ function createStorageShim(storageAccessor) {
|
|
|
4525
4528
|
return fake;
|
|
4526
4529
|
}
|
|
4527
4530
|
}
|
|
4528
|
-
var pendoLocalStorage$
|
|
4531
|
+
var pendoLocalStorage$3 = createStorageShim(function () { return window.localStorage; });
|
|
4529
4532
|
var pendoSessionStorage$2 = createStorageShim(function () { return window.sessionStorage; });
|
|
4530
4533
|
|
|
4531
4534
|
/**
|
|
@@ -4913,7 +4916,7 @@ var agentStorage = (function () {
|
|
|
4913
4916
|
}
|
|
4914
4917
|
const wrappedPendoSessionStorage = _.extend({}, pendoSessionStorage$2);
|
|
4915
4918
|
wrappedPendoSessionStorage.setItem = wrapStorageWriteMethod(wrappedPendoSessionStorage.setItem, 'hasSession');
|
|
4916
|
-
const wrappedPendoLocalStorage = _.extend({}, pendoLocalStorage$
|
|
4919
|
+
const wrappedPendoLocalStorage = _.extend({}, pendoLocalStorage$3);
|
|
4917
4920
|
wrappedPendoLocalStorage.setItem = wrapStorageWriteMethod(wrappedPendoLocalStorage.setItem, 'hasLocal');
|
|
4918
4921
|
return {
|
|
4919
4922
|
/* API */
|
|
@@ -4927,18 +4930,6 @@ var agentStorage = (function () {
|
|
|
4927
4930
|
};
|
|
4928
4931
|
})();
|
|
4929
4932
|
|
|
4930
|
-
/*
|
|
4931
|
-
* @license MIT - https://github.com/taylorhakes/promise-polyfill
|
|
4932
|
-
*/
|
|
4933
|
-
// Modified to not affect global environment: the `var` keeps the binding module-local
|
|
4934
|
-
// so the polyfill never assigns to the real global `Promise`.
|
|
4935
|
-
//
|
|
4936
|
-
// Main-thread only. Reading `window` here is safe because rollup's `inject` is skipped
|
|
4937
|
-
// for the nested worker build (see `skipPlugins` on webWorkerLoader in
|
|
4938
|
-
// rollup/web-sdk.config.mjs): a worker realm has a native Promise and no `window`, so
|
|
4939
|
-
// injecting this polyfill there was both unreachable and fatal — it threw
|
|
4940
|
-
// `ReferenceError: window is not defined` at worker startup and stopped session replay
|
|
4941
|
-
// with a WORKER_ERROR. APP-163474.
|
|
4942
4933
|
var Promise$1;
|
|
4943
4934
|
|
|
4944
4935
|
Promise$1 = (function (global, factory) {
|
|
@@ -4948,7 +4939,7 @@ Promise$1 = (function (global, factory) {
|
|
|
4948
4939
|
}
|
|
4949
4940
|
|
|
4950
4941
|
return nativePromise;
|
|
4951
|
-
}(
|
|
4942
|
+
}(globalScope, (function () {
|
|
4952
4943
|
/**
|
|
4953
4944
|
* @this {Promise}
|
|
4954
4945
|
*/
|
|
@@ -6277,6 +6268,7 @@ function wasSessionCleared(evt) {
|
|
|
6277
6268
|
//
|
|
6278
6269
|
// NOTE: This, for now, at least is identified as an instanceof HTMLAnchorElement
|
|
6279
6270
|
|
|
6271
|
+
|
|
6280
6272
|
function URLPolyfillFactory(doc) {
|
|
6281
6273
|
return function URL(str) {
|
|
6282
6274
|
var a = doc.createElement('a');
|
|
@@ -6308,11 +6300,11 @@ function URLPolyfillFactory(doc) {
|
|
|
6308
6300
|
var URL = (function(global, factory) {
|
|
6309
6301
|
var nativeURL = global.URL;
|
|
6310
6302
|
if (typeof nativeURL !== 'function' || !/native/.test(nativeURL)) {
|
|
6311
|
-
return factory(document);
|
|
6303
|
+
return factory(global.document);
|
|
6312
6304
|
}
|
|
6313
6305
|
|
|
6314
6306
|
return nativeURL;
|
|
6315
|
-
})(
|
|
6307
|
+
})(globalScope, URLPolyfillFactory);
|
|
6316
6308
|
|
|
6317
6309
|
function hasComposedPath(evt) {
|
|
6318
6310
|
return evt && typeof evt.composedPath === 'function';
|
|
@@ -11658,7 +11650,7 @@ var WeakRef = (function(global, factory) {
|
|
|
11658
11650
|
}
|
|
11659
11651
|
|
|
11660
11652
|
return nativeWeakRef;
|
|
11661
|
-
})(
|
|
11653
|
+
})(globalScope, WeakRefFactory);
|
|
11662
11654
|
|
|
11663
11655
|
const trimString = (str, limit) => {
|
|
11664
11656
|
const len = str.length;
|
|
@@ -20556,6 +20548,23 @@ var GuideStateModule = (function () {
|
|
|
20556
20548
|
};
|
|
20557
20549
|
})();
|
|
20558
20550
|
|
|
20551
|
+
function prioritizeAdoptOrCoreGuides(guides, shouldPrioritizeAdopt) {
|
|
20552
|
+
var adoptGuides = [];
|
|
20553
|
+
var coreGuides = [];
|
|
20554
|
+
_.each(guides, function (guide) {
|
|
20555
|
+
if (guide.isTraining) {
|
|
20556
|
+
adoptGuides.push(guide);
|
|
20557
|
+
}
|
|
20558
|
+
else {
|
|
20559
|
+
coreGuides.push(guide);
|
|
20560
|
+
}
|
|
20561
|
+
});
|
|
20562
|
+
if (shouldPrioritizeAdopt) {
|
|
20563
|
+
return adoptGuides.concat(coreGuides);
|
|
20564
|
+
}
|
|
20565
|
+
return coreGuides.concat(adoptGuides);
|
|
20566
|
+
}
|
|
20567
|
+
|
|
20559
20568
|
var AutoDisplay = (function () {
|
|
20560
20569
|
return {
|
|
20561
20570
|
canDisplay,
|
|
@@ -20639,24 +20648,6 @@ var AutoDisplay = (function () {
|
|
|
20639
20648
|
var prioritizeAdopt = ConfigReader.get('adoptPrioritizeAdoptGuides');
|
|
20640
20649
|
return prioritizeAdoptOrCoreGuides(sortedGuides, prioritizeAdopt);
|
|
20641
20650
|
}
|
|
20642
|
-
function prioritizeAdoptOrCoreGuides(guides, shouldPrioritizeAdopt) {
|
|
20643
|
-
var adoptGuides = [];
|
|
20644
|
-
var coreGuides = [];
|
|
20645
|
-
_.each(guides, function (guide) {
|
|
20646
|
-
if (guide.isTraining) {
|
|
20647
|
-
adoptGuides.push(guide);
|
|
20648
|
-
}
|
|
20649
|
-
else {
|
|
20650
|
-
coreGuides.push(guide);
|
|
20651
|
-
}
|
|
20652
|
-
});
|
|
20653
|
-
if (shouldPrioritizeAdopt) {
|
|
20654
|
-
return adoptGuides.concat(coreGuides);
|
|
20655
|
-
}
|
|
20656
|
-
else {
|
|
20657
|
-
return coreGuides.concat(adoptGuides);
|
|
20658
|
-
}
|
|
20659
|
-
}
|
|
20660
20651
|
function lastDismissedTime(pendo) {
|
|
20661
20652
|
return Math.max(pendo.latestDismissedAutoAt || -Infinity, pendo.finalAdvancedAutoAt || -Infinity, pendo.latestSnoozedAutoAt || -Infinity);
|
|
20662
20653
|
}
|
|
@@ -20866,642 +20857,6 @@ class AutoDisplayPhase {
|
|
|
20866
20857
|
}
|
|
20867
20858
|
}
|
|
20868
20859
|
|
|
20869
|
-
/*
|
|
20870
|
-
* Guide Loop
|
|
20871
|
-
*
|
|
20872
|
-
* The function that gets called to process and handle the ever
|
|
20873
|
-
* changing landscape of a web app to best handle showing / hiding of
|
|
20874
|
-
* guides as needed.
|
|
20875
|
-
*/
|
|
20876
|
-
/*
|
|
20877
|
-
* Holds the timeout handle for the startGuides process.
|
|
20878
|
-
* This should work as a simple monitor on that process
|
|
20879
|
-
* to prevent it from being run too often.
|
|
20880
|
-
*/
|
|
20881
|
-
var guidesProcessingThreadHandle = null;
|
|
20882
|
-
var DEFAULT_TIMER_LENGTH = 500;
|
|
20883
|
-
var waitThenLoop = function (delayAmount) {
|
|
20884
|
-
delayAmount = delayAmount || DEFAULT_TIMER_LENGTH;
|
|
20885
|
-
guidesProcessingThreadHandle = _.delay(function () {
|
|
20886
|
-
guidesProcessingThreadHandle = null;
|
|
20887
|
-
startGuides();
|
|
20888
|
-
}, delayAmount);
|
|
20889
|
-
pendo$1.guidesProcessingThreadHandle = guidesProcessingThreadHandle;
|
|
20890
|
-
};
|
|
20891
|
-
var clearLoopTimer = function () {
|
|
20892
|
-
if (guidesProcessingThreadHandle) {
|
|
20893
|
-
clearTimeout(guidesProcessingThreadHandle);
|
|
20894
|
-
guidesProcessingThreadHandle = null;
|
|
20895
|
-
pendo$1.guidesProcessingThreadHandle = guidesProcessingThreadHandle;
|
|
20896
|
-
}
|
|
20897
|
-
store.dispatch('guideUpdate/stopScheduledUpdate');
|
|
20898
|
-
};
|
|
20899
|
-
/**
|
|
20900
|
-
* Clears any showing guides and prevents any loaded guides from rendering.
|
|
20901
|
-
* Loaded guides remain in memory, so calling startGuides will work just fine
|
|
20902
|
-
* after a stopGuides call.
|
|
20903
|
-
*
|
|
20904
|
-
* @access public
|
|
20905
|
-
* @category Guides
|
|
20906
|
-
* @example
|
|
20907
|
-
* pendo.stopGuides()
|
|
20908
|
-
*/
|
|
20909
|
-
var stopGuides = function () {
|
|
20910
|
-
hideGuides();
|
|
20911
|
-
updateRunner.cancel();
|
|
20912
|
-
// Remove other stuff.
|
|
20913
|
-
for (var i = 0; i < activeElements.length; i++) {
|
|
20914
|
-
var elem = activeElements[i];
|
|
20915
|
-
elem.parentNode.removeChild(elem);
|
|
20916
|
-
}
|
|
20917
|
-
activeElements.length = 0;
|
|
20918
|
-
store.dispatch('guideUpdate/stopObserver');
|
|
20919
|
-
Events.guideLoopStopped.trigger();
|
|
20920
|
-
};
|
|
20921
|
-
/**
|
|
20922
|
-
* Starts the process that loops over all currently loaded guides and
|
|
20923
|
-
* determines what to show. Checks multi-step guide continuation,
|
|
20924
|
-
* auto-display guides, launcher guides, and badges.
|
|
20925
|
-
*
|
|
20926
|
-
* @access public
|
|
20927
|
-
* @category Guides
|
|
20928
|
-
* @example
|
|
20929
|
-
* pendo.startGuides()
|
|
20930
|
-
*/
|
|
20931
|
-
var startGuides = function () {
|
|
20932
|
-
clearLoopTimer();
|
|
20933
|
-
if (isGuideRequestPending()) {
|
|
20934
|
-
log.info('guides are loading.', { contexts: ['guides', 'loading'] });
|
|
20935
|
-
return;
|
|
20936
|
-
}
|
|
20937
|
-
if (areGuidesDisabled()) {
|
|
20938
|
-
log.info('guides are disabled.', { contexts: ['guides', 'disabled'] });
|
|
20939
|
-
return;
|
|
20940
|
-
}
|
|
20941
|
-
if (areGuidesDelayed()) {
|
|
20942
|
-
log.info('guides are delayed.', { contexts: ['guides', 'delayed'] });
|
|
20943
|
-
return;
|
|
20944
|
-
}
|
|
20945
|
-
if (store.getters['guideState/tabLostFocus']())
|
|
20946
|
-
return;
|
|
20947
|
-
try {
|
|
20948
|
-
const activeGuides = getActiveGuides();
|
|
20949
|
-
if (!activeGuides || activeGuides.length === 0)
|
|
20950
|
-
return;
|
|
20951
|
-
store.dispatch('guideUpdate/startObserverIfNeeded');
|
|
20952
|
-
if (updateRunner.isComplete()) {
|
|
20953
|
-
updateRunner.markDirty();
|
|
20954
|
-
}
|
|
20955
|
-
if (leaderExists()) {
|
|
20956
|
-
updateRunner.process();
|
|
20957
|
-
}
|
|
20958
|
-
}
|
|
20959
|
-
catch (e) {
|
|
20960
|
-
updateRunner.cancel();
|
|
20961
|
-
log.critical('ERROR in guide-loop', { error: e });
|
|
20962
|
-
}
|
|
20963
|
-
finally {
|
|
20964
|
-
if (!updateRunner.paused) {
|
|
20965
|
-
continueOrCompleteUpdate();
|
|
20966
|
-
}
|
|
20967
|
-
}
|
|
20968
|
-
};
|
|
20969
|
-
function continueOrCompleteUpdate() {
|
|
20970
|
-
if (!updateRunner.isComplete()) {
|
|
20971
|
-
waitThenLoop(50);
|
|
20972
|
-
}
|
|
20973
|
-
else {
|
|
20974
|
-
if (!store.state.guideUpdate.useObserver || store.state.guideUpdate.needsUpdate) {
|
|
20975
|
-
waitThenLoop();
|
|
20976
|
-
}
|
|
20977
|
-
Events.guideUpdateComplete.trigger();
|
|
20978
|
-
}
|
|
20979
|
-
}
|
|
20980
|
-
/*
|
|
20981
|
-
* If delayGuides is true, removes the delay and starts guides.
|
|
20982
|
-
* Otherwise, it is the same as calling startGuides.
|
|
20983
|
-
*/
|
|
20984
|
-
var manuallyStartGuides = function () {
|
|
20985
|
-
setGuidesDelayed(false);
|
|
20986
|
-
startGuides();
|
|
20987
|
-
};
|
|
20988
|
-
var manuallyStopGuides = function () {
|
|
20989
|
-
if (!areGuidesDisabled()) {
|
|
20990
|
-
setGuidesDelayed(true);
|
|
20991
|
-
}
|
|
20992
|
-
resetPendoUI();
|
|
20993
|
-
};
|
|
20994
|
-
// a phase that only has one unit of work per update
|
|
20995
|
-
class SimplePhase {
|
|
20996
|
-
constructor(name, processFn) {
|
|
20997
|
-
this.dirty = false;
|
|
20998
|
-
this.name = name;
|
|
20999
|
-
this.processFn = _.isFunction(processFn) ? processFn : _.noop;
|
|
21000
|
-
}
|
|
21001
|
-
markDirty() {
|
|
21002
|
-
this.dirty = true;
|
|
21003
|
-
}
|
|
21004
|
-
process() {
|
|
21005
|
-
this.processFn();
|
|
21006
|
-
this.dirty = false;
|
|
21007
|
-
}
|
|
21008
|
-
cancel() {
|
|
21009
|
-
this.dirty = false;
|
|
21010
|
-
}
|
|
21011
|
-
isComplete() {
|
|
21012
|
-
return !this.dirty;
|
|
21013
|
-
}
|
|
21014
|
-
}
|
|
21015
|
-
/**
|
|
21016
|
-
* Runs on a timer whenever a guide is displayed.
|
|
21017
|
-
*
|
|
21018
|
-
* @access private
|
|
21019
|
-
* @param {Guide} guide The currently visible guide
|
|
21020
|
-
* @param {GuideStep} step The currently visible step
|
|
21021
|
-
*/
|
|
21022
|
-
function guideShowingProc() {
|
|
21023
|
-
const shownGuides = _.compact(_.map(_.filter(frameLocalGuides(getActiveGuides()), (guide) => guide.isShown()), (guide) => {
|
|
21024
|
-
if (BuildingBlockResourceCenter.isModule(guide)) {
|
|
21025
|
-
return BuildingBlockResourceCenter.getResourceCenter();
|
|
21026
|
-
}
|
|
21027
|
-
else {
|
|
21028
|
-
return guide;
|
|
21029
|
-
}
|
|
21030
|
-
}));
|
|
21031
|
-
_.each(shownGuides, function (guide) {
|
|
21032
|
-
_.each(guide.steps, function (step) {
|
|
21033
|
-
if (step.isShown()) {
|
|
21034
|
-
stepShowingProc(guide, step);
|
|
21035
|
-
}
|
|
21036
|
-
});
|
|
21037
|
-
guide.checkForHiddenGroupSteps();
|
|
21038
|
-
});
|
|
21039
|
-
}
|
|
21040
|
-
function stepShowingProc(guide, step) {
|
|
21041
|
-
if (step.isLocked()) {
|
|
21042
|
-
return;
|
|
21043
|
-
}
|
|
21044
|
-
if (store.getters['frames/isShownInAnotherFrame']()(step)) {
|
|
21045
|
-
return;
|
|
21046
|
-
}
|
|
21047
|
-
var element = step.element;
|
|
21048
|
-
var targetElement = step.targetElement;
|
|
21049
|
-
var guideElement = step.guideElement;
|
|
21050
|
-
var guideElm = dom('.' + GUIDE_CSS_NAME);
|
|
21051
|
-
// As part of the guides accessibility work, we need to make sure that all
|
|
21052
|
-
// guide content remain visible and/or accessible during text-only (font) zoom.
|
|
21053
|
-
// Since text zoom does NOT trigger our existing resize listener, the
|
|
21054
|
-
// alternative is to track text zoom via font-size changes.
|
|
21055
|
-
var guideContainer = guideElement.find('#pendo-guide-container')[0];
|
|
21056
|
-
var guideHasTextContent = guideContainer && guideContainer.textContent;
|
|
21057
|
-
if (guideHasTextContent) {
|
|
21058
|
-
var currentBrowserFontSize = getComputedStyle_safe(guideContainer).fontSize;
|
|
21059
|
-
var hasBrowserFontSizeChanged = step.attributes.currentTextZoomFontSize !== currentBrowserFontSize;
|
|
21060
|
-
if (hasBrowserFontSizeChanged) {
|
|
21061
|
-
if (step.attributes.currentTextZoomFontSize) {
|
|
21062
|
-
step.redraw();
|
|
21063
|
-
}
|
|
21064
|
-
step.attributes.currentTextZoomFontSize = currentBrowserFontSize;
|
|
21065
|
-
}
|
|
21066
|
-
}
|
|
21067
|
-
syncColorMode(step, true);
|
|
21068
|
-
if (step.elementPathRule && targetElement && !SizzleProxy.matchesSelector(targetElement, step.elementPathRule)) {
|
|
21069
|
-
step.hide();
|
|
21070
|
-
return;
|
|
21071
|
-
}
|
|
21072
|
-
const embedSelector = _.get(guide, 'attributes.embedConfig.selector');
|
|
21073
|
-
if (embedSelector && !dom(embedSelector).length) {
|
|
21074
|
-
step.hide();
|
|
21075
|
-
return;
|
|
21076
|
-
}
|
|
21077
|
-
var elementIsStillValidTarget = isElementVisible(element);
|
|
21078
|
-
var elementMatchesContainsRules = true;
|
|
21079
|
-
// targetElement is specifically the elementPathRule dom node
|
|
21080
|
-
// element is either the element path rule OR the badge element
|
|
21081
|
-
// we can rely on the isElementVisible check as a shortcut for having to call isElementVisible twice
|
|
21082
|
-
if (targetElement && elementIsStillValidTarget && step.elementContainsRulesList) {
|
|
21083
|
-
// Only bother trying to check element contains rules if the element is still visible
|
|
21084
|
-
elementMatchesContainsRules = doesElementMatchContainsRules(targetElement, step.elementContainsRulesList);
|
|
21085
|
-
elementIsStillValidTarget = elementMatchesContainsRules;
|
|
21086
|
-
}
|
|
21087
|
-
if (element && (elementIsStillValidTarget || guideElm.hasClass('mouseover'))) {
|
|
21088
|
-
step.checkPlacement();
|
|
21089
|
-
return;
|
|
21090
|
-
}
|
|
21091
|
-
var isTooltip = (step.type === 'tooltip' || step.attributes.calculatedType === 'tooltip');
|
|
21092
|
-
if (isTooltip && wouldBeVisibleAfterAutoScroll(element) && elementMatchesContainsRules) {
|
|
21093
|
-
// the element was scrolled out of view, the guide should be invisible for now, but still "active"
|
|
21094
|
-
return;
|
|
21095
|
-
}
|
|
21096
|
-
step.hide();
|
|
21097
|
-
}
|
|
21098
|
-
class BadgePhase {
|
|
21099
|
-
constructor() {
|
|
21100
|
-
this.name = 'badge';
|
|
21101
|
-
this.queue = [];
|
|
21102
|
-
}
|
|
21103
|
-
markDirty() {
|
|
21104
|
-
this.queue.push.apply(this.queue, _.filter(frameLocalGuides(getActiveGuides()), isBadge));
|
|
21105
|
-
}
|
|
21106
|
-
process(monitor) {
|
|
21107
|
-
var badge = this.queue.shift();
|
|
21108
|
-
monitor.saveContext(badge);
|
|
21109
|
-
if (badge) {
|
|
21110
|
-
badge.placeBadge();
|
|
21111
|
-
}
|
|
21112
|
-
}
|
|
21113
|
-
handleProcessTimeExceeded(monitor) {
|
|
21114
|
-
const badge = monitor.getContext();
|
|
21115
|
-
log.warn('Badge (' + badge.id + ') timeout exceeded');
|
|
21116
|
-
}
|
|
21117
|
-
cancel() {
|
|
21118
|
-
this.queue.length = 0;
|
|
21119
|
-
}
|
|
21120
|
-
isComplete() {
|
|
21121
|
-
return !this.queue.length;
|
|
21122
|
-
}
|
|
21123
|
-
}
|
|
21124
|
-
function resourceCenterShowingProc(resourceCenter, resourceCenterBadgeProc) {
|
|
21125
|
-
var reEvaluateModuleContent = false;
|
|
21126
|
-
// Find eligible modules
|
|
21127
|
-
var controlGuidesInAllModules = [];
|
|
21128
|
-
var eligibleModules = _.filter(resourceCenter.modules, function (module) {
|
|
21129
|
-
if (!module)
|
|
21130
|
-
return false;
|
|
21131
|
-
// Iframe modules can not dynamically show/hide.
|
|
21132
|
-
if (BuildingBlockResourceCenter.isIframeModule(module))
|
|
21133
|
-
return true;
|
|
21134
|
-
// Sandbox modules can not dynamically show/hide.
|
|
21135
|
-
if (BuildingBlockResourceCenter.isSandboxModule(module))
|
|
21136
|
-
return true;
|
|
21137
|
-
if (BuildingBlockResourceCenter.isIntegrationModule(module)) {
|
|
21138
|
-
if (!BuildingBlockResourceCenter.integrationModuleHasContent(module)) {
|
|
21139
|
-
module.hasResourceCenterContent = false;
|
|
21140
|
-
return false;
|
|
21141
|
-
}
|
|
21142
|
-
else {
|
|
21143
|
-
return true;
|
|
21144
|
-
}
|
|
21145
|
-
}
|
|
21146
|
-
var eligibleGuidesInModule = _.filter(module.guidesInModule, function (guide) {
|
|
21147
|
-
if (!guide.shouldBeAddedToResourceCenter()) {
|
|
21148
|
-
guide.ineligibleForRC = true;
|
|
21149
|
-
if (guide.control) {
|
|
21150
|
-
controlGuidesInAllModules.push(guide);
|
|
21151
|
-
}
|
|
21152
|
-
return false;
|
|
21153
|
-
}
|
|
21154
|
-
guide.ineligibleForRC = false;
|
|
21155
|
-
return true;
|
|
21156
|
-
});
|
|
21157
|
-
var isEligible = eligibleGuidesInModule.length;
|
|
21158
|
-
module.hasResourceCenterContent = !!isEligible;
|
|
21159
|
-
// We only need to hash the active module, no other module has content rendered
|
|
21160
|
-
if (resourceCenter.activeModule && module.id === resourceCenter.activeModule.id) {
|
|
21161
|
-
var moduleContentHash = crc32(_.map(eligibleGuidesInModule, function (guide) {
|
|
21162
|
-
// don't change content hash when announcements update their seen state, as that will cause
|
|
21163
|
-
// the RC to re-render (and lose scroll position)
|
|
21164
|
-
var isAnnouncementOrWhatsNew = guide.isAnnouncement() || guide.isWhatsNew();
|
|
21165
|
-
var seenState = isAnnouncementOrWhatsNew ? null : _.pluck(guide.steps, 'seenState');
|
|
21166
|
-
return {
|
|
21167
|
-
id: guide.id,
|
|
21168
|
-
seenState
|
|
21169
|
-
};
|
|
21170
|
-
}));
|
|
21171
|
-
if (module.eligibleGuidesInModuleHash && moduleContentHash !== module.eligibleGuidesInModuleHash)
|
|
21172
|
-
reEvaluateModuleContent = true;
|
|
21173
|
-
module.eligibleGuidesInModuleHash = moduleContentHash;
|
|
21174
|
-
}
|
|
21175
|
-
return isEligible;
|
|
21176
|
-
});
|
|
21177
|
-
// If we are rendering the home view and its modules change re-render it
|
|
21178
|
-
var reEvaluateHomeViewContent = false;
|
|
21179
|
-
if (!resourceCenter.activeModule) {
|
|
21180
|
-
var eligibleModulesHash = crc32(_.map(eligibleModules, function (module) {
|
|
21181
|
-
return {
|
|
21182
|
-
id: module.id
|
|
21183
|
-
};
|
|
21184
|
-
}));
|
|
21185
|
-
if (resourceCenter.eligibleModulesHash && resourceCenter.eligibleModulesHash !== eligibleModulesHash) {
|
|
21186
|
-
reEvaluateHomeViewContent = true;
|
|
21187
|
-
BuildingBlockResourceCenter.replaceResourceCenterContent(resourceCenter.id, undefined, true);
|
|
21188
|
-
}
|
|
21189
|
-
resourceCenter.eligibleModulesHash = eligibleModulesHash;
|
|
21190
|
-
}
|
|
21191
|
-
resourceCenterBadgeProc(resourceCenter, eligibleModules, controlGuidesInAllModules);
|
|
21192
|
-
// Skip directly to the one eligible module
|
|
21193
|
-
if (eligibleModules.length === 1) {
|
|
21194
|
-
resourceCenter.skipResourceCenterHomeView = true;
|
|
21195
|
-
resourceCenter.moduleIdToReplaceHomeViewWith = eligibleModules[0].id;
|
|
21196
|
-
var noActiveModule = !resourceCenter.activeModule && reEvaluateHomeViewContent;
|
|
21197
|
-
var differentActiveModule = resourceCenter.activeModule && resourceCenter.activeModule.id !== eligibleModules[0].id;
|
|
21198
|
-
// If the one module is different or content needs to be reevaluated, reload
|
|
21199
|
-
// or if we are on the home view and the modules on the home view have changed
|
|
21200
|
-
if (noActiveModule || differentActiveModule || reEvaluateModuleContent) {
|
|
21201
|
-
BuildingBlockResourceCenter.replaceResourceCenterContent(resourceCenter.moduleIdToReplaceHomeViewWith, undefined, true);
|
|
21202
|
-
}
|
|
21203
|
-
}
|
|
21204
|
-
if (eligibleModules) {
|
|
21205
|
-
// find the in-progress native integration module and set continueToNativeModule on both the
|
|
21206
|
-
// RC container and the module since both will try to render
|
|
21207
|
-
var continuedModule = _.find(eligibleModules, function (module) {
|
|
21208
|
-
return module.isContinuation(getLastGuideStepSeen());
|
|
21209
|
-
});
|
|
21210
|
-
var shouldNotResume = _.get(continuedModule, 'attributes.doNotResume');
|
|
21211
|
-
var integrationProvider = _.get(continuedModule, 'attributes.resourceCenter.integrationProvider');
|
|
21212
|
-
var isNativeModule = BuildingBlockResourceCenter.isSupportedNativeIntegration(integrationProvider);
|
|
21213
|
-
if (isNativeModule && !shouldNotResume && resourceCenter) {
|
|
21214
|
-
resourceCenter.continueToNativeModule = true;
|
|
21215
|
-
continuedModule.continueToNativeModule = true;
|
|
21216
|
-
}
|
|
21217
|
-
}
|
|
21218
|
-
// Ensure the active module is still eligible and re-evaluate its content if need be.
|
|
21219
|
-
// Go to Home View if active module is no longer eligible
|
|
21220
|
-
var activeModuleisEligible;
|
|
21221
|
-
if (resourceCenter.activeModule) {
|
|
21222
|
-
activeModuleisEligible = _.find(eligibleModules, function (module) {
|
|
21223
|
-
return module.id === resourceCenter.activeModule.id;
|
|
21224
|
-
});
|
|
21225
|
-
if (activeModuleisEligible && reEvaluateModuleContent) {
|
|
21226
|
-
BuildingBlockResourceCenter.replaceResourceCenterContent(resourceCenter.activeModule.id, undefined, true);
|
|
21227
|
-
}
|
|
21228
|
-
if (!activeModuleisEligible) {
|
|
21229
|
-
BuildingBlockResourceCenter.replaceResourceCenterContent(resourceCenter.id);
|
|
21230
|
-
}
|
|
21231
|
-
}
|
|
21232
|
-
BuildingBlockResourceCenter.repositionResourceCenter();
|
|
21233
|
-
}
|
|
21234
|
-
function resourceCenterNotShowingProc(resourceCenter, resourceCenterBadgeProc) {
|
|
21235
|
-
var eligibleModules = [];
|
|
21236
|
-
var controlGuidesInAllModules = [];
|
|
21237
|
-
var staticModule = _.find(resourceCenter.modules, function (module) {
|
|
21238
|
-
if (BuildingBlockResourceCenter.isIframeModule(module))
|
|
21239
|
-
return true;
|
|
21240
|
-
if (BuildingBlockResourceCenter.isSandboxModule(module))
|
|
21241
|
-
return true;
|
|
21242
|
-
if (BuildingBlockResourceCenter.isIntegrationModule(module)) {
|
|
21243
|
-
if (!BuildingBlockResourceCenter.integrationModuleHasContent(module)) {
|
|
21244
|
-
module.hasResourceCenterContent = false;
|
|
21245
|
-
return false;
|
|
21246
|
-
}
|
|
21247
|
-
else {
|
|
21248
|
-
return true;
|
|
21249
|
-
}
|
|
21250
|
-
}
|
|
21251
|
-
return false;
|
|
21252
|
-
});
|
|
21253
|
-
if (staticModule) {
|
|
21254
|
-
eligibleModules.push(staticModule);
|
|
21255
|
-
}
|
|
21256
|
-
var allGuidesInModules = _.flatten(_.pluck(resourceCenter.modules, 'guidesInModule'));
|
|
21257
|
-
controlGuidesInAllModules = _.filter(_.compact(allGuidesInModules), function (guide) {
|
|
21258
|
-
return guide.control;
|
|
21259
|
-
});
|
|
21260
|
-
if (!eligibleModules.length) {
|
|
21261
|
-
var moduleLookup = _.reduce(resourceCenter.modules, function (lookup, module) {
|
|
21262
|
-
_.each(module.guidesInModule, function (guide) {
|
|
21263
|
-
lookup[guide.id] = module;
|
|
21264
|
-
});
|
|
21265
|
-
return lookup;
|
|
21266
|
-
}, {});
|
|
21267
|
-
var guidesWithSteps = _.filter(allGuidesInModules, function (guide) {
|
|
21268
|
-
return guide && guide.steps && guide.steps.length;
|
|
21269
|
-
});
|
|
21270
|
-
// Sort guides so that untargeted guides are first
|
|
21271
|
-
var sortedGuides = _.sortBy(guidesWithSteps, function (guide) {
|
|
21272
|
-
return guide.steps[0].elementPathRule ? 1 : 0;
|
|
21273
|
-
});
|
|
21274
|
-
var firstEligibleGuide = _.find(sortedGuides, function (guide) {
|
|
21275
|
-
return guide.shouldBeAddedToResourceCenter();
|
|
21276
|
-
});
|
|
21277
|
-
var firstEligibleModule = moduleLookup[firstEligibleGuide && firstEligibleGuide.id];
|
|
21278
|
-
if (firstEligibleModule) {
|
|
21279
|
-
eligibleModules.push(firstEligibleModule);
|
|
21280
|
-
}
|
|
21281
|
-
}
|
|
21282
|
-
resourceCenterBadgeProc(resourceCenter, eligibleModules, controlGuidesInAllModules);
|
|
21283
|
-
}
|
|
21284
|
-
function resourceCenterBadgeProc(resourceCenter, eligibleModules, controlGuidesInAllModules) {
|
|
21285
|
-
var RCBadge = badgesShown[resourceCenter.id];
|
|
21286
|
-
// If we get no modules back, hide the RC and RC Badge, clear active module
|
|
21287
|
-
if (!eligibleModules || !eligibleModules.length) {
|
|
21288
|
-
var isBadgeActivated = resourceCenter.launchMethod.indexOf('badge') !== -1;
|
|
21289
|
-
var isElementActivated = resourceCenter.launchMethod.indexOf('dom') !== -1;
|
|
21290
|
-
resourceCenter.hasResourceCenterContent = false;
|
|
21291
|
-
// If the RC is badge activated, hide it from the dom completely. Otherwise, leave it alone
|
|
21292
|
-
// This is important for element-activated RC, which should still display with an empty state
|
|
21293
|
-
// even if it has no content
|
|
21294
|
-
if (isBadgeActivated) {
|
|
21295
|
-
if (resourceCenter.isShown())
|
|
21296
|
-
resourceCenter.hide();
|
|
21297
|
-
if (RCBadge) {
|
|
21298
|
-
BuildingBlockResourceCenter.hideRCBadge(RCBadge, resourceCenter);
|
|
21299
|
-
}
|
|
21300
|
-
// If all modules are not eligible because of control group guides
|
|
21301
|
-
// Send guideNotSeen events where appropriate
|
|
21302
|
-
_.each(controlGuidesInAllModules, function (guide) {
|
|
21303
|
-
if (!guide.hasGuideBeenControlled()) {
|
|
21304
|
-
guide.steps[0].onControlGuide('control');
|
|
21305
|
-
}
|
|
21306
|
-
});
|
|
21307
|
-
resourceCenter.badgeHidden = true;
|
|
21308
|
-
delete resourceCenter.activeModule;
|
|
21309
|
-
}
|
|
21310
|
-
else if (isElementActivated) {
|
|
21311
|
-
resourceCenter.showEmptyState = true;
|
|
21312
|
-
resourceCenter.controlGuidesInAllModules = controlGuidesInAllModules;
|
|
21313
|
-
}
|
|
21314
|
-
}
|
|
21315
|
-
// Show the badge if it was hidden but now has content
|
|
21316
|
-
if (resourceCenter.badgeHidden && resourceCenter.hasResourceCenterContent && !resourceCenter.isNativeModuleActive) {
|
|
21317
|
-
if (!RCBadge) {
|
|
21318
|
-
if (_.isFunction(resourceCenter.placeBadge))
|
|
21319
|
-
resourceCenter.placeBadge();
|
|
21320
|
-
RCBadge = badgesShown[resourceCenter.id];
|
|
21321
|
-
}
|
|
21322
|
-
if (RCBadge)
|
|
21323
|
-
RCBadge.show();
|
|
21324
|
-
resourceCenter.badgeHidden = false;
|
|
21325
|
-
}
|
|
21326
|
-
}
|
|
21327
|
-
function resourceCenterProc(resourceCenter) {
|
|
21328
|
-
if (!resourceCenter || !resourceCenter.launchMethod)
|
|
21329
|
-
return;
|
|
21330
|
-
// Reset state to start each loop
|
|
21331
|
-
resourceCenter.skipResourceCenterHomeView = false;
|
|
21332
|
-
resourceCenter.hasResourceCenterContent = true;
|
|
21333
|
-
delete resourceCenter.moduleIdToReplaceHomeViewWith;
|
|
21334
|
-
var activeModule = resourceCenter.activeModule;
|
|
21335
|
-
var activeIntegrationProvider = _.get(activeModule, 'attributes.resourceCenter.integrationProvider');
|
|
21336
|
-
resourceCenter.isNativeModuleActive = activeModule && BuildingBlockResourceCenter.isSupportedNativeIntegration(activeIntegrationProvider);
|
|
21337
|
-
if (resourceCenter.isShown() || !store.getters['frames/isLeader']()) {
|
|
21338
|
-
resourceCenterShowingProc(resourceCenter, resourceCenterBadgeProc);
|
|
21339
|
-
}
|
|
21340
|
-
else {
|
|
21341
|
-
resourceCenterNotShowingProc(resourceCenter, resourceCenterBadgeProc);
|
|
21342
|
-
}
|
|
21343
|
-
// Hide the badge if we're displaying a native integration module
|
|
21344
|
-
if (resourceCenter.isNativeModuleActive) {
|
|
21345
|
-
if (resourceCenter.isShown())
|
|
21346
|
-
resourceCenter.hide();
|
|
21347
|
-
var RCBadge = badgesShown[resourceCenter.id];
|
|
21348
|
-
if (RCBadge) {
|
|
21349
|
-
BuildingBlockResourceCenter.hideRCBadge(RCBadge, resourceCenter);
|
|
21350
|
-
}
|
|
21351
|
-
}
|
|
21352
|
-
// Place Element Activated Notification Badge
|
|
21353
|
-
if (resourceCenter.launchMethod === 'dom' && resourceCenter.canShowOnDevice()) {
|
|
21354
|
-
BuildingBlockResourceCenter.updateNotificationBubbles();
|
|
21355
|
-
BuildingBlockResourceCenter.adjustNotificationBubbleForResize();
|
|
21356
|
-
}
|
|
21357
|
-
}
|
|
21358
|
-
class PhaseProcessTimeMonitor {
|
|
21359
|
-
constructor(timeLimit = 50) {
|
|
21360
|
-
this.startTime = -1;
|
|
21361
|
-
this.timeLimit = timeLimit;
|
|
21362
|
-
this.context = null;
|
|
21363
|
-
}
|
|
21364
|
-
start() {
|
|
21365
|
-
this.context = null;
|
|
21366
|
-
this.startTime = getNow();
|
|
21367
|
-
}
|
|
21368
|
-
isTimeExceeded() {
|
|
21369
|
-
const currTime = getNow();
|
|
21370
|
-
return (currTime - this.startTime) > this.timeLimit;
|
|
21371
|
-
}
|
|
21372
|
-
saveContext(context) {
|
|
21373
|
-
this.context = context;
|
|
21374
|
-
}
|
|
21375
|
-
getContext() {
|
|
21376
|
-
return this.context;
|
|
21377
|
-
}
|
|
21378
|
-
}
|
|
21379
|
-
class UpdateRunner {
|
|
21380
|
-
constructor(phases) {
|
|
21381
|
-
this.phases = phases;
|
|
21382
|
-
this.queue = [];
|
|
21383
|
-
this.paused = false;
|
|
21384
|
-
}
|
|
21385
|
-
markDirty() {
|
|
21386
|
-
for (let phase of this.phases) {
|
|
21387
|
-
phase.markDirty();
|
|
21388
|
-
}
|
|
21389
|
-
this.queue = this.phases.slice();
|
|
21390
|
-
}
|
|
21391
|
-
runGuidePhase() {
|
|
21392
|
-
const currentPhase = this.queue[0];
|
|
21393
|
-
var promise;
|
|
21394
|
-
const monitor = new PhaseProcessTimeMonitor(50);
|
|
21395
|
-
while (!this.timedOut() && !currentPhase.isComplete() && !promise) {
|
|
21396
|
-
monitor.start();
|
|
21397
|
-
promise = currentPhase.process(monitor);
|
|
21398
|
-
if (!promise && monitor.isTimeExceeded()) {
|
|
21399
|
-
performanceMonitor.count(GUIDE_LOOP_TIMEOUT);
|
|
21400
|
-
if (currentPhase.handleProcessTimeExceeded) {
|
|
21401
|
-
currentPhase.handleProcessTimeExceeded(monitor);
|
|
21402
|
-
}
|
|
21403
|
-
this.ejectGuide(monitor.getContext());
|
|
21404
|
-
}
|
|
21405
|
-
}
|
|
21406
|
-
if (isPromise(promise)) {
|
|
21407
|
-
var id = this.updateId;
|
|
21408
|
-
var resume = _.bind(function () {
|
|
21409
|
-
if (this.updateId === id) {
|
|
21410
|
-
this.paused = false;
|
|
21411
|
-
startGuides();
|
|
21412
|
-
}
|
|
21413
|
-
}, this);
|
|
21414
|
-
this.paused = true;
|
|
21415
|
-
promise.then(resume).catch(resume);
|
|
21416
|
-
}
|
|
21417
|
-
else if (currentPhase.isComplete()) {
|
|
21418
|
-
this.queue.shift();
|
|
21419
|
-
}
|
|
21420
|
-
}
|
|
21421
|
-
addPhase(phaseFn, phaseName) {
|
|
21422
|
-
phaseName = phaseName || phaseFn.name || this.phases.length;
|
|
21423
|
-
if (_.isFunction(phaseFn)) {
|
|
21424
|
-
phaseFn = new SimplePhase(phaseName, phaseFn);
|
|
21425
|
-
}
|
|
21426
|
-
this.phases.push(phaseFn);
|
|
21427
|
-
}
|
|
21428
|
-
removePhase(phaseName) {
|
|
21429
|
-
var i = _.findIndex(this.phases, function (phase) {
|
|
21430
|
-
return phase.name === phaseName;
|
|
21431
|
-
});
|
|
21432
|
-
if (i >= 0) {
|
|
21433
|
-
this.phases.splice(i, 1);
|
|
21434
|
-
}
|
|
21435
|
-
}
|
|
21436
|
-
timedOut() {
|
|
21437
|
-
if (!this.iterationStartTime)
|
|
21438
|
-
return false;
|
|
21439
|
-
var elapsed = getNow() - this.iterationStartTime;
|
|
21440
|
-
return elapsed > store.state.guideUpdate.iterationTimeLimit;
|
|
21441
|
-
}
|
|
21442
|
-
ejectGuide(guide) {
|
|
21443
|
-
if (this.ejectOnTimeout === undefined) {
|
|
21444
|
-
this.ejectOnTimeout = ConfigReader.get('guides.ejectOnTimeout');
|
|
21445
|
-
}
|
|
21446
|
-
if (this.ejectOnTimeout && guide && !guide.isShown()) {
|
|
21447
|
-
Tombstone.addGuide(guide);
|
|
21448
|
-
}
|
|
21449
|
-
}
|
|
21450
|
-
process() {
|
|
21451
|
-
var now = getNow();
|
|
21452
|
-
if (!this.updateId) {
|
|
21453
|
-
this.updateId = now;
|
|
21454
|
-
this.updateTotalTime = 0;
|
|
21455
|
-
store.commit('guideUpdate/startUpdate', now);
|
|
21456
|
-
}
|
|
21457
|
-
this.iterationStartTime = now;
|
|
21458
|
-
while (!this.paused &&
|
|
21459
|
-
this.queue.length &&
|
|
21460
|
-
!this.timedOut()) {
|
|
21461
|
-
this.runGuidePhase();
|
|
21462
|
-
}
|
|
21463
|
-
now = getNow();
|
|
21464
|
-
this.updateTotalTime += now - this.iterationStartTime;
|
|
21465
|
-
if (this.isComplete()) {
|
|
21466
|
-
this.complete(now);
|
|
21467
|
-
}
|
|
21468
|
-
}
|
|
21469
|
-
complete(time) {
|
|
21470
|
-
store.commit('guideUpdate/completeUpdate', time);
|
|
21471
|
-
this.cancel();
|
|
21472
|
-
}
|
|
21473
|
-
cancel() {
|
|
21474
|
-
this.updateId = null;
|
|
21475
|
-
this.paused = false;
|
|
21476
|
-
this.queue.length = 0;
|
|
21477
|
-
for (let phase of this.phases) {
|
|
21478
|
-
if (_.isFunction(phase.cancel)) {
|
|
21479
|
-
phase.cancel();
|
|
21480
|
-
}
|
|
21481
|
-
}
|
|
21482
|
-
}
|
|
21483
|
-
isComplete() {
|
|
21484
|
-
return !this.queue.length;
|
|
21485
|
-
}
|
|
21486
|
-
}
|
|
21487
|
-
var updateRunner = new UpdateRunner([
|
|
21488
|
-
new SimplePhase('guideShowing', guideShowingProc),
|
|
21489
|
-
new AutoDisplayPhase(),
|
|
21490
|
-
new BadgePhase(),
|
|
21491
|
-
new SimplePhase('resourceCenter', function () {
|
|
21492
|
-
resourceCenterProc(BuildingBlockResourceCenter.getResourceCenter());
|
|
21493
|
-
}),
|
|
21494
|
-
new SimplePhase('frameVisibility', function () {
|
|
21495
|
-
store.dispatch('frames/checkFrameVisibility');
|
|
21496
|
-
})
|
|
21497
|
-
]);
|
|
21498
|
-
function addUpdatePhase() {
|
|
21499
|
-
updateRunner.addPhase(...arguments);
|
|
21500
|
-
}
|
|
21501
|
-
function removeUpdatePhase() {
|
|
21502
|
-
updateRunner.removePhase(...arguments);
|
|
21503
|
-
}
|
|
21504
|
-
|
|
21505
20860
|
function Wrappable() {
|
|
21506
20861
|
var wrappers = {};
|
|
21507
20862
|
var wrap = function (wrappedMethod, before, after) {
|
|
@@ -22699,118 +22054,809 @@ function ShowOnHover() {
|
|
|
22699
22054
|
mouseout();
|
|
22700
22055
|
}
|
|
22701
22056
|
});
|
|
22702
|
-
badge.after('hide', function () {
|
|
22703
|
-
if (attached) {
|
|
22704
|
-
detachEvent(target, 'mouseover', mouseover);
|
|
22705
|
-
attached = false;
|
|
22706
|
-
}
|
|
22707
|
-
mouseout();
|
|
22057
|
+
badge.after('hide', function () {
|
|
22058
|
+
if (attached) {
|
|
22059
|
+
detachEvent(target, 'mouseover', mouseover);
|
|
22060
|
+
attached = false;
|
|
22061
|
+
}
|
|
22062
|
+
mouseout();
|
|
22063
|
+
});
|
|
22064
|
+
}
|
|
22065
|
+
return badge;
|
|
22066
|
+
}
|
|
22067
|
+
var BADGE_CLAIM_PROP = '_pendoBadgeClaimIsTraining';
|
|
22068
|
+
var badgeClaimTargets = [];
|
|
22069
|
+
function resetBadgeTargetClaims() {
|
|
22070
|
+
_.each(badgeClaimTargets, function (target) {
|
|
22071
|
+
target[BADGE_CLAIM_PROP] = undefined;
|
|
22072
|
+
});
|
|
22073
|
+
badgeClaimTargets.length = 0;
|
|
22074
|
+
}
|
|
22075
|
+
function isBadgeTargetClaimedByOppositeTraining(guide, target) {
|
|
22076
|
+
if (!target)
|
|
22077
|
+
return false;
|
|
22078
|
+
var claimedIsTraining = target[BADGE_CLAIM_PROP];
|
|
22079
|
+
if (claimedIsTraining === undefined)
|
|
22080
|
+
return false;
|
|
22081
|
+
return claimedIsTraining !== !!guide.isTraining;
|
|
22082
|
+
}
|
|
22083
|
+
function claimBadgeTarget(guide, target) {
|
|
22084
|
+
if (!target)
|
|
22085
|
+
return;
|
|
22086
|
+
target[BADGE_CLAIM_PROP] = !!guide.isTraining;
|
|
22087
|
+
if (!_.contains(badgeClaimTargets, target)) {
|
|
22088
|
+
badgeClaimTargets.push(target);
|
|
22089
|
+
}
|
|
22090
|
+
}
|
|
22091
|
+
function hideBadgeForGuideInMap(guide, badgesShownObj) {
|
|
22092
|
+
var existingBadge = badgesShownObj[guide.id];
|
|
22093
|
+
if (!existingBadge)
|
|
22094
|
+
return;
|
|
22095
|
+
var badgeStep = existingBadge.step();
|
|
22096
|
+
if (badgeStep && badgeStep.isShown && badgeStep.isShown())
|
|
22097
|
+
return;
|
|
22098
|
+
delete badgesShownObj[guide.id];
|
|
22099
|
+
if (_.isFunction(existingBadge.hide)) {
|
|
22100
|
+
existingBadge.hide();
|
|
22101
|
+
}
|
|
22102
|
+
}
|
|
22103
|
+
/*
|
|
22104
|
+
* Maintains the DOM presence of the badge for the specified guide
|
|
22105
|
+
* where in the badge is either added or removed depending on if
|
|
22106
|
+
* the appropiate DOM element is found.
|
|
22107
|
+
*
|
|
22108
|
+
* For this reason, when Badges are in the play the setTimeout to
|
|
22109
|
+
* keep showing the startGuides needs to also continue running.
|
|
22110
|
+
*
|
|
22111
|
+
* input: guide obj, and Map of ids to badges being shown currently
|
|
22112
|
+
*/
|
|
22113
|
+
var placeBadge = function (guide, badgesShownObj) {
|
|
22114
|
+
badgesShownObj = badgesShownObj || badgesShown;
|
|
22115
|
+
var badge = badgesShownObj[guide.id];
|
|
22116
|
+
var step = badge ? badge.step() : Badge.findStep(guide);
|
|
22117
|
+
if (!step)
|
|
22118
|
+
return;
|
|
22119
|
+
var target = badge ? badge.target() : getElementForTargeting(step);
|
|
22120
|
+
var isRcBadge = _.get(guide, 'attributes.resourceCenter');
|
|
22121
|
+
if (isRcBadge && !store.getters['frames/canShowRCBadge']()) {
|
|
22122
|
+
if (badge && _.isFunction(badge.hide)) {
|
|
22123
|
+
// If top frame comes in late and RC badge showing in inner frame bc of delay, hide it here
|
|
22124
|
+
delete badgesShownObj[guide.id];
|
|
22125
|
+
badge.hide();
|
|
22126
|
+
}
|
|
22127
|
+
return;
|
|
22128
|
+
}
|
|
22129
|
+
// Don't render the resource center badge if the resource center itself has nothing to show
|
|
22130
|
+
if (isRcBadge && !guide.hasResourceCenterContent)
|
|
22131
|
+
return;
|
|
22132
|
+
BuildingBlockResourceCenter.updateNotificationBubbles();
|
|
22133
|
+
var passesElementContainsRules = true;
|
|
22134
|
+
if (step.elementContainsRulesList) {
|
|
22135
|
+
passesElementContainsRules = target && doesElementMatchContainsRules(target, step.elementContainsRulesList);
|
|
22136
|
+
}
|
|
22137
|
+
const badgeCanShow = step.elementPathRule &&
|
|
22138
|
+
isElementVisible(target) &&
|
|
22139
|
+
SizzleProxy.matchesSelector(target, step.elementPathRule) &&
|
|
22140
|
+
passesElementContainsRules;
|
|
22141
|
+
if (badgeCanShow) {
|
|
22142
|
+
if (isBadgeTargetClaimedByOppositeTraining(guide, target)) {
|
|
22143
|
+
if (isDebuggingEnabled()) {
|
|
22144
|
+
Events.stepEligibility.trigger({
|
|
22145
|
+
stepId: step.id,
|
|
22146
|
+
guideId: guide.id,
|
|
22147
|
+
eligible: false,
|
|
22148
|
+
reason: 'adopt_badge_priority'
|
|
22149
|
+
});
|
|
22150
|
+
}
|
|
22151
|
+
hideBadgeForGuideInMap(guide, badgesShownObj);
|
|
22152
|
+
return;
|
|
22153
|
+
}
|
|
22154
|
+
claimBadgeTarget(guide, target);
|
|
22155
|
+
if (!badge) {
|
|
22156
|
+
badge = Badge.create(guide);
|
|
22157
|
+
}
|
|
22158
|
+
badge.show();
|
|
22159
|
+
badgesShownObj[guide.id] = badge;
|
|
22160
|
+
}
|
|
22161
|
+
else if (badge) {
|
|
22162
|
+
// if the step is being show for this badge then
|
|
22163
|
+
// don't remove it.
|
|
22164
|
+
if (!step.isShown()) {
|
|
22165
|
+
// 3. If the element is not found, remove it if it's currently shown.
|
|
22166
|
+
delete badgesShownObj[guide.id];
|
|
22167
|
+
badge.hide();
|
|
22168
|
+
}
|
|
22169
|
+
}
|
|
22170
|
+
if (isDebuggingEnabled()) {
|
|
22171
|
+
Events.stepEligibility.trigger({
|
|
22172
|
+
stepId: step.id,
|
|
22173
|
+
guideId: guide.id,
|
|
22174
|
+
eligible: badgeCanShow,
|
|
22175
|
+
reason: 'target_element_badge'
|
|
22176
|
+
});
|
|
22177
|
+
}
|
|
22178
|
+
BuildingBlockResourceCenter.repositionDoubleResourceCenterBadge();
|
|
22179
|
+
};
|
|
22180
|
+
var removeAllBadges = function () {
|
|
22181
|
+
_.map(badgesShown, removeBadge);
|
|
22182
|
+
setBadgesShown({});
|
|
22183
|
+
};
|
|
22184
|
+
var removeBadge = function (badge, guide) {
|
|
22185
|
+
if (!badge)
|
|
22186
|
+
return;
|
|
22187
|
+
if (_.isFunction(badge.hide)) {
|
|
22188
|
+
badge.hide();
|
|
22189
|
+
}
|
|
22190
|
+
if (_.isFunction(badge.shouldShow) && !badge.shouldShow() && guide) {
|
|
22191
|
+
delete badgesShown[guide.id];
|
|
22192
|
+
}
|
|
22193
|
+
};
|
|
22194
|
+
var removeBadgeForGuide = function (guideGroup) {
|
|
22195
|
+
var badge = badgesShown[guideGroup.id];
|
|
22196
|
+
if (badge) {
|
|
22197
|
+
removeBadge(badge, guideGroup);
|
|
22198
|
+
}
|
|
22199
|
+
};
|
|
22200
|
+
function _adjustBadgesForResize() {
|
|
22201
|
+
_.map(badgesShown, function (badge) {
|
|
22202
|
+
if (badge && badge.show) {
|
|
22203
|
+
badge.show();
|
|
22204
|
+
}
|
|
22205
|
+
});
|
|
22206
|
+
}
|
|
22207
|
+
var adjustBadgesForResize = _.throttle(_adjustBadgesForResize, 10);
|
|
22208
|
+
function pruneBadges(activeGuideIds) {
|
|
22209
|
+
if (!activeGuideIds || !activeGuideIds.length)
|
|
22210
|
+
return;
|
|
22211
|
+
const lookup = _.indexBy(activeGuideIds);
|
|
22212
|
+
_.each(badgesShown, function (badge, guideId) {
|
|
22213
|
+
if (!lookup[guideId] && badge) {
|
|
22214
|
+
badge.hide();
|
|
22215
|
+
delete badgesShown[guideId];
|
|
22216
|
+
}
|
|
22217
|
+
});
|
|
22218
|
+
}
|
|
22219
|
+
|
|
22220
|
+
/*
|
|
22221
|
+
* Guide Loop
|
|
22222
|
+
*
|
|
22223
|
+
* The function that gets called to process and handle the ever
|
|
22224
|
+
* changing landscape of a web app to best handle showing / hiding of
|
|
22225
|
+
* guides as needed.
|
|
22226
|
+
*/
|
|
22227
|
+
/*
|
|
22228
|
+
* Holds the timeout handle for the startGuides process.
|
|
22229
|
+
* This should work as a simple monitor on that process
|
|
22230
|
+
* to prevent it from being run too often.
|
|
22231
|
+
*/
|
|
22232
|
+
var guidesProcessingThreadHandle = null;
|
|
22233
|
+
var DEFAULT_TIMER_LENGTH = 500;
|
|
22234
|
+
var waitThenLoop = function (delayAmount) {
|
|
22235
|
+
delayAmount = delayAmount || DEFAULT_TIMER_LENGTH;
|
|
22236
|
+
guidesProcessingThreadHandle = _.delay(function () {
|
|
22237
|
+
guidesProcessingThreadHandle = null;
|
|
22238
|
+
startGuides();
|
|
22239
|
+
}, delayAmount);
|
|
22240
|
+
pendo$1.guidesProcessingThreadHandle = guidesProcessingThreadHandle;
|
|
22241
|
+
};
|
|
22242
|
+
var clearLoopTimer = function () {
|
|
22243
|
+
if (guidesProcessingThreadHandle) {
|
|
22244
|
+
clearTimeout(guidesProcessingThreadHandle);
|
|
22245
|
+
guidesProcessingThreadHandle = null;
|
|
22246
|
+
pendo$1.guidesProcessingThreadHandle = guidesProcessingThreadHandle;
|
|
22247
|
+
}
|
|
22248
|
+
store.dispatch('guideUpdate/stopScheduledUpdate');
|
|
22249
|
+
};
|
|
22250
|
+
/**
|
|
22251
|
+
* Clears any showing guides and prevents any loaded guides from rendering.
|
|
22252
|
+
* Loaded guides remain in memory, so calling startGuides will work just fine
|
|
22253
|
+
* after a stopGuides call.
|
|
22254
|
+
*
|
|
22255
|
+
* @access public
|
|
22256
|
+
* @category Guides
|
|
22257
|
+
* @example
|
|
22258
|
+
* pendo.stopGuides()
|
|
22259
|
+
*/
|
|
22260
|
+
var stopGuides = function () {
|
|
22261
|
+
hideGuides();
|
|
22262
|
+
updateRunner.cancel();
|
|
22263
|
+
// Remove other stuff.
|
|
22264
|
+
for (var i = 0; i < activeElements.length; i++) {
|
|
22265
|
+
var elem = activeElements[i];
|
|
22266
|
+
elem.parentNode.removeChild(elem);
|
|
22267
|
+
}
|
|
22268
|
+
activeElements.length = 0;
|
|
22269
|
+
store.dispatch('guideUpdate/stopObserver');
|
|
22270
|
+
Events.guideLoopStopped.trigger();
|
|
22271
|
+
};
|
|
22272
|
+
/**
|
|
22273
|
+
* Starts the process that loops over all currently loaded guides and
|
|
22274
|
+
* determines what to show. Checks multi-step guide continuation,
|
|
22275
|
+
* auto-display guides, launcher guides, and badges.
|
|
22276
|
+
*
|
|
22277
|
+
* @access public
|
|
22278
|
+
* @category Guides
|
|
22279
|
+
* @example
|
|
22280
|
+
* pendo.startGuides()
|
|
22281
|
+
*/
|
|
22282
|
+
var startGuides = function () {
|
|
22283
|
+
clearLoopTimer();
|
|
22284
|
+
if (isGuideRequestPending()) {
|
|
22285
|
+
log.info('guides are loading.', { contexts: ['guides', 'loading'] });
|
|
22286
|
+
return;
|
|
22287
|
+
}
|
|
22288
|
+
if (areGuidesDisabled()) {
|
|
22289
|
+
log.info('guides are disabled.', { contexts: ['guides', 'disabled'] });
|
|
22290
|
+
return;
|
|
22291
|
+
}
|
|
22292
|
+
if (areGuidesDelayed()) {
|
|
22293
|
+
log.info('guides are delayed.', { contexts: ['guides', 'delayed'] });
|
|
22294
|
+
return;
|
|
22295
|
+
}
|
|
22296
|
+
if (store.getters['guideState/tabLostFocus']())
|
|
22297
|
+
return;
|
|
22298
|
+
try {
|
|
22299
|
+
const activeGuides = getActiveGuides();
|
|
22300
|
+
if (!activeGuides || activeGuides.length === 0)
|
|
22301
|
+
return;
|
|
22302
|
+
store.dispatch('guideUpdate/startObserverIfNeeded');
|
|
22303
|
+
if (updateRunner.isComplete()) {
|
|
22304
|
+
updateRunner.markDirty();
|
|
22305
|
+
}
|
|
22306
|
+
if (leaderExists()) {
|
|
22307
|
+
updateRunner.process();
|
|
22308
|
+
}
|
|
22309
|
+
}
|
|
22310
|
+
catch (e) {
|
|
22311
|
+
updateRunner.cancel();
|
|
22312
|
+
log.critical('ERROR in guide-loop', { error: e });
|
|
22313
|
+
}
|
|
22314
|
+
finally {
|
|
22315
|
+
if (!updateRunner.paused) {
|
|
22316
|
+
continueOrCompleteUpdate();
|
|
22317
|
+
}
|
|
22318
|
+
}
|
|
22319
|
+
};
|
|
22320
|
+
function continueOrCompleteUpdate() {
|
|
22321
|
+
if (!updateRunner.isComplete()) {
|
|
22322
|
+
waitThenLoop(50);
|
|
22323
|
+
}
|
|
22324
|
+
else {
|
|
22325
|
+
if (!store.state.guideUpdate.useObserver || store.state.guideUpdate.needsUpdate) {
|
|
22326
|
+
waitThenLoop();
|
|
22327
|
+
}
|
|
22328
|
+
Events.guideUpdateComplete.trigger();
|
|
22329
|
+
}
|
|
22330
|
+
}
|
|
22331
|
+
/*
|
|
22332
|
+
* If delayGuides is true, removes the delay and starts guides.
|
|
22333
|
+
* Otherwise, it is the same as calling startGuides.
|
|
22334
|
+
*/
|
|
22335
|
+
var manuallyStartGuides = function () {
|
|
22336
|
+
setGuidesDelayed(false);
|
|
22337
|
+
startGuides();
|
|
22338
|
+
};
|
|
22339
|
+
var manuallyStopGuides = function () {
|
|
22340
|
+
if (!areGuidesDisabled()) {
|
|
22341
|
+
setGuidesDelayed(true);
|
|
22342
|
+
}
|
|
22343
|
+
resetPendoUI();
|
|
22344
|
+
};
|
|
22345
|
+
// a phase that only has one unit of work per update
|
|
22346
|
+
class SimplePhase {
|
|
22347
|
+
constructor(name, processFn) {
|
|
22348
|
+
this.dirty = false;
|
|
22349
|
+
this.name = name;
|
|
22350
|
+
this.processFn = _.isFunction(processFn) ? processFn : _.noop;
|
|
22351
|
+
}
|
|
22352
|
+
markDirty() {
|
|
22353
|
+
this.dirty = true;
|
|
22354
|
+
}
|
|
22355
|
+
process() {
|
|
22356
|
+
this.processFn();
|
|
22357
|
+
this.dirty = false;
|
|
22358
|
+
}
|
|
22359
|
+
cancel() {
|
|
22360
|
+
this.dirty = false;
|
|
22361
|
+
}
|
|
22362
|
+
isComplete() {
|
|
22363
|
+
return !this.dirty;
|
|
22364
|
+
}
|
|
22365
|
+
}
|
|
22366
|
+
/**
|
|
22367
|
+
* Runs on a timer whenever a guide is displayed.
|
|
22368
|
+
*
|
|
22369
|
+
* @access private
|
|
22370
|
+
* @param {Guide} guide The currently visible guide
|
|
22371
|
+
* @param {GuideStep} step The currently visible step
|
|
22372
|
+
*/
|
|
22373
|
+
function guideShowingProc() {
|
|
22374
|
+
const shownGuides = _.compact(_.map(_.filter(frameLocalGuides(getActiveGuides()), (guide) => guide.isShown()), (guide) => {
|
|
22375
|
+
if (BuildingBlockResourceCenter.isModule(guide)) {
|
|
22376
|
+
return BuildingBlockResourceCenter.getResourceCenter();
|
|
22377
|
+
}
|
|
22378
|
+
else {
|
|
22379
|
+
return guide;
|
|
22380
|
+
}
|
|
22381
|
+
}));
|
|
22382
|
+
_.each(shownGuides, function (guide) {
|
|
22383
|
+
_.each(guide.steps, function (step) {
|
|
22384
|
+
if (step.isShown()) {
|
|
22385
|
+
stepShowingProc(guide, step);
|
|
22386
|
+
}
|
|
22387
|
+
});
|
|
22388
|
+
guide.checkForHiddenGroupSteps();
|
|
22389
|
+
});
|
|
22390
|
+
}
|
|
22391
|
+
function stepShowingProc(guide, step) {
|
|
22392
|
+
if (step.isLocked()) {
|
|
22393
|
+
return;
|
|
22394
|
+
}
|
|
22395
|
+
if (store.getters['frames/isShownInAnotherFrame']()(step)) {
|
|
22396
|
+
return;
|
|
22397
|
+
}
|
|
22398
|
+
var element = step.element;
|
|
22399
|
+
var targetElement = step.targetElement;
|
|
22400
|
+
var guideElement = step.guideElement;
|
|
22401
|
+
var guideElm = dom('.' + GUIDE_CSS_NAME);
|
|
22402
|
+
// As part of the guides accessibility work, we need to make sure that all
|
|
22403
|
+
// guide content remain visible and/or accessible during text-only (font) zoom.
|
|
22404
|
+
// Since text zoom does NOT trigger our existing resize listener, the
|
|
22405
|
+
// alternative is to track text zoom via font-size changes.
|
|
22406
|
+
var guideContainer = guideElement.find('#pendo-guide-container')[0];
|
|
22407
|
+
var guideHasTextContent = guideContainer && guideContainer.textContent;
|
|
22408
|
+
if (guideHasTextContent) {
|
|
22409
|
+
var currentBrowserFontSize = getComputedStyle_safe(guideContainer).fontSize;
|
|
22410
|
+
var hasBrowserFontSizeChanged = step.attributes.currentTextZoomFontSize !== currentBrowserFontSize;
|
|
22411
|
+
if (hasBrowserFontSizeChanged) {
|
|
22412
|
+
if (step.attributes.currentTextZoomFontSize) {
|
|
22413
|
+
step.redraw();
|
|
22414
|
+
}
|
|
22415
|
+
step.attributes.currentTextZoomFontSize = currentBrowserFontSize;
|
|
22416
|
+
}
|
|
22417
|
+
}
|
|
22418
|
+
syncColorMode(step, true);
|
|
22419
|
+
if (step.elementPathRule && targetElement && !SizzleProxy.matchesSelector(targetElement, step.elementPathRule)) {
|
|
22420
|
+
step.hide();
|
|
22421
|
+
return;
|
|
22422
|
+
}
|
|
22423
|
+
const embedSelector = _.get(guide, 'attributes.embedConfig.selector');
|
|
22424
|
+
if (embedSelector && !dom(embedSelector).length) {
|
|
22425
|
+
step.hide();
|
|
22426
|
+
return;
|
|
22427
|
+
}
|
|
22428
|
+
var elementIsStillValidTarget = isElementVisible(element);
|
|
22429
|
+
var elementMatchesContainsRules = true;
|
|
22430
|
+
// targetElement is specifically the elementPathRule dom node
|
|
22431
|
+
// element is either the element path rule OR the badge element
|
|
22432
|
+
// we can rely on the isElementVisible check as a shortcut for having to call isElementVisible twice
|
|
22433
|
+
if (targetElement && elementIsStillValidTarget && step.elementContainsRulesList) {
|
|
22434
|
+
// Only bother trying to check element contains rules if the element is still visible
|
|
22435
|
+
elementMatchesContainsRules = doesElementMatchContainsRules(targetElement, step.elementContainsRulesList);
|
|
22436
|
+
elementIsStillValidTarget = elementMatchesContainsRules;
|
|
22437
|
+
}
|
|
22438
|
+
if (element && (elementIsStillValidTarget || guideElm.hasClass('mouseover'))) {
|
|
22439
|
+
step.checkPlacement();
|
|
22440
|
+
return;
|
|
22441
|
+
}
|
|
22442
|
+
var isTooltip = (step.type === 'tooltip' || step.attributes.calculatedType === 'tooltip');
|
|
22443
|
+
if (isTooltip && wouldBeVisibleAfterAutoScroll(element) && elementMatchesContainsRules) {
|
|
22444
|
+
// the element was scrolled out of view, the guide should be invisible for now, but still "active"
|
|
22445
|
+
return;
|
|
22446
|
+
}
|
|
22447
|
+
step.hide();
|
|
22448
|
+
}
|
|
22449
|
+
class BadgePhase {
|
|
22450
|
+
constructor() {
|
|
22451
|
+
this.name = 'badge';
|
|
22452
|
+
this.queue = [];
|
|
22453
|
+
}
|
|
22454
|
+
markDirty() {
|
|
22455
|
+
var badgeGuides = _.filter(frameLocalGuides(getActiveGuides()), isBadge);
|
|
22456
|
+
var hasAdoptGuides = _.find(badgeGuides, function (guide) { return guide.isTraining; });
|
|
22457
|
+
if (hasAdoptGuides) {
|
|
22458
|
+
badgeGuides = prioritizeAdoptOrCoreGuides(badgeGuides, ConfigReader.get('adoptPrioritizeAdoptGuides'));
|
|
22459
|
+
}
|
|
22460
|
+
resetBadgeTargetClaims();
|
|
22461
|
+
this.queue.push.apply(this.queue, badgeGuides);
|
|
22462
|
+
}
|
|
22463
|
+
process(monitor) {
|
|
22464
|
+
var badge = this.queue.shift();
|
|
22465
|
+
monitor.saveContext(badge);
|
|
22466
|
+
if (badge) {
|
|
22467
|
+
badge.placeBadge();
|
|
22468
|
+
}
|
|
22469
|
+
}
|
|
22470
|
+
handleProcessTimeExceeded(monitor) {
|
|
22471
|
+
const badge = monitor.getContext();
|
|
22472
|
+
log.warn('Badge (' + badge.id + ') timeout exceeded');
|
|
22473
|
+
}
|
|
22474
|
+
cancel() {
|
|
22475
|
+
this.queue.length = 0;
|
|
22476
|
+
}
|
|
22477
|
+
isComplete() {
|
|
22478
|
+
return !this.queue.length;
|
|
22479
|
+
}
|
|
22480
|
+
}
|
|
22481
|
+
function resourceCenterShowingProc(resourceCenter, resourceCenterBadgeProc) {
|
|
22482
|
+
var reEvaluateModuleContent = false;
|
|
22483
|
+
// Find eligible modules
|
|
22484
|
+
var controlGuidesInAllModules = [];
|
|
22485
|
+
var eligibleModules = _.filter(resourceCenter.modules, function (module) {
|
|
22486
|
+
if (!module)
|
|
22487
|
+
return false;
|
|
22488
|
+
// Iframe modules can not dynamically show/hide.
|
|
22489
|
+
if (BuildingBlockResourceCenter.isIframeModule(module))
|
|
22490
|
+
return true;
|
|
22491
|
+
// Sandbox modules can not dynamically show/hide.
|
|
22492
|
+
if (BuildingBlockResourceCenter.isSandboxModule(module))
|
|
22493
|
+
return true;
|
|
22494
|
+
if (BuildingBlockResourceCenter.isIntegrationModule(module)) {
|
|
22495
|
+
if (!BuildingBlockResourceCenter.integrationModuleHasContent(module)) {
|
|
22496
|
+
module.hasResourceCenterContent = false;
|
|
22497
|
+
return false;
|
|
22498
|
+
}
|
|
22499
|
+
else {
|
|
22500
|
+
return true;
|
|
22501
|
+
}
|
|
22502
|
+
}
|
|
22503
|
+
var eligibleGuidesInModule = _.filter(module.guidesInModule, function (guide) {
|
|
22504
|
+
if (!guide.shouldBeAddedToResourceCenter()) {
|
|
22505
|
+
guide.ineligibleForRC = true;
|
|
22506
|
+
if (guide.control) {
|
|
22507
|
+
controlGuidesInAllModules.push(guide);
|
|
22508
|
+
}
|
|
22509
|
+
return false;
|
|
22510
|
+
}
|
|
22511
|
+
guide.ineligibleForRC = false;
|
|
22512
|
+
return true;
|
|
22513
|
+
});
|
|
22514
|
+
var isEligible = eligibleGuidesInModule.length;
|
|
22515
|
+
module.hasResourceCenterContent = !!isEligible;
|
|
22516
|
+
// We only need to hash the active module, no other module has content rendered
|
|
22517
|
+
if (resourceCenter.activeModule && module.id === resourceCenter.activeModule.id) {
|
|
22518
|
+
var moduleContentHash = crc32(_.map(eligibleGuidesInModule, function (guide) {
|
|
22519
|
+
// don't change content hash when announcements update their seen state, as that will cause
|
|
22520
|
+
// the RC to re-render (and lose scroll position)
|
|
22521
|
+
var isAnnouncementOrWhatsNew = guide.isAnnouncement() || guide.isWhatsNew();
|
|
22522
|
+
var seenState = isAnnouncementOrWhatsNew ? null : _.pluck(guide.steps, 'seenState');
|
|
22523
|
+
return {
|
|
22524
|
+
id: guide.id,
|
|
22525
|
+
seenState
|
|
22526
|
+
};
|
|
22527
|
+
}));
|
|
22528
|
+
if (module.eligibleGuidesInModuleHash && moduleContentHash !== module.eligibleGuidesInModuleHash)
|
|
22529
|
+
reEvaluateModuleContent = true;
|
|
22530
|
+
module.eligibleGuidesInModuleHash = moduleContentHash;
|
|
22531
|
+
}
|
|
22532
|
+
return isEligible;
|
|
22533
|
+
});
|
|
22534
|
+
// If we are rendering the home view and its modules change re-render it
|
|
22535
|
+
var reEvaluateHomeViewContent = false;
|
|
22536
|
+
if (!resourceCenter.activeModule) {
|
|
22537
|
+
var eligibleModulesHash = crc32(_.map(eligibleModules, function (module) {
|
|
22538
|
+
return {
|
|
22539
|
+
id: module.id
|
|
22540
|
+
};
|
|
22541
|
+
}));
|
|
22542
|
+
if (resourceCenter.eligibleModulesHash && resourceCenter.eligibleModulesHash !== eligibleModulesHash) {
|
|
22543
|
+
reEvaluateHomeViewContent = true;
|
|
22544
|
+
BuildingBlockResourceCenter.replaceResourceCenterContent(resourceCenter.id, undefined, true);
|
|
22545
|
+
}
|
|
22546
|
+
resourceCenter.eligibleModulesHash = eligibleModulesHash;
|
|
22547
|
+
}
|
|
22548
|
+
resourceCenterBadgeProc(resourceCenter, eligibleModules, controlGuidesInAllModules);
|
|
22549
|
+
// Skip directly to the one eligible module
|
|
22550
|
+
if (eligibleModules.length === 1) {
|
|
22551
|
+
resourceCenter.skipResourceCenterHomeView = true;
|
|
22552
|
+
resourceCenter.moduleIdToReplaceHomeViewWith = eligibleModules[0].id;
|
|
22553
|
+
var noActiveModule = !resourceCenter.activeModule && reEvaluateHomeViewContent;
|
|
22554
|
+
var differentActiveModule = resourceCenter.activeModule && resourceCenter.activeModule.id !== eligibleModules[0].id;
|
|
22555
|
+
// If the one module is different or content needs to be reevaluated, reload
|
|
22556
|
+
// or if we are on the home view and the modules on the home view have changed
|
|
22557
|
+
if (noActiveModule || differentActiveModule || reEvaluateModuleContent) {
|
|
22558
|
+
BuildingBlockResourceCenter.replaceResourceCenterContent(resourceCenter.moduleIdToReplaceHomeViewWith, undefined, true);
|
|
22559
|
+
}
|
|
22560
|
+
}
|
|
22561
|
+
if (eligibleModules) {
|
|
22562
|
+
// find the in-progress native integration module and set continueToNativeModule on both the
|
|
22563
|
+
// RC container and the module since both will try to render
|
|
22564
|
+
var continuedModule = _.find(eligibleModules, function (module) {
|
|
22565
|
+
return module.isContinuation(getLastGuideStepSeen());
|
|
22566
|
+
});
|
|
22567
|
+
var shouldNotResume = _.get(continuedModule, 'attributes.doNotResume');
|
|
22568
|
+
var integrationProvider = _.get(continuedModule, 'attributes.resourceCenter.integrationProvider');
|
|
22569
|
+
var isNativeModule = BuildingBlockResourceCenter.isSupportedNativeIntegration(integrationProvider);
|
|
22570
|
+
if (isNativeModule && !shouldNotResume && resourceCenter) {
|
|
22571
|
+
resourceCenter.continueToNativeModule = true;
|
|
22572
|
+
continuedModule.continueToNativeModule = true;
|
|
22573
|
+
}
|
|
22574
|
+
}
|
|
22575
|
+
// Ensure the active module is still eligible and re-evaluate its content if need be.
|
|
22576
|
+
// Go to Home View if active module is no longer eligible
|
|
22577
|
+
var activeModuleisEligible;
|
|
22578
|
+
if (resourceCenter.activeModule) {
|
|
22579
|
+
activeModuleisEligible = _.find(eligibleModules, function (module) {
|
|
22580
|
+
return module.id === resourceCenter.activeModule.id;
|
|
22581
|
+
});
|
|
22582
|
+
if (activeModuleisEligible && reEvaluateModuleContent) {
|
|
22583
|
+
BuildingBlockResourceCenter.replaceResourceCenterContent(resourceCenter.activeModule.id, undefined, true);
|
|
22584
|
+
}
|
|
22585
|
+
if (!activeModuleisEligible) {
|
|
22586
|
+
BuildingBlockResourceCenter.replaceResourceCenterContent(resourceCenter.id);
|
|
22587
|
+
}
|
|
22588
|
+
}
|
|
22589
|
+
BuildingBlockResourceCenter.repositionResourceCenter();
|
|
22590
|
+
}
|
|
22591
|
+
function resourceCenterNotShowingProc(resourceCenter, resourceCenterBadgeProc) {
|
|
22592
|
+
var eligibleModules = [];
|
|
22593
|
+
var controlGuidesInAllModules = [];
|
|
22594
|
+
var staticModule = _.find(resourceCenter.modules, function (module) {
|
|
22595
|
+
if (BuildingBlockResourceCenter.isIframeModule(module))
|
|
22596
|
+
return true;
|
|
22597
|
+
if (BuildingBlockResourceCenter.isSandboxModule(module))
|
|
22598
|
+
return true;
|
|
22599
|
+
if (BuildingBlockResourceCenter.isIntegrationModule(module)) {
|
|
22600
|
+
if (!BuildingBlockResourceCenter.integrationModuleHasContent(module)) {
|
|
22601
|
+
module.hasResourceCenterContent = false;
|
|
22602
|
+
return false;
|
|
22603
|
+
}
|
|
22604
|
+
else {
|
|
22605
|
+
return true;
|
|
22606
|
+
}
|
|
22607
|
+
}
|
|
22608
|
+
return false;
|
|
22609
|
+
});
|
|
22610
|
+
if (staticModule) {
|
|
22611
|
+
eligibleModules.push(staticModule);
|
|
22612
|
+
}
|
|
22613
|
+
var allGuidesInModules = _.flatten(_.pluck(resourceCenter.modules, 'guidesInModule'));
|
|
22614
|
+
controlGuidesInAllModules = _.filter(_.compact(allGuidesInModules), function (guide) {
|
|
22615
|
+
return guide.control;
|
|
22616
|
+
});
|
|
22617
|
+
if (!eligibleModules.length) {
|
|
22618
|
+
var moduleLookup = _.reduce(resourceCenter.modules, function (lookup, module) {
|
|
22619
|
+
_.each(module.guidesInModule, function (guide) {
|
|
22620
|
+
lookup[guide.id] = module;
|
|
22621
|
+
});
|
|
22622
|
+
return lookup;
|
|
22623
|
+
}, {});
|
|
22624
|
+
var guidesWithSteps = _.filter(allGuidesInModules, function (guide) {
|
|
22625
|
+
return guide && guide.steps && guide.steps.length;
|
|
22626
|
+
});
|
|
22627
|
+
// Sort guides so that untargeted guides are first
|
|
22628
|
+
var sortedGuides = _.sortBy(guidesWithSteps, function (guide) {
|
|
22629
|
+
return guide.steps[0].elementPathRule ? 1 : 0;
|
|
22630
|
+
});
|
|
22631
|
+
var firstEligibleGuide = _.find(sortedGuides, function (guide) {
|
|
22632
|
+
return guide.shouldBeAddedToResourceCenter();
|
|
22708
22633
|
});
|
|
22634
|
+
var firstEligibleModule = moduleLookup[firstEligibleGuide && firstEligibleGuide.id];
|
|
22635
|
+
if (firstEligibleModule) {
|
|
22636
|
+
eligibleModules.push(firstEligibleModule);
|
|
22637
|
+
}
|
|
22709
22638
|
}
|
|
22710
|
-
|
|
22639
|
+
resourceCenterBadgeProc(resourceCenter, eligibleModules, controlGuidesInAllModules);
|
|
22711
22640
|
}
|
|
22712
|
-
|
|
22713
|
-
|
|
22714
|
-
|
|
22715
|
-
|
|
22716
|
-
|
|
22717
|
-
|
|
22718
|
-
|
|
22719
|
-
|
|
22720
|
-
|
|
22721
|
-
|
|
22722
|
-
|
|
22723
|
-
|
|
22724
|
-
|
|
22725
|
-
|
|
22726
|
-
|
|
22727
|
-
|
|
22728
|
-
|
|
22729
|
-
|
|
22730
|
-
|
|
22731
|
-
|
|
22732
|
-
|
|
22733
|
-
|
|
22734
|
-
|
|
22641
|
+
function resourceCenterBadgeProc(resourceCenter, eligibleModules, controlGuidesInAllModules) {
|
|
22642
|
+
var RCBadge = badgesShown[resourceCenter.id];
|
|
22643
|
+
// If we get no modules back, hide the RC and RC Badge, clear active module
|
|
22644
|
+
if (!eligibleModules || !eligibleModules.length) {
|
|
22645
|
+
var isBadgeActivated = resourceCenter.launchMethod.indexOf('badge') !== -1;
|
|
22646
|
+
var isElementActivated = resourceCenter.launchMethod.indexOf('dom') !== -1;
|
|
22647
|
+
resourceCenter.hasResourceCenterContent = false;
|
|
22648
|
+
// If the RC is badge activated, hide it from the dom completely. Otherwise, leave it alone
|
|
22649
|
+
// This is important for element-activated RC, which should still display with an empty state
|
|
22650
|
+
// even if it has no content
|
|
22651
|
+
if (isBadgeActivated) {
|
|
22652
|
+
if (resourceCenter.isShown())
|
|
22653
|
+
resourceCenter.hide();
|
|
22654
|
+
if (RCBadge) {
|
|
22655
|
+
BuildingBlockResourceCenter.hideRCBadge(RCBadge, resourceCenter);
|
|
22656
|
+
}
|
|
22657
|
+
// If all modules are not eligible because of control group guides
|
|
22658
|
+
// Send guideNotSeen events where appropriate
|
|
22659
|
+
_.each(controlGuidesInAllModules, function (guide) {
|
|
22660
|
+
if (!guide.hasGuideBeenControlled()) {
|
|
22661
|
+
guide.steps[0].onControlGuide('control');
|
|
22662
|
+
}
|
|
22663
|
+
});
|
|
22664
|
+
resourceCenter.badgeHidden = true;
|
|
22665
|
+
delete resourceCenter.activeModule;
|
|
22666
|
+
}
|
|
22667
|
+
else if (isElementActivated) {
|
|
22668
|
+
resourceCenter.showEmptyState = true;
|
|
22669
|
+
resourceCenter.controlGuidesInAllModules = controlGuidesInAllModules;
|
|
22735
22670
|
}
|
|
22736
|
-
return;
|
|
22737
22671
|
}
|
|
22738
|
-
//
|
|
22739
|
-
if (
|
|
22672
|
+
// Show the badge if it was hidden but now has content
|
|
22673
|
+
if (resourceCenter.badgeHidden && resourceCenter.hasResourceCenterContent && !resourceCenter.isNativeModuleActive) {
|
|
22674
|
+
if (!RCBadge) {
|
|
22675
|
+
if (_.isFunction(resourceCenter.placeBadge))
|
|
22676
|
+
resourceCenter.placeBadge();
|
|
22677
|
+
RCBadge = badgesShown[resourceCenter.id];
|
|
22678
|
+
}
|
|
22679
|
+
if (RCBadge)
|
|
22680
|
+
RCBadge.show();
|
|
22681
|
+
resourceCenter.badgeHidden = false;
|
|
22682
|
+
}
|
|
22683
|
+
}
|
|
22684
|
+
function resourceCenterProc(resourceCenter) {
|
|
22685
|
+
if (!resourceCenter || !resourceCenter.launchMethod)
|
|
22740
22686
|
return;
|
|
22741
|
-
|
|
22742
|
-
|
|
22743
|
-
|
|
22744
|
-
|
|
22687
|
+
// Reset state to start each loop
|
|
22688
|
+
resourceCenter.skipResourceCenterHomeView = false;
|
|
22689
|
+
resourceCenter.hasResourceCenterContent = true;
|
|
22690
|
+
delete resourceCenter.moduleIdToReplaceHomeViewWith;
|
|
22691
|
+
var activeModule = resourceCenter.activeModule;
|
|
22692
|
+
var activeIntegrationProvider = _.get(activeModule, 'attributes.resourceCenter.integrationProvider');
|
|
22693
|
+
resourceCenter.isNativeModuleActive = activeModule && BuildingBlockResourceCenter.isSupportedNativeIntegration(activeIntegrationProvider);
|
|
22694
|
+
if (resourceCenter.isShown() || !store.getters['frames/isLeader']()) {
|
|
22695
|
+
resourceCenterShowingProc(resourceCenter, resourceCenterBadgeProc);
|
|
22745
22696
|
}
|
|
22746
|
-
|
|
22747
|
-
|
|
22748
|
-
|
|
22749
|
-
|
|
22750
|
-
if (
|
|
22751
|
-
if (
|
|
22752
|
-
|
|
22697
|
+
else {
|
|
22698
|
+
resourceCenterNotShowingProc(resourceCenter, resourceCenterBadgeProc);
|
|
22699
|
+
}
|
|
22700
|
+
// Hide the badge if we're displaying a native integration module
|
|
22701
|
+
if (resourceCenter.isNativeModuleActive) {
|
|
22702
|
+
if (resourceCenter.isShown())
|
|
22703
|
+
resourceCenter.hide();
|
|
22704
|
+
var RCBadge = badgesShown[resourceCenter.id];
|
|
22705
|
+
if (RCBadge) {
|
|
22706
|
+
BuildingBlockResourceCenter.hideRCBadge(RCBadge, resourceCenter);
|
|
22753
22707
|
}
|
|
22754
|
-
badge.show();
|
|
22755
|
-
badgesShownObj[guide.id] = badge;
|
|
22756
22708
|
}
|
|
22757
|
-
|
|
22758
|
-
|
|
22759
|
-
|
|
22760
|
-
|
|
22761
|
-
|
|
22762
|
-
|
|
22763
|
-
|
|
22709
|
+
// Place Element Activated Notification Badge
|
|
22710
|
+
if (resourceCenter.launchMethod === 'dom' && resourceCenter.canShowOnDevice()) {
|
|
22711
|
+
BuildingBlockResourceCenter.updateNotificationBubbles();
|
|
22712
|
+
BuildingBlockResourceCenter.adjustNotificationBubbleForResize();
|
|
22713
|
+
}
|
|
22714
|
+
}
|
|
22715
|
+
class PhaseProcessTimeMonitor {
|
|
22716
|
+
constructor(timeLimit = 50) {
|
|
22717
|
+
this.startTime = -1;
|
|
22718
|
+
this.timeLimit = timeLimit;
|
|
22719
|
+
this.context = null;
|
|
22720
|
+
}
|
|
22721
|
+
start() {
|
|
22722
|
+
this.context = null;
|
|
22723
|
+
this.startTime = getNow();
|
|
22724
|
+
}
|
|
22725
|
+
isTimeExceeded() {
|
|
22726
|
+
const currTime = getNow();
|
|
22727
|
+
return (currTime - this.startTime) > this.timeLimit;
|
|
22728
|
+
}
|
|
22729
|
+
saveContext(context) {
|
|
22730
|
+
this.context = context;
|
|
22731
|
+
}
|
|
22732
|
+
getContext() {
|
|
22733
|
+
return this.context;
|
|
22734
|
+
}
|
|
22735
|
+
}
|
|
22736
|
+
class UpdateRunner {
|
|
22737
|
+
constructor(phases) {
|
|
22738
|
+
this.phases = phases;
|
|
22739
|
+
this.queue = [];
|
|
22740
|
+
this.paused = false;
|
|
22741
|
+
}
|
|
22742
|
+
markDirty() {
|
|
22743
|
+
for (let phase of this.phases) {
|
|
22744
|
+
phase.markDirty();
|
|
22764
22745
|
}
|
|
22746
|
+
this.queue = this.phases.slice();
|
|
22765
22747
|
}
|
|
22766
|
-
|
|
22767
|
-
|
|
22768
|
-
|
|
22769
|
-
|
|
22770
|
-
|
|
22771
|
-
|
|
22748
|
+
runGuidePhase() {
|
|
22749
|
+
const currentPhase = this.queue[0];
|
|
22750
|
+
var promise;
|
|
22751
|
+
const monitor = new PhaseProcessTimeMonitor(50);
|
|
22752
|
+
while (!this.timedOut() && !currentPhase.isComplete() && !promise) {
|
|
22753
|
+
monitor.start();
|
|
22754
|
+
promise = currentPhase.process(monitor);
|
|
22755
|
+
if (!promise && monitor.isTimeExceeded()) {
|
|
22756
|
+
performanceMonitor.count(GUIDE_LOOP_TIMEOUT);
|
|
22757
|
+
if (currentPhase.handleProcessTimeExceeded) {
|
|
22758
|
+
currentPhase.handleProcessTimeExceeded(monitor);
|
|
22759
|
+
}
|
|
22760
|
+
this.ejectGuide(monitor.getContext());
|
|
22761
|
+
}
|
|
22762
|
+
}
|
|
22763
|
+
if (isPromise(promise)) {
|
|
22764
|
+
var id = this.updateId;
|
|
22765
|
+
var resume = _.bind(function () {
|
|
22766
|
+
if (this.updateId === id) {
|
|
22767
|
+
this.paused = false;
|
|
22768
|
+
startGuides();
|
|
22769
|
+
}
|
|
22770
|
+
}, this);
|
|
22771
|
+
this.paused = true;
|
|
22772
|
+
promise.then(resume).catch(resume);
|
|
22773
|
+
}
|
|
22774
|
+
else if (currentPhase.isComplete()) {
|
|
22775
|
+
this.queue.shift();
|
|
22776
|
+
}
|
|
22777
|
+
}
|
|
22778
|
+
addPhase(phaseFn, phaseName) {
|
|
22779
|
+
phaseName = phaseName || phaseFn.name || this.phases.length;
|
|
22780
|
+
if (_.isFunction(phaseFn)) {
|
|
22781
|
+
phaseFn = new SimplePhase(phaseName, phaseFn);
|
|
22782
|
+
}
|
|
22783
|
+
this.phases.push(phaseFn);
|
|
22784
|
+
}
|
|
22785
|
+
removePhase(phaseName) {
|
|
22786
|
+
var i = _.findIndex(this.phases, function (phase) {
|
|
22787
|
+
return phase.name === phaseName;
|
|
22772
22788
|
});
|
|
22789
|
+
if (i >= 0) {
|
|
22790
|
+
this.phases.splice(i, 1);
|
|
22791
|
+
}
|
|
22773
22792
|
}
|
|
22774
|
-
|
|
22775
|
-
|
|
22776
|
-
|
|
22777
|
-
|
|
22778
|
-
|
|
22779
|
-
};
|
|
22780
|
-
var removeBadge = function (badge, guide) {
|
|
22781
|
-
if (!badge)
|
|
22782
|
-
return;
|
|
22783
|
-
if (_.isFunction(badge.hide)) {
|
|
22784
|
-
badge.hide();
|
|
22793
|
+
timedOut() {
|
|
22794
|
+
if (!this.iterationStartTime)
|
|
22795
|
+
return false;
|
|
22796
|
+
var elapsed = getNow() - this.iterationStartTime;
|
|
22797
|
+
return elapsed > store.state.guideUpdate.iterationTimeLimit;
|
|
22785
22798
|
}
|
|
22786
|
-
|
|
22787
|
-
|
|
22799
|
+
ejectGuide(guide) {
|
|
22800
|
+
if (this.ejectOnTimeout === undefined) {
|
|
22801
|
+
this.ejectOnTimeout = ConfigReader.get('guides.ejectOnTimeout');
|
|
22802
|
+
}
|
|
22803
|
+
if (this.ejectOnTimeout && guide && !guide.isShown()) {
|
|
22804
|
+
Tombstone.addGuide(guide);
|
|
22805
|
+
}
|
|
22788
22806
|
}
|
|
22789
|
-
|
|
22790
|
-
var
|
|
22791
|
-
|
|
22792
|
-
|
|
22793
|
-
|
|
22807
|
+
process() {
|
|
22808
|
+
var now = getNow();
|
|
22809
|
+
if (!this.updateId) {
|
|
22810
|
+
this.updateId = now;
|
|
22811
|
+
this.updateTotalTime = 0;
|
|
22812
|
+
store.commit('guideUpdate/startUpdate', now);
|
|
22813
|
+
}
|
|
22814
|
+
this.iterationStartTime = now;
|
|
22815
|
+
while (!this.paused &&
|
|
22816
|
+
this.queue.length &&
|
|
22817
|
+
!this.timedOut()) {
|
|
22818
|
+
this.runGuidePhase();
|
|
22819
|
+
}
|
|
22820
|
+
now = getNow();
|
|
22821
|
+
this.updateTotalTime += now - this.iterationStartTime;
|
|
22822
|
+
if (this.isComplete()) {
|
|
22823
|
+
this.complete(now);
|
|
22824
|
+
}
|
|
22794
22825
|
}
|
|
22795
|
-
|
|
22796
|
-
|
|
22797
|
-
|
|
22798
|
-
|
|
22799
|
-
|
|
22826
|
+
complete(time) {
|
|
22827
|
+
store.commit('guideUpdate/completeUpdate', time);
|
|
22828
|
+
this.cancel();
|
|
22829
|
+
}
|
|
22830
|
+
cancel() {
|
|
22831
|
+
this.updateId = null;
|
|
22832
|
+
this.paused = false;
|
|
22833
|
+
this.queue.length = 0;
|
|
22834
|
+
for (let phase of this.phases) {
|
|
22835
|
+
if (_.isFunction(phase.cancel)) {
|
|
22836
|
+
phase.cancel();
|
|
22837
|
+
}
|
|
22800
22838
|
}
|
|
22801
|
-
}
|
|
22839
|
+
}
|
|
22840
|
+
isComplete() {
|
|
22841
|
+
return !this.queue.length;
|
|
22842
|
+
}
|
|
22802
22843
|
}
|
|
22803
|
-
var
|
|
22804
|
-
|
|
22805
|
-
|
|
22806
|
-
|
|
22807
|
-
|
|
22808
|
-
|
|
22809
|
-
|
|
22810
|
-
|
|
22811
|
-
|
|
22812
|
-
|
|
22813
|
-
|
|
22844
|
+
var updateRunner = new UpdateRunner([
|
|
22845
|
+
new SimplePhase('guideShowing', guideShowingProc),
|
|
22846
|
+
new AutoDisplayPhase(),
|
|
22847
|
+
new BadgePhase(),
|
|
22848
|
+
new SimplePhase('resourceCenter', function () {
|
|
22849
|
+
resourceCenterProc(BuildingBlockResourceCenter.getResourceCenter());
|
|
22850
|
+
}),
|
|
22851
|
+
new SimplePhase('frameVisibility', function () {
|
|
22852
|
+
store.dispatch('frames/checkFrameVisibility');
|
|
22853
|
+
})
|
|
22854
|
+
]);
|
|
22855
|
+
function addUpdatePhase() {
|
|
22856
|
+
updateRunner.addPhase(...arguments);
|
|
22857
|
+
}
|
|
22858
|
+
function removeUpdatePhase() {
|
|
22859
|
+
updateRunner.removePhase(...arguments);
|
|
22814
22860
|
}
|
|
22815
22861
|
|
|
22816
22862
|
/**
|
|
@@ -23525,7 +23571,7 @@ function registerPostrenderScripts(runtime) {
|
|
|
23525
23571
|
});
|
|
23526
23572
|
}
|
|
23527
23573
|
|
|
23528
|
-
const pendoLocalStorage$
|
|
23574
|
+
const pendoLocalStorage$2 = agentStorage.getLocal();
|
|
23529
23575
|
var guideEvtCache = [];
|
|
23530
23576
|
var controlGuideLogMessage = 'Guide was not shown because this visitor is in a control group of an active experiment for the guide';
|
|
23531
23577
|
/**
|
|
@@ -25323,7 +25369,7 @@ var handleResizeOnVideoFullScreenChange = function (event) {
|
|
|
25323
25369
|
function shouldReloadGuides(url, visitorId, accountId, metadataHash) {
|
|
25324
25370
|
if (!url || !pendo$1.apiKey)
|
|
25325
25371
|
return false;
|
|
25326
|
-
if (areGuidesDisabled() && !(pendoLocalStorage$
|
|
25372
|
+
if (areGuidesDisabled() && !(pendoLocalStorage$2.getItem('pendo-designer-mode') === 'true'))
|
|
25327
25373
|
return false;
|
|
25328
25374
|
return (url !== reloadGuides.lastUrl ||
|
|
25329
25375
|
visitorId !== reloadGuides.lastVisitorId ||
|
|
@@ -25407,6 +25453,180 @@ function getActiveGuide(options) {
|
|
|
25407
25453
|
};
|
|
25408
25454
|
}
|
|
25409
25455
|
|
|
25456
|
+
var RELAY_FRAME_ID = 'pendo-live-preview-relay';
|
|
25457
|
+
var RELAY_FILE = 'live-preview.html';
|
|
25458
|
+
var MESSAGE_CONFIG = 'pendo-live-preview::config';
|
|
25459
|
+
var MESSAGE_READY = 'pendo-live-preview::ready';
|
|
25460
|
+
var MESSAGE_UPDATE = 'pendo-live-preview::update';
|
|
25461
|
+
var MESSAGE_END = 'pendo-live-preview::end';
|
|
25462
|
+
var MESSAGE_ENDED = 'pendo-live-preview::ended';
|
|
25463
|
+
var MESSAGE_END_ACK = 'pendo-live-preview::end-ack';
|
|
25464
|
+
var RELAY_END_MESSAGE_TIMEOUT = 2000;
|
|
25465
|
+
var activeRelay = null;
|
|
25466
|
+
function isRelayMessage(event, relay) {
|
|
25467
|
+
return event.source === relay.frame.contentWindow && !!event.data;
|
|
25468
|
+
}
|
|
25469
|
+
function sendConfig(relay) {
|
|
25470
|
+
if (!relay.frame.contentWindow)
|
|
25471
|
+
return;
|
|
25472
|
+
relay.frame.contentWindow.postMessage({
|
|
25473
|
+
type: MESSAGE_CONFIG,
|
|
25474
|
+
livePreview: relay.payload
|
|
25475
|
+
}, relay.origin);
|
|
25476
|
+
}
|
|
25477
|
+
function teardownRelay(relay) {
|
|
25478
|
+
window.removeEventListener('message', relay.listener, false);
|
|
25479
|
+
dom(relay.frame).remove();
|
|
25480
|
+
}
|
|
25481
|
+
function waitForRelayEndMessage(relay, onStopped) {
|
|
25482
|
+
var timer = setTimeout$1(finish, RELAY_END_MESSAGE_TIMEOUT);
|
|
25483
|
+
function finish() {
|
|
25484
|
+
clearTimeout(timer);
|
|
25485
|
+
window.removeEventListener('message', onMessage, false);
|
|
25486
|
+
onStopped();
|
|
25487
|
+
}
|
|
25488
|
+
function onMessage(event) {
|
|
25489
|
+
if (!isRelayMessage(event, relay))
|
|
25490
|
+
return;
|
|
25491
|
+
if (event.data.type === MESSAGE_END_ACK || event.data.type === MESSAGE_ENDED) {
|
|
25492
|
+
finish();
|
|
25493
|
+
}
|
|
25494
|
+
}
|
|
25495
|
+
window.addEventListener('message', onMessage, false);
|
|
25496
|
+
}
|
|
25497
|
+
function getRelayUrl() {
|
|
25498
|
+
// TODO: Support fetching the relay URL from designer lookasides
|
|
25499
|
+
var host = DESIGNER_SERVER || SERVER;
|
|
25500
|
+
var url = host + '/in-app-designer/latest/' + RELAY_FILE;
|
|
25501
|
+
var separator = url.indexOf('?') === -1 ? '?' : '&';
|
|
25502
|
+
return url + separator + 'parentOrigin=' + encodeURIComponent(window.location.origin);
|
|
25503
|
+
}
|
|
25504
|
+
function getRelayOrigin(url) {
|
|
25505
|
+
try {
|
|
25506
|
+
return new URL(url).origin;
|
|
25507
|
+
}
|
|
25508
|
+
catch (err) {
|
|
25509
|
+
return '*';
|
|
25510
|
+
}
|
|
25511
|
+
}
|
|
25512
|
+
function resolveSurvivingPreviewPosition(previousGuide, updatedGuide, savedPosition) {
|
|
25513
|
+
if (!savedPosition || !updatedGuide)
|
|
25514
|
+
return savedPosition;
|
|
25515
|
+
var updatedSteps = updatedGuide.steps || [];
|
|
25516
|
+
var updatedGuideId = updatedGuide.id;
|
|
25517
|
+
if (!updatedSteps.length || savedPosition.guideId !== updatedGuideId)
|
|
25518
|
+
return savedPosition;
|
|
25519
|
+
if (savedPosition.state !== 'active' && savedPosition.state !== 'advanced')
|
|
25520
|
+
return savedPosition;
|
|
25521
|
+
var targetStepId = savedPosition.state === 'advanced' && savedPosition.destinationStepId;
|
|
25522
|
+
if (!targetStepId)
|
|
25523
|
+
targetStepId = savedPosition.guideStepId;
|
|
25524
|
+
var savedGuideStepSurvives = _.some(updatedSteps, { id: savedPosition.guideStepId });
|
|
25525
|
+
var targetStepSurvives = _.some(updatedSteps, { id: targetStepId });
|
|
25526
|
+
if (savedGuideStepSurvives && targetStepSurvives)
|
|
25527
|
+
return savedPosition;
|
|
25528
|
+
var survivingStepId;
|
|
25529
|
+
if (targetStepSurvives) {
|
|
25530
|
+
survivingStepId = targetStepId;
|
|
25531
|
+
}
|
|
25532
|
+
else {
|
|
25533
|
+
const previousSteps = previousGuide && previousGuide.id === updatedGuideId
|
|
25534
|
+
? previousGuide.steps || []
|
|
25535
|
+
: [];
|
|
25536
|
+
const targetStepIndex = _.findIndex(previousSteps, { id: targetStepId });
|
|
25537
|
+
const clampedStepIndex = Math.min(Math.max(targetStepIndex, 0), updatedSteps.length - 1);
|
|
25538
|
+
survivingStepId = updatedSteps[clampedStepIndex].id;
|
|
25539
|
+
}
|
|
25540
|
+
return {
|
|
25541
|
+
guideId: updatedGuideId,
|
|
25542
|
+
guideStepId: survivingStepId,
|
|
25543
|
+
state: 'active'
|
|
25544
|
+
};
|
|
25545
|
+
}
|
|
25546
|
+
function createRelayFrame(url) {
|
|
25547
|
+
var frame = document.createElement('iframe');
|
|
25548
|
+
frame.id = RELAY_FRAME_ID;
|
|
25549
|
+
frame.setAttribute('aria-hidden', 'true');
|
|
25550
|
+
frame.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-storage-access-by-user-activation');
|
|
25551
|
+
frame.style.display = 'none';
|
|
25552
|
+
frame.src = url;
|
|
25553
|
+
return frame;
|
|
25554
|
+
}
|
|
25555
|
+
function hasLivePreviewRelay() {
|
|
25556
|
+
return !!activeRelay;
|
|
25557
|
+
}
|
|
25558
|
+
function applyRelayUpdate(payload) {
|
|
25559
|
+
var previousGuide = _.get(store.state, 'preview.designerPreview.guide');
|
|
25560
|
+
var updatedGuide = payload.guide;
|
|
25561
|
+
var savedPosition = _.get(store.state, 'preview.preview.state') || getLastGuideStepSeen();
|
|
25562
|
+
var survivingPosition = resolveSurvivingPreviewPosition(previousGuide, updatedGuide, savedPosition);
|
|
25563
|
+
store.commit('preview/setDesignerPreview', payload);
|
|
25564
|
+
if (survivingPosition !== savedPosition) {
|
|
25565
|
+
survivingPosition.time = getNow();
|
|
25566
|
+
writeLastStepSeenCache(survivingPosition);
|
|
25567
|
+
store.dispatch('preview/update', survivingPosition);
|
|
25568
|
+
}
|
|
25569
|
+
forceGuideReload();
|
|
25570
|
+
}
|
|
25571
|
+
function startLivePreviewRelay(livePreview, onEnded) {
|
|
25572
|
+
if (!livePreview || activeRelay)
|
|
25573
|
+
return;
|
|
25574
|
+
var url = getRelayUrl();
|
|
25575
|
+
var frame = createRelayFrame(url);
|
|
25576
|
+
var relay = {
|
|
25577
|
+
frame,
|
|
25578
|
+
origin: getRelayOrigin(url),
|
|
25579
|
+
payload: livePreview,
|
|
25580
|
+
listener: onMessage
|
|
25581
|
+
};
|
|
25582
|
+
function onMessage(event) {
|
|
25583
|
+
if (!isRelayMessage(event, relay))
|
|
25584
|
+
return;
|
|
25585
|
+
if (event.data.type === MESSAGE_READY) {
|
|
25586
|
+
sendConfig(relay);
|
|
25587
|
+
return;
|
|
25588
|
+
}
|
|
25589
|
+
if (event.data.type === MESSAGE_ENDED) {
|
|
25590
|
+
teardownRelay(relay);
|
|
25591
|
+
activeRelay = null;
|
|
25592
|
+
if (onEnded)
|
|
25593
|
+
onEnded();
|
|
25594
|
+
return;
|
|
25595
|
+
}
|
|
25596
|
+
if (event.data.type !== MESSAGE_UPDATE)
|
|
25597
|
+
return;
|
|
25598
|
+
applyRelayUpdate(event.data.payload);
|
|
25599
|
+
}
|
|
25600
|
+
activeRelay = relay;
|
|
25601
|
+
window.addEventListener('message', onMessage, false);
|
|
25602
|
+
getBody().appendChild(frame);
|
|
25603
|
+
}
|
|
25604
|
+
function stopLivePreviewRelay(onStopped) {
|
|
25605
|
+
var relay = activeRelay;
|
|
25606
|
+
activeRelay = null;
|
|
25607
|
+
if (!relay) {
|
|
25608
|
+
if (onStopped)
|
|
25609
|
+
onStopped();
|
|
25610
|
+
return;
|
|
25611
|
+
}
|
|
25612
|
+
if (!relay.frame.contentWindow) {
|
|
25613
|
+
teardownRelay(relay);
|
|
25614
|
+
if (onStopped)
|
|
25615
|
+
onStopped();
|
|
25616
|
+
return;
|
|
25617
|
+
}
|
|
25618
|
+
window.removeEventListener('message', relay.listener, false);
|
|
25619
|
+
// Removing the frame now would destroy the ::end below, so the callback removes it once the relay answers
|
|
25620
|
+
waitForRelayEndMessage(relay, function () {
|
|
25621
|
+
dom(relay.frame).remove();
|
|
25622
|
+
if (onStopped)
|
|
25623
|
+
onStopped();
|
|
25624
|
+
});
|
|
25625
|
+
relay.frame.contentWindow.postMessage({
|
|
25626
|
+
type: MESSAGE_END
|
|
25627
|
+
}, relay.origin);
|
|
25628
|
+
}
|
|
25629
|
+
|
|
25410
25630
|
var REMOVE_FRAME_ACTION = 'frames/removeFrame';
|
|
25411
25631
|
function tryParseJson(messageData) {
|
|
25412
25632
|
if (!_.isString(messageData)) {
|
|
@@ -25921,7 +26141,8 @@ function initializeCrossFrameChannel(store) {
|
|
|
25921
26141
|
return () => crossFrameChannel.close();
|
|
25922
26142
|
}
|
|
25923
26143
|
|
|
25924
|
-
const pendoLocalStorage$
|
|
26144
|
+
const pendoLocalStorage$1 = agentStorage.getLocal();
|
|
26145
|
+
var pendoLivePreview$1 = 'pendo-live-preview';
|
|
25925
26146
|
var pendoPreview$1 = 'pendo-preview';
|
|
25926
26147
|
var designerPreview$1 = 'current-guide-preview';
|
|
25927
26148
|
var previewFrame;
|
|
@@ -25935,6 +26156,11 @@ function startPreviewMode(window) {
|
|
|
25935
26156
|
return;
|
|
25936
26157
|
addPreviewUI(window);
|
|
25937
26158
|
store.dispatch('preview/start', _.extend(config));
|
|
26159
|
+
if (config.livePreview) {
|
|
26160
|
+
startLivePreviewRelay(config.livePreview, function () {
|
|
26161
|
+
exitPreviewMode({ preventWindowClose: false });
|
|
26162
|
+
});
|
|
26163
|
+
}
|
|
25938
26164
|
agentStorage.clear(LAST_STEP_ADVANCED_COOKIE);
|
|
25939
26165
|
enableCookies(false);
|
|
25940
26166
|
buffers.lock();
|
|
@@ -25998,7 +26224,7 @@ function restartPreview(activeGuides, lastGuideStepSeen, language) {
|
|
|
25998
26224
|
var config = findStoredPreviewConfig();
|
|
25999
26225
|
// Go back to 1st step of walkthrough on explicit restart
|
|
26000
26226
|
if (config) {
|
|
26001
|
-
var previewGuide = _.findWhere(activeGuides, { id: config
|
|
26227
|
+
var previewGuide = _.findWhere(activeGuides, { id: getPreviewGuideId(config) });
|
|
26002
26228
|
if (previewGuide) {
|
|
26003
26229
|
var restartConfig = { stepId: previewGuide.steps[0].id };
|
|
26004
26230
|
// language is used in `previewGuideRequest` to generate the proper urls for rendering translations
|
|
@@ -26122,6 +26348,7 @@ function prepareDesignerPreviewGuide() {
|
|
|
26122
26348
|
currentPreviewGuideConfig.guide.expiresAfter = null;
|
|
26123
26349
|
return [currentPreviewGuideConfig.guide];
|
|
26124
26350
|
}
|
|
26351
|
+
return [];
|
|
26125
26352
|
}
|
|
26126
26353
|
function preparePreviewGuides(activeGuides) {
|
|
26127
26354
|
var config = findStoredPreviewConfig();
|
|
@@ -26200,7 +26427,8 @@ function preparePreviewLastGuideStepSeen(activeGuides, lastGuideStepSeen) {
|
|
|
26200
26427
|
var config = findStoredPreviewConfig();
|
|
26201
26428
|
if (!config)
|
|
26202
26429
|
return lastGuideStepSeen;
|
|
26203
|
-
var
|
|
26430
|
+
var guideId = getPreviewGuideId(config);
|
|
26431
|
+
var previewGuide = _.findWhere(activeGuides, { id: guideId });
|
|
26204
26432
|
if (!previewGuide)
|
|
26205
26433
|
return lastGuideStepSeen;
|
|
26206
26434
|
if (config.state)
|
|
@@ -26213,7 +26441,7 @@ function preparePreviewLastGuideStepSeen(activeGuides, lastGuideStepSeen) {
|
|
|
26213
26441
|
return {};
|
|
26214
26442
|
}
|
|
26215
26443
|
return {
|
|
26216
|
-
guideId
|
|
26444
|
+
guideId,
|
|
26217
26445
|
guideStepId: config.stepId,
|
|
26218
26446
|
state: 'active'
|
|
26219
26447
|
};
|
|
@@ -26261,9 +26489,13 @@ function updatePreview(document, activeGuides, lastGuideStepSeen) {
|
|
|
26261
26489
|
}
|
|
26262
26490
|
}, location.origin);
|
|
26263
26491
|
}
|
|
26492
|
+
var previewGuideId = getPreviewGuideId(config);
|
|
26264
26493
|
var guide = _.find(activeGuides, function (guide) {
|
|
26265
|
-
return guide.id ===
|
|
26494
|
+
return guide.id === previewGuideId;
|
|
26266
26495
|
});
|
|
26496
|
+
// In live preview the guide may not have been relayed yet; skip this tick and retry on the next.
|
|
26497
|
+
if (!guide && config.livePreview)
|
|
26498
|
+
return;
|
|
26267
26499
|
var currentStepIndex = 0;
|
|
26268
26500
|
var stepCount = guide.steps.length;
|
|
26269
26501
|
var lastSeen = lastGuideStepSeen || {};
|
|
@@ -26425,7 +26657,7 @@ function exitPreviewMode(designerConfig) {
|
|
|
26425
26657
|
if (currentPreviewGuideConfig) {
|
|
26426
26658
|
wasInDesignerSameTabPreview = currentPreviewGuideConfig.previewModeType === 'SAME_TAB';
|
|
26427
26659
|
}
|
|
26428
|
-
var storedNavigationMode = pendoLocalStorage$
|
|
26660
|
+
var storedNavigationMode = pendoLocalStorage$1.getItem('pendo-navigation-state');
|
|
26429
26661
|
store.dispatch('preview/exit');
|
|
26430
26662
|
hideGuides({ stayHidden: true });
|
|
26431
26663
|
agentStorage.clear(LAST_STEP_ADVANCED_COOKIE);
|
|
@@ -26434,30 +26666,37 @@ function exitPreviewMode(designerConfig) {
|
|
|
26434
26666
|
restoreGuideLoader();
|
|
26435
26667
|
removeUpdatePhase(updatePreviewUI);
|
|
26436
26668
|
enableCookies(true);
|
|
26437
|
-
|
|
26438
|
-
|
|
26439
|
-
|
|
26440
|
-
|
|
26441
|
-
|
|
26442
|
-
|
|
26443
|
-
|
|
26444
|
-
|
|
26445
|
-
|
|
26446
|
-
|
|
26447
|
-
|
|
26448
|
-
|
|
26449
|
-
|
|
26450
|
-
|
|
26451
|
-
|
|
26452
|
-
|
|
26453
|
-
|
|
26669
|
+
function finishExit() {
|
|
26670
|
+
// Try to launch back into designer
|
|
26671
|
+
if (wasInDesignerSameTabPreview && storedNavigationMode) {
|
|
26672
|
+
pendoLocalStorage$1.setItem(designerPreview$1, JSON.stringify(_.extend(currentPreviewGuideConfig, { isExitBackToDesigner: true })));
|
|
26673
|
+
try {
|
|
26674
|
+
var message = JSON.parse(storedNavigationMode);
|
|
26675
|
+
var launchOptions = {
|
|
26676
|
+
target: message.baseUrl,
|
|
26677
|
+
gcsBucket: message.gcsBucket,
|
|
26678
|
+
preloader: true,
|
|
26679
|
+
DADesigner: message.isDADesigner,
|
|
26680
|
+
host: message.host,
|
|
26681
|
+
forceNativeDesigner: currentPreviewGuideConfig.guide.appPlatform === 'web'
|
|
26682
|
+
};
|
|
26683
|
+
store.dispatch('preview/launchInAppDesigner', launchOptions);
|
|
26684
|
+
}
|
|
26685
|
+
catch (err) {
|
|
26686
|
+
store.dispatch('preview/exitDesignerPreview');
|
|
26687
|
+
}
|
|
26688
|
+
return;
|
|
26454
26689
|
}
|
|
26690
|
+
if (designerConfig && !designerConfig.preventWindowClose)
|
|
26691
|
+
window.close();
|
|
26692
|
+
// For cases when window.close() does not work, as when exiting restored preview sessions in newly opened windows, resume normal guide display
|
|
26693
|
+
forceGuideReload();
|
|
26694
|
+
}
|
|
26695
|
+
if (hasLivePreviewRelay()) {
|
|
26696
|
+
stopLivePreviewRelay(finishExit);
|
|
26455
26697
|
return;
|
|
26456
26698
|
}
|
|
26457
|
-
|
|
26458
|
-
window.close();
|
|
26459
|
-
// For cases when window.close() does not work, as when exiting restored preview sessions in newly opened windows, resume normal guide display
|
|
26460
|
-
forceGuideReload();
|
|
26699
|
+
finishExit();
|
|
26461
26700
|
}
|
|
26462
26701
|
function clonePreviewConfig(getStore, path) {
|
|
26463
26702
|
var previewConfig = _.get(getStore(), path);
|
|
@@ -26470,6 +26709,14 @@ function getStore() {
|
|
|
26470
26709
|
}
|
|
26471
26710
|
var findStoredPreviewConfig = _.partial(clonePreviewConfig, getStore, 'state.preview.preview');
|
|
26472
26711
|
var findStoredDesignerPreviewConfig = _.partial(clonePreviewConfig, getStore, 'state.preview.designerPreview');
|
|
26712
|
+
function getPreviewGuideId(config) {
|
|
26713
|
+
if (config.guideId)
|
|
26714
|
+
return config.guideId;
|
|
26715
|
+
// config.guideId is empty for unsaved guides in live preview
|
|
26716
|
+
// Live preview relies on guide id from the websocket relay instead
|
|
26717
|
+
if (config.livePreview)
|
|
26718
|
+
return _.get(findStoredDesignerPreviewConfig(), 'guide.id');
|
|
26719
|
+
}
|
|
26473
26720
|
function getGuideUrl(config) {
|
|
26474
26721
|
if (!config.subscriptionId || !config.guideId)
|
|
26475
26722
|
return;
|
|
@@ -26518,6 +26765,35 @@ function previewGuideRequest(config, id) {
|
|
|
26518
26765
|
log.info('Invalid origin, exiting preview');
|
|
26519
26766
|
return q.reject('invalid origin');
|
|
26520
26767
|
}
|
|
26768
|
+
// In live preview, the guide comes from the websocket relay (see live-preview-relay.js) and
|
|
26769
|
+
// stored on state.preview.designerPreview. We render it directly and skip the backend fetch.
|
|
26770
|
+
if (config.livePreview) {
|
|
26771
|
+
var livePreviewConfig = findStoredDesignerPreviewConfig();
|
|
26772
|
+
var liveGuides = prepareDesignerPreviewGuide();
|
|
26773
|
+
if (!livePreviewConfig || !liveGuides.length || !liveGuides[0].steps.length) {
|
|
26774
|
+
pendo$1.guidesPayload({
|
|
26775
|
+
id,
|
|
26776
|
+
lastGuideStepSeen: {},
|
|
26777
|
+
guides: []
|
|
26778
|
+
});
|
|
26779
|
+
return q.resolve();
|
|
26780
|
+
}
|
|
26781
|
+
var liveGuide = liveGuides[0];
|
|
26782
|
+
coerceGuideForPreview(liveGuide);
|
|
26783
|
+
formatGuideSeenStateForPreviewGuide(liveGuide, config.stepId);
|
|
26784
|
+
pendo$1.guideWidget = {
|
|
26785
|
+
enabled: false,
|
|
26786
|
+
data: { guideCssUrl: livePreviewConfig.guideCssUrl }
|
|
26787
|
+
};
|
|
26788
|
+
pendo$1.guideCssUrl = livePreviewConfig.guideCssUrl;
|
|
26789
|
+
pendo$1.throttling = undefined;
|
|
26790
|
+
pendo$1.guidesPayload({
|
|
26791
|
+
id,
|
|
26792
|
+
lastGuideStepSeen: preparePreviewLastGuideStepSeen(liveGuides, pendo$1.lastGuideStepSeen),
|
|
26793
|
+
guides: liveGuides
|
|
26794
|
+
});
|
|
26795
|
+
return q.resolve();
|
|
26796
|
+
}
|
|
26521
26797
|
var guideURL = getGuideUrl(config);
|
|
26522
26798
|
if (!guideURL) {
|
|
26523
26799
|
log.info('Invalid guide url, exiting preview');
|
|
@@ -26607,9 +26883,16 @@ function previewGuideRequest(config, id) {
|
|
|
26607
26883
|
}
|
|
26608
26884
|
function previewLoadGuideJs(url, callback) {
|
|
26609
26885
|
var config = findStoredPreviewConfig();
|
|
26610
|
-
if (!config
|
|
26886
|
+
if (!config)
|
|
26611
26887
|
return q.reject();
|
|
26612
26888
|
var match = /[?&]id=(\d+)/.exec(url);
|
|
26889
|
+
// Live preview has no backend guide URL, but the url still carries the internal guide-request id
|
|
26890
|
+
// that guidesPayload matches against; render the relayed guide directly.
|
|
26891
|
+
if (config.livePreview) {
|
|
26892
|
+
return previewGuideRequest(config, match && match[1]).then(callback);
|
|
26893
|
+
}
|
|
26894
|
+
if (!getGuideUrl(config))
|
|
26895
|
+
return q.reject();
|
|
26613
26896
|
if (!match || match.length < 1)
|
|
26614
26897
|
return q.reject();
|
|
26615
26898
|
return previewGuideRequest(config, match[1]).then(callback);
|
|
@@ -26733,7 +27016,7 @@ const Keys = (function () {
|
|
|
26733
27016
|
};
|
|
26734
27017
|
})();
|
|
26735
27018
|
|
|
26736
|
-
const pendoLocalStorage
|
|
27019
|
+
const pendoLocalStorage = agentStorage.getLocal();
|
|
26737
27020
|
var DesignerV2 = (function () {
|
|
26738
27021
|
// Always use the production server for CNAME customers
|
|
26739
27022
|
var host = DESIGNER_SERVER || SERVER;
|
|
@@ -26747,7 +27030,7 @@ var DesignerV2 = (function () {
|
|
|
26747
27030
|
function localStorageNavigation(options) {
|
|
26748
27031
|
if (options.ignoreLocalStorageNavigation)
|
|
26749
27032
|
return;
|
|
26750
|
-
var storedNavigationMode = pendoLocalStorage
|
|
27033
|
+
var storedNavigationMode = pendoLocalStorage.getItem('pendo-navigation-state');
|
|
26751
27034
|
if (!storedNavigationMode)
|
|
26752
27035
|
return;
|
|
26753
27036
|
// only launch from local storage if you are the leader frame
|
|
@@ -27464,6 +27747,16 @@ function launchDesignerOrPreview(options) {
|
|
|
27464
27747
|
* @label designerPreview
|
|
27465
27748
|
*/
|
|
27466
27749
|
agentStorage.registry.addLocal(designerPreview$1, { localStorageOnly: true });
|
|
27750
|
+
/**
|
|
27751
|
+
* Ephemeral live-preview session handoff. Scoped to the tab so it survives reloads
|
|
27752
|
+
* but is never restored in a freshly opened preview tab.
|
|
27753
|
+
*
|
|
27754
|
+
* @name pendo-live-preview
|
|
27755
|
+
* @category Cookies/sessionStorage
|
|
27756
|
+
* @access public
|
|
27757
|
+
* @label pendoLivePreview
|
|
27758
|
+
*/
|
|
27759
|
+
agentStorage.registry.addSession(pendoLivePreview$1);
|
|
27467
27760
|
// in order, try to launch:
|
|
27468
27761
|
// 1) designer
|
|
27469
27762
|
// 2) preview
|
|
@@ -28712,6 +29005,7 @@ const PluginAPI = {
|
|
|
28712
29005
|
SERVER,
|
|
28713
29006
|
DESIGNER_SERVER
|
|
28714
29007
|
},
|
|
29008
|
+
isTrustedOrigin,
|
|
28715
29009
|
NodeSerializer,
|
|
28716
29010
|
q,
|
|
28717
29011
|
SendQueue,
|
|
@@ -34455,7 +34749,16 @@ var FramesModule = (function () {
|
|
|
34455
34749
|
testSelector(context, payload) {
|
|
34456
34750
|
const { selector, sourceFrame, promiseId } = payload;
|
|
34457
34751
|
const startTime = performance.now();
|
|
34458
|
-
|
|
34752
|
+
let count = 0;
|
|
34753
|
+
// Sizzle only ever matches selectors. pendo.dom would parse a payload that
|
|
34754
|
+
// starts with '<' as HTML, through innerHTML on the live document (APP-165872).
|
|
34755
|
+
// It also swallowed invalid selectors, which Sizzle throws on.
|
|
34756
|
+
try {
|
|
34757
|
+
count = SizzleProxy(selector).length;
|
|
34758
|
+
}
|
|
34759
|
+
catch (e) {
|
|
34760
|
+
count = 0;
|
|
34761
|
+
}
|
|
34459
34762
|
const timing = performance.now() - startTime;
|
|
34460
34763
|
if (promiseId && sourceFrame) {
|
|
34461
34764
|
crossFrameChannel.postMessage({
|
|
@@ -35043,7 +35346,7 @@ var MetadataModule = (function () {
|
|
|
35043
35346
|
};
|
|
35044
35347
|
})();
|
|
35045
35348
|
|
|
35046
|
-
|
|
35349
|
+
var pendoLivePreview = 'pendo-live-preview';
|
|
35047
35350
|
var pendoPreview = 'pendo-preview';
|
|
35048
35351
|
var designerPreview = 'current-guide-preview';
|
|
35049
35352
|
var PreviewModule = (function () {
|
|
@@ -35067,11 +35370,15 @@ var PreviewModule = (function () {
|
|
|
35067
35370
|
load(context) {
|
|
35068
35371
|
try {
|
|
35069
35372
|
var storage = context.getters.storage();
|
|
35070
|
-
var config = JSON.parse(storage.getItem(pendoPreview));
|
|
35373
|
+
var config = JSON.parse(storage.getLocal().getItem(pendoPreview));
|
|
35071
35374
|
if (config.apiKey === pendo$1.apiKey) {
|
|
35375
|
+
var livePreviewConfig = JSON.parse(storage.getSession().getItem(pendoLivePreview));
|
|
35376
|
+
if (livePreviewConfig) {
|
|
35377
|
+
config.livePreview = livePreviewConfig;
|
|
35378
|
+
}
|
|
35072
35379
|
context.commit('setPreview', config);
|
|
35073
35380
|
}
|
|
35074
|
-
var designerPreviewConfig = storage.getItem(designerPreview);
|
|
35381
|
+
var designerPreviewConfig = storage.getLocal().getItem(designerPreview);
|
|
35075
35382
|
if (designerPreviewConfig) {
|
|
35076
35383
|
context.commit('setDesignerPreview', JSON.parse(designerPreviewConfig));
|
|
35077
35384
|
}
|
|
@@ -35082,11 +35389,19 @@ var PreviewModule = (function () {
|
|
|
35082
35389
|
write(context) {
|
|
35083
35390
|
try {
|
|
35084
35391
|
var storage = context.getters.storage();
|
|
35085
|
-
|
|
35086
|
-
|
|
35392
|
+
var preview = context.state.preview;
|
|
35393
|
+
var livePreview = preview && preview.livePreview;
|
|
35394
|
+
if (preview) {
|
|
35395
|
+
storage.getLocal().setItem(pendoPreview, JSON.stringify(_.omit(preview, 'livePreview')));
|
|
35396
|
+
}
|
|
35397
|
+
else {
|
|
35398
|
+
storage.getLocal().removeItem(pendoPreview);
|
|
35399
|
+
}
|
|
35400
|
+
if (livePreview) {
|
|
35401
|
+
storage.getSession().setItem(pendoLivePreview, JSON.stringify(livePreview));
|
|
35087
35402
|
}
|
|
35088
35403
|
else {
|
|
35089
|
-
storage.removeItem(
|
|
35404
|
+
storage.getSession().removeItem(pendoLivePreview);
|
|
35090
35405
|
}
|
|
35091
35406
|
}
|
|
35092
35407
|
catch (e) {
|
|
@@ -35098,8 +35413,7 @@ var PreviewModule = (function () {
|
|
|
35098
35413
|
},
|
|
35099
35414
|
exitDesignerPreview(context) {
|
|
35100
35415
|
context.commit('setDesignerPreview', null);
|
|
35101
|
-
|
|
35102
|
-
storage.removeItem(designerPreview);
|
|
35416
|
+
context.getters.storage().getLocal().removeItem(designerPreview);
|
|
35103
35417
|
},
|
|
35104
35418
|
launchInAppDesigner(context, launchOptions) {
|
|
35105
35419
|
DesignerV2.launchInAppDesigner(launchOptions);
|
|
@@ -35126,7 +35440,7 @@ var PreviewModule = (function () {
|
|
|
35126
35440
|
return !!state.designerPreview && getters.isInPreviewMode();
|
|
35127
35441
|
},
|
|
35128
35442
|
storage() {
|
|
35129
|
-
return
|
|
35443
|
+
return agentStorage;
|
|
35130
35444
|
}
|
|
35131
35445
|
};
|
|
35132
35446
|
return {
|
|
@@ -35282,13 +35596,10 @@ function highlight(dom, elements) {
|
|
|
35282
35596
|
}
|
|
35283
35597
|
function highlightElements(dom, selector) {
|
|
35284
35598
|
try {
|
|
35285
|
-
|
|
35286
|
-
|
|
35287
|
-
|
|
35288
|
-
|
|
35289
|
-
elements.push(query[i]);
|
|
35290
|
-
}
|
|
35291
|
-
return highlight(dom, elements);
|
|
35599
|
+
// Sizzle rather than dom() so a selector arriving over the cross-frame channel
|
|
35600
|
+
// can't reach dom()'s innerHTML branch (APP-165872). dom is still needed below
|
|
35601
|
+
// for the overlay elements it builds.
|
|
35602
|
+
return highlight(dom, SizzleProxy(selector));
|
|
35292
35603
|
}
|
|
35293
35604
|
catch (e) {
|
|
35294
35605
|
clearHighlights();
|
|
@@ -40927,6 +41238,14 @@ class DOMPrompt {
|
|
|
40927
41238
|
});
|
|
40928
41239
|
}
|
|
40929
41240
|
setupObserver() {
|
|
41241
|
+
if (!document.body) {
|
|
41242
|
+
this.observerReadyListener = () => {
|
|
41243
|
+
document.removeEventListener('DOMContentLoaded', this.observerReadyListener);
|
|
41244
|
+
this.setupObserver();
|
|
41245
|
+
};
|
|
41246
|
+
document.addEventListener('DOMContentLoaded', this.observerReadyListener);
|
|
41247
|
+
return;
|
|
41248
|
+
}
|
|
40930
41249
|
this.promptContainer = new this.dom.Observer();
|
|
40931
41250
|
const validInputEls = this._.filter(this.inputEls, el => el);
|
|
40932
41251
|
const validSubmitEls = this._.filter(this.submitEls, el => el);
|
|
@@ -41002,6 +41321,9 @@ class DOMPrompt {
|
|
|
41002
41321
|
});
|
|
41003
41322
|
}
|
|
41004
41323
|
teardown() {
|
|
41324
|
+
if (this.observerReadyListener) {
|
|
41325
|
+
document.removeEventListener('DOMContentLoaded', this.observerReadyListener);
|
|
41326
|
+
}
|
|
41005
41327
|
if (this.promptContainer) {
|
|
41006
41328
|
this.promptContainer.teardown();
|
|
41007
41329
|
}
|
|
@@ -41046,7 +41368,8 @@ class PromptPlugin {
|
|
|
41046
41368
|
// register config schema
|
|
41047
41369
|
this.api.ConfigReader.addOption(this.configName, ['pendoconfig', 'snippet']);
|
|
41048
41370
|
this.loadConfig(this.configName)
|
|
41049
|
-
.then(config => this.onConfigLoaded(config))
|
|
41371
|
+
.then(config => this.onConfigLoaded(config))
|
|
41372
|
+
.catch(err => this.api.log.error('[PromptPlugin] Failed to load config:', err));
|
|
41050
41373
|
// Listen for URL changes to re-evaluate page rules
|
|
41051
41374
|
// guidesLoaded: normalizedUrl comes back from the guides payload
|
|
41052
41375
|
// urlChanged: handles SPA navigation where guides may already be cached and guidesLoaded won't re-fire
|
|
@@ -41112,7 +41435,12 @@ class PromptPlugin {
|
|
|
41112
41435
|
const isCurrentlyActive = this._.contains(currentlyActiveIds, aiAgent.id);
|
|
41113
41436
|
const shouldBeActive = this.testPageRule(aiAgent.pageRule, currentUrl, aiAgent.id);
|
|
41114
41437
|
if (shouldBeActive && !isCurrentlyActive) {
|
|
41115
|
-
|
|
41438
|
+
try {
|
|
41439
|
+
this.observePrompt(aiAgent);
|
|
41440
|
+
}
|
|
41441
|
+
catch (err) {
|
|
41442
|
+
this.api.log.error('[PromptPlugin] Failed to set up agent:', aiAgent.id, err);
|
|
41443
|
+
}
|
|
41116
41444
|
}
|
|
41117
41445
|
});
|
|
41118
41446
|
}
|
|
@@ -41161,7 +41489,12 @@ class PromptPlugin {
|
|
|
41161
41489
|
this._.each(domAgents, aiAgent => {
|
|
41162
41490
|
const currentUrl = getNormalizedUrl();
|
|
41163
41491
|
if (this.testPageRule(aiAgent.pageRule, currentUrl, aiAgent.id)) {
|
|
41164
|
-
|
|
41492
|
+
try {
|
|
41493
|
+
this.observePrompt(aiAgent);
|
|
41494
|
+
}
|
|
41495
|
+
catch (err) {
|
|
41496
|
+
this.api.log.error('[PromptPlugin] Failed to set up agent:', aiAgent.id, err);
|
|
41497
|
+
}
|
|
41165
41498
|
}
|
|
41166
41499
|
});
|
|
41167
41500
|
}
|
|
@@ -42910,10 +43243,13 @@ function GuideMarkdown() {
|
|
|
42910
43243
|
}
|
|
42911
43244
|
|
|
42912
43245
|
// Resolve the global object without relying on a bare `window` identifier.
|
|
42913
|
-
// `window` is only a bound global in a normal browser realm;
|
|
42914
|
-
//
|
|
42915
|
-
//
|
|
42916
|
-
//
|
|
43246
|
+
// `window` is only a bound global in a normal browser realm; the compat builds
|
|
43247
|
+
// bind it via their `(function(window, document){...})` wrapper, but the npm
|
|
43248
|
+
// module build has no wrapper, so its module scope can be evaluated in realms
|
|
43249
|
+
// where `window` is undeclared (a Web Worker, or a customer bundler re-chunking
|
|
43250
|
+
// the module into worker-reachable code) and a bare reference throws a
|
|
43251
|
+
// ReferenceError at module-load. `globalThis` resolves in every realm; the
|
|
43252
|
+
// `self`/`window` fallbacks cover engines that predate it. APP-160669, APP-165809.
|
|
42917
43253
|
function getGlobalScope() {
|
|
42918
43254
|
if (typeof globalThis !== 'undefined') {
|
|
42919
43255
|
return globalThis;
|
|
@@ -42961,6 +43297,7 @@ var setTimeout$1 = setTimeout;
|
|
|
42961
43297
|
//
|
|
42962
43298
|
// NOTE: This, for now, at least is identified as an instanceof HTMLAnchorElement
|
|
42963
43299
|
|
|
43300
|
+
|
|
42964
43301
|
function URLPolyfillFactory(doc) {
|
|
42965
43302
|
return function URL(str) {
|
|
42966
43303
|
var a = doc.createElement('a');
|
|
@@ -42992,24 +43329,12 @@ function URLPolyfillFactory(doc) {
|
|
|
42992
43329
|
var URL$1 = (function(global, factory) {
|
|
42993
43330
|
var nativeURL = global.URL;
|
|
42994
43331
|
if (typeof nativeURL !== 'function' || !/native/.test(nativeURL)) {
|
|
42995
|
-
return factory(document);
|
|
43332
|
+
return factory(global.document);
|
|
42996
43333
|
}
|
|
42997
43334
|
|
|
42998
43335
|
return nativeURL;
|
|
42999
|
-
})(
|
|
43336
|
+
})(globalScope, URLPolyfillFactory);
|
|
43000
43337
|
|
|
43001
|
-
/*
|
|
43002
|
-
* @license MIT - https://github.com/taylorhakes/promise-polyfill
|
|
43003
|
-
*/
|
|
43004
|
-
// Modified to not affect global environment: the `var` keeps the binding module-local
|
|
43005
|
-
// so the polyfill never assigns to the real global `Promise`.
|
|
43006
|
-
//
|
|
43007
|
-
// Main-thread only. Reading `window` here is safe because rollup's `inject` is skipped
|
|
43008
|
-
// for the nested worker build (see `skipPlugins` on webWorkerLoader in
|
|
43009
|
-
// rollup/web-sdk.config.mjs): a worker realm has a native Promise and no `window`, so
|
|
43010
|
-
// injecting this polyfill there was both unreachable and fatal — it threw
|
|
43011
|
-
// `ReferenceError: window is not defined` at worker startup and stopped session replay
|
|
43012
|
-
// with a WORKER_ERROR. APP-163474.
|
|
43013
43338
|
var Promise$1;
|
|
43014
43339
|
|
|
43015
43340
|
Promise$1 = (function (global, factory) {
|
|
@@ -43019,7 +43344,7 @@ Promise$1 = (function (global, factory) {
|
|
|
43019
43344
|
}
|
|
43020
43345
|
|
|
43021
43346
|
return nativePromise;
|
|
43022
|
-
}(
|
|
43347
|
+
}(globalScope, (function () {
|
|
43023
43348
|
/**
|
|
43024
43349
|
* @this {Promise}
|
|
43025
43350
|
*/
|
|
@@ -43484,6 +43809,7 @@ const MESSAGE_PROMISE = 'promise';
|
|
|
43484
43809
|
* @property {*} requestId - The id of the request. This is used to match responses to requests.
|
|
43485
43810
|
* @property {*} sender - The name of the sender. This is used to determine if the message is from a trusted source.
|
|
43486
43811
|
* @property {*} destination - The destination of the message. This is used to determine where to send the message.
|
|
43812
|
+
* @property {*} origin - The origin a received message arrived from. Responses are targeted back at it.
|
|
43487
43813
|
*/
|
|
43488
43814
|
function Message(name, type, data) {
|
|
43489
43815
|
this.name = name;
|
|
@@ -43512,6 +43838,7 @@ const fromEvent = (event) => {
|
|
|
43512
43838
|
message.requestId = requestId;
|
|
43513
43839
|
message.sender = sender;
|
|
43514
43840
|
message.destination = event.source;
|
|
43841
|
+
message.origin = event.origin;
|
|
43515
43842
|
message.isResponse = isResponse;
|
|
43516
43843
|
|
|
43517
43844
|
return message;
|
|
@@ -43521,7 +43848,7 @@ const fromEvent = (event) => {
|
|
|
43521
43848
|
};
|
|
43522
43849
|
|
|
43523
43850
|
const serialize = (message) => {
|
|
43524
|
-
const redacted = omit(message, ['destination', 'toString', 'data']);
|
|
43851
|
+
const redacted = omit(message, ['destination', 'origin', 'toString', 'data']);
|
|
43525
43852
|
redacted.data = JSON.stringify(message.data, (key, value) => {
|
|
43526
43853
|
if (typeof value === 'function' || value instanceof RegExp) {
|
|
43527
43854
|
return value.toString ? value.toString() : undefined; // return undefined to omit
|
|
@@ -43637,14 +43964,24 @@ const state = {
|
|
|
43637
43964
|
topics: {},
|
|
43638
43965
|
senderIds: {},
|
|
43639
43966
|
originProvider: undefined,
|
|
43967
|
+
isOriginAllowed: undefined,
|
|
43640
43968
|
destinationProvider: undefined
|
|
43641
43969
|
};
|
|
43642
43970
|
|
|
43643
43971
|
const getListener = () => `${state.appName}${state.connectionName}(${state.frameId})`;
|
|
43644
43972
|
|
|
43973
|
+
// An opaque origin (sandboxed frame, file://) serializes to the string "null",
|
|
43974
|
+
// which postMessage rejects as a targetOrigin, so those fall back to the provider.
|
|
43975
|
+
const getTargetOrigin = (message) => {
|
|
43976
|
+
if (message.origin && message.origin !== 'null') return message.origin;
|
|
43977
|
+
|
|
43978
|
+
return state.originProvider();
|
|
43979
|
+
};
|
|
43980
|
+
|
|
43645
43981
|
const _send = (message) => {
|
|
43646
43982
|
if (!message.sender) message.sender = getListener();
|
|
43647
43983
|
const win = message.destination || state.destinationProvider();
|
|
43984
|
+
const targetOrigin = getTargetOrigin(message);
|
|
43648
43985
|
const redactedMessage = serialize(message);
|
|
43649
43986
|
|
|
43650
43987
|
const port = getPortForSender(message.sender);
|
|
@@ -43654,11 +43991,11 @@ const _send = (message) => {
|
|
|
43654
43991
|
// XXX Need to handle the scenario where MessageChannel is unavailable
|
|
43655
43992
|
const channel = new MessageChannel();
|
|
43656
43993
|
addPort(channel.port1, message.sender);
|
|
43657
|
-
win.postMessage(redactedMessage,
|
|
43994
|
+
win.postMessage(redactedMessage, targetOrigin, [channel.port2]);
|
|
43658
43995
|
} else if (port) {
|
|
43659
43996
|
port.postMessage(redactedMessage);
|
|
43660
43997
|
} else {
|
|
43661
|
-
win.postMessage(redactedMessage,
|
|
43998
|
+
win.postMessage(redactedMessage, targetOrigin);
|
|
43662
43999
|
}
|
|
43663
44000
|
};
|
|
43664
44001
|
|
|
@@ -43788,6 +44125,8 @@ const commonFns = pipe(
|
|
|
43788
44125
|
);
|
|
43789
44126
|
|
|
43790
44127
|
const filterWrongOrigin = (event) => {
|
|
44128
|
+
if (state.isOriginAllowed) return state.isOriginAllowed(event.origin) ? event : false;
|
|
44129
|
+
|
|
43791
44130
|
if (state.originProvider() === '*') return event;
|
|
43792
44131
|
|
|
43793
44132
|
return event.origin === state.originProvider() ? event : false;
|
|
@@ -43852,7 +44191,8 @@ const startListening = (
|
|
|
43852
44191
|
originProvider,
|
|
43853
44192
|
destinationProvider,
|
|
43854
44193
|
usePingAutoReconnect = false,
|
|
43855
|
-
connectionName
|
|
44194
|
+
connectionName,
|
|
44195
|
+
isOriginAllowed
|
|
43856
44196
|
) => {
|
|
43857
44197
|
if (!window.addEventListener) return;
|
|
43858
44198
|
state.appName = appName;
|
|
@@ -43861,6 +44201,7 @@ const startListening = (
|
|
|
43861
44201
|
state.usePingAutoReconnect = usePingAutoReconnect;
|
|
43862
44202
|
state.connectionName = connectionName || '';
|
|
43863
44203
|
|
|
44204
|
+
state.isOriginAllowed = typeof isOriginAllowed === 'function' ? isOriginAllowed : undefined;
|
|
43864
44205
|
state.originProvider = originProvider || (() => window.location.origin || '*');
|
|
43865
44206
|
state.destinationProvider = destinationProvider || (() => window.parent || window.top);
|
|
43866
44207
|
|
|
@@ -43936,7 +44277,11 @@ function VocPortal() {
|
|
|
43936
44277
|
pluginAPI = PluginAPI;
|
|
43937
44278
|
const { frameId } = PluginAPI.EventTracer.addTracerIds({});
|
|
43938
44279
|
this.removeResizeEvent = pendoGlobal.attachEvent(window, 'resize', resizePortalIframe);
|
|
43939
|
-
startListening('VocPortal', frameId, routeHandlerLookup,
|
|
44280
|
+
startListening('VocPortal', frameId, routeHandlerLookup, undefined, // originProvider, unused: replies target the origin that asked
|
|
44281
|
+
undefined, // destinationProvider
|
|
44282
|
+
undefined, // usePingAutoReconnect
|
|
44283
|
+
undefined, // connectionName
|
|
44284
|
+
pluginAPI.isTrustedOrigin);
|
|
43940
44285
|
try {
|
|
43941
44286
|
observePortalContainer();
|
|
43942
44287
|
}
|
|
@@ -50662,6 +51007,113 @@ class Transport {
|
|
|
50662
51007
|
}
|
|
50663
51008
|
}
|
|
50664
51009
|
|
|
51010
|
+
const CATCHALL_TYPES = ['load', 'click', 'track'];
|
|
51011
|
+
const EVENT_KINDS = {
|
|
51012
|
+
catchall: 'catchall',
|
|
51013
|
+
devlog: 'devlog',
|
|
51014
|
+
recording: 'recording'
|
|
51015
|
+
};
|
|
51016
|
+
class LiveReplayClient {
|
|
51017
|
+
constructor(pendo, api) {
|
|
51018
|
+
this.pendo = pendo;
|
|
51019
|
+
this.api = api;
|
|
51020
|
+
this.ws = null;
|
|
51021
|
+
this.sequence = 0;
|
|
51022
|
+
this.unsubscribeEventCaptured = null;
|
|
51023
|
+
}
|
|
51024
|
+
buildWsUrl() {
|
|
51025
|
+
const url = new URL$1(`${this.pendo.HOST}/data/live-replay/${this.pendo.apiKey}`);
|
|
51026
|
+
if (url.protocol === 'http:') {
|
|
51027
|
+
url.protocol = 'ws:';
|
|
51028
|
+
}
|
|
51029
|
+
else if (url.protocol === 'https:') {
|
|
51030
|
+
url.protocol = 'wss:';
|
|
51031
|
+
}
|
|
51032
|
+
return url.href;
|
|
51033
|
+
}
|
|
51034
|
+
start(visitorId, onOpen) {
|
|
51035
|
+
if (this.ws && this.ws.readyState !== WebSocket.CLOSED)
|
|
51036
|
+
return;
|
|
51037
|
+
this.sequence = 0;
|
|
51038
|
+
this.unsubscribeEventCaptured = this.api.attachEvent(this.api.Events, 'eventCaptured', this.pendo._.bind(this.forwardEvent, this));
|
|
51039
|
+
this.ws = new WebSocket(this.buildWsUrl());
|
|
51040
|
+
this.ws.onopen = this.pendo._.bind(this.handleOpen, this, visitorId, onOpen);
|
|
51041
|
+
const boundHandleClose = this.pendo._.bind(this.handleClose, this);
|
|
51042
|
+
this.ws.onclose = boundHandleClose;
|
|
51043
|
+
this.ws.onerror = boundHandleClose;
|
|
51044
|
+
}
|
|
51045
|
+
handleOpen(visitorId, onOpen) {
|
|
51046
|
+
this.sendAuth(visitorId);
|
|
51047
|
+
if (onOpen)
|
|
51048
|
+
onOpen();
|
|
51049
|
+
}
|
|
51050
|
+
handleClose() {
|
|
51051
|
+
this.unsubscribeFromEvents();
|
|
51052
|
+
}
|
|
51053
|
+
unsubscribeFromEvents() {
|
|
51054
|
+
if (!this.unsubscribeEventCaptured)
|
|
51055
|
+
return;
|
|
51056
|
+
this.unsubscribeEventCaptured();
|
|
51057
|
+
this.unsubscribeEventCaptured = null;
|
|
51058
|
+
}
|
|
51059
|
+
sendAuth(visitorId) {
|
|
51060
|
+
if (!this.isOpen())
|
|
51061
|
+
return;
|
|
51062
|
+
const jwtOptions = this.api.agent.getJwtInfoCopy();
|
|
51063
|
+
this.ws.send(JSON.stringify(this.pendo._.extend({
|
|
51064
|
+
type: 'auth',
|
|
51065
|
+
visitorId
|
|
51066
|
+
}, jwtOptions)));
|
|
51067
|
+
}
|
|
51068
|
+
stop() {
|
|
51069
|
+
if (this.ws) {
|
|
51070
|
+
this.ws.close();
|
|
51071
|
+
this.ws = null;
|
|
51072
|
+
}
|
|
51073
|
+
this.unsubscribeFromEvents();
|
|
51074
|
+
}
|
|
51075
|
+
isOpen() {
|
|
51076
|
+
return !!this.ws && this.ws.readyState === WebSocket.OPEN;
|
|
51077
|
+
}
|
|
51078
|
+
onLiveFlag(live, visitorId, onOpen) {
|
|
51079
|
+
if (live) {
|
|
51080
|
+
this.start(visitorId, onOpen);
|
|
51081
|
+
}
|
|
51082
|
+
else {
|
|
51083
|
+
this.stop();
|
|
51084
|
+
}
|
|
51085
|
+
}
|
|
51086
|
+
send(payload) {
|
|
51087
|
+
if (!this.isOpen())
|
|
51088
|
+
return;
|
|
51089
|
+
try {
|
|
51090
|
+
this.ws.send(this.pendo.compress(payload, 'binary'));
|
|
51091
|
+
}
|
|
51092
|
+
catch (_a) { }
|
|
51093
|
+
}
|
|
51094
|
+
sendRecordingEvent(fields) {
|
|
51095
|
+
this.send(Object.assign(Object.assign({}, fields), { kind: EVENT_KINDS.recording, sequence: this.sequence++ }));
|
|
51096
|
+
}
|
|
51097
|
+
forwardEvent(event) {
|
|
51098
|
+
if (!event || !event.data || !event.data.length)
|
|
51099
|
+
return;
|
|
51100
|
+
const capturedEvent = event.data[0];
|
|
51101
|
+
const config = { event: capturedEvent };
|
|
51102
|
+
if (this.pendo._.contains(CATCHALL_TYPES, capturedEvent.type)) {
|
|
51103
|
+
config.kind = EVENT_KINDS.catchall;
|
|
51104
|
+
}
|
|
51105
|
+
if (capturedEvent.type === EVENT_KINDS.devlog) {
|
|
51106
|
+
config.kind = EVENT_KINDS.devlog;
|
|
51107
|
+
}
|
|
51108
|
+
if (!config.kind)
|
|
51109
|
+
return;
|
|
51110
|
+
// other eventCaptured listeners still mutate capturedEvent in place; defer until they're done
|
|
51111
|
+
this.pendo._.defer(() => {
|
|
51112
|
+
this.send(config);
|
|
51113
|
+
});
|
|
51114
|
+
}
|
|
51115
|
+
}
|
|
51116
|
+
|
|
50665
51117
|
const ELEMENT_NODE = 1;
|
|
50666
51118
|
const INPUT_MASK = '*'.repeat(10);
|
|
50667
51119
|
const NUMBER_INPUT_MASK = '0'.repeat(10);
|
|
@@ -50907,6 +51359,7 @@ class SessionRecorder {
|
|
|
50907
51359
|
this.transport = new Transport(this.WorkerClass, this.pendo, this.api);
|
|
50908
51360
|
this.transport.onError = bind(this.sendFailure, this, 'WORKER_ERROR');
|
|
50909
51361
|
this.transport.onWorkerMessage = bind(this.onWorkerMessage, this);
|
|
51362
|
+
this.liveReplayClient = new LiveReplayClient(this.pendo, this.api);
|
|
50910
51363
|
this.sendQueue = new this.api.SendQueue(bind(this.transport.send, this.transport));
|
|
50911
51364
|
this.sendQueue.onTimeout = bind(this.sendFailure, this, 'SEND_TIMEOUT');
|
|
50912
51365
|
this.errorHandler = this.pendo._.throttle(bind(this.logStopReason, this, 'RRWEB_ERROR'), ONE_MINUTE_IN_MILLISECONDS, { trailing: false });
|
|
@@ -50997,6 +51450,7 @@ class SessionRecorder {
|
|
|
50997
51450
|
this.subscriptions.length = 0;
|
|
50998
51451
|
this.stop();
|
|
50999
51452
|
this.transport.stop();
|
|
51453
|
+
this.liveReplayClient.stop();
|
|
51000
51454
|
}
|
|
51001
51455
|
ready() {
|
|
51002
51456
|
if (this.api.ConfigReader.get(RECORDING_CONFIG_AUTO_START) !== false) {
|
|
@@ -51071,6 +51525,7 @@ class SessionRecorder {
|
|
|
51071
51525
|
this.stop();
|
|
51072
51526
|
}
|
|
51073
51527
|
if (visitorConfig && visitorConfig.enable && this.isRecording()) {
|
|
51528
|
+
this.liveReplayClient.onLiveFlag(visitorConfig.live, this.visitorId, () => this.snapshot());
|
|
51074
51529
|
if (continuationCallback) {
|
|
51075
51530
|
continuationCallback.call(this);
|
|
51076
51531
|
}
|
|
@@ -51213,6 +51668,7 @@ class SessionRecorder {
|
|
|
51213
51668
|
var bind = this.pendo._.bind;
|
|
51214
51669
|
this.visitorConfig = visitorConfig;
|
|
51215
51670
|
this.clearOldSessionId();
|
|
51671
|
+
this.liveReplayClient.onLiveFlag(visitorConfig.live, this.visitorId);
|
|
51216
51672
|
this.sendQueue.start();
|
|
51217
51673
|
this.interval = setInterval(() => {
|
|
51218
51674
|
if (!this.sendQueue.failed()) {
|
|
@@ -51411,9 +51867,11 @@ class SessionRecorder {
|
|
|
51411
51867
|
this.sessionId(this.recordingId);
|
|
51412
51868
|
this.restartPtm();
|
|
51413
51869
|
this._sendIds();
|
|
51870
|
+
this.pendo._.each(this.buffer.data, (bufferedEvent) => this._sendLiveReplayEvent(bufferedEvent));
|
|
51414
51871
|
this.send({ keyframe: true });
|
|
51415
51872
|
}
|
|
51416
51873
|
else if (!isMeta) {
|
|
51874
|
+
this._sendLiveReplayEvent(event);
|
|
51417
51875
|
this.eventsSinceLastKeyFrame += 1;
|
|
51418
51876
|
const timeSinceLastKeyFrame = event.timestamp - this.lastKeyFrameTime;
|
|
51419
51877
|
const exceeds24Hours = timeSinceLastKeyFrame >= ONE_DAY_IN_MILLISECONDS;
|
|
@@ -51437,6 +51895,23 @@ class SessionRecorder {
|
|
|
51437
51895
|
updateCurrentRecordingSize(recordingPayloadSize) {
|
|
51438
51896
|
this.currentRecordingSize += recordingPayloadSize;
|
|
51439
51897
|
}
|
|
51898
|
+
_sendLiveReplayEvent(event) {
|
|
51899
|
+
if (!this.recordingId || !this.liveReplayClient.isOpen())
|
|
51900
|
+
return;
|
|
51901
|
+
this.liveReplayClient.sendRecordingEvent({
|
|
51902
|
+
type: 'recording',
|
|
51903
|
+
visitorId: this.visitorId,
|
|
51904
|
+
accountId: this.accountId,
|
|
51905
|
+
recordingId: this.recordingId,
|
|
51906
|
+
recordingSessionId: this.sessionId(this.recordingId),
|
|
51907
|
+
url: this.pendo.url.get(),
|
|
51908
|
+
keyFrame: event.type === EVENT_TYPES.FULL_SNAPSHOT,
|
|
51909
|
+
lastKeyFrame: this.lastKeyFrameTime,
|
|
51910
|
+
recordingPayload: [event],
|
|
51911
|
+
recordingPayloadCount: 1,
|
|
51912
|
+
browserTime: event.timestamp
|
|
51913
|
+
});
|
|
51914
|
+
}
|
|
51440
51915
|
onWorkerMessage(messageData) {
|
|
51441
51916
|
switch (messageData.type) {
|
|
51442
51917
|
case 'workerDied': {
|
|
@@ -51471,6 +51946,13 @@ class SessionRecorder {
|
|
|
51471
51946
|
this.logStopReason(`MISSING_${messageData.missingData}`);
|
|
51472
51947
|
break;
|
|
51473
51948
|
}
|
|
51949
|
+
case 'liveFlag': {
|
|
51950
|
+
// ignore stale responses from a since-changed identity
|
|
51951
|
+
if (messageData.visitorId !== this.visitorId)
|
|
51952
|
+
break;
|
|
51953
|
+
this.liveReplayClient.onLiveFlag(messageData.live, this.visitorId, () => this.snapshot());
|
|
51954
|
+
break;
|
|
51955
|
+
}
|
|
51474
51956
|
}
|
|
51475
51957
|
}
|
|
51476
51958
|
addRecordingId(event) {
|
|
@@ -51509,6 +51991,7 @@ class SessionRecorder {
|
|
|
51509
51991
|
}
|
|
51510
51992
|
// send existing events
|
|
51511
51993
|
this.send();
|
|
51994
|
+
this.liveReplayClient.stop();
|
|
51512
51995
|
// now, finish stopping any more collection or sending
|
|
51513
51996
|
this.sendQueue.stop();
|
|
51514
51997
|
this.recordingId = null;
|
|
@@ -51578,7 +52061,8 @@ class SessionRecorder {
|
|
|
51578
52061
|
this.api.Events.eventCaptured.trigger(payload);
|
|
51579
52062
|
const params = {
|
|
51580
52063
|
v: this.pendo.VERSION,
|
|
51581
|
-
recordingId: this.recordingId
|
|
52064
|
+
recordingId: this.recordingId,
|
|
52065
|
+
vis: this.api.util.base64EncodeString(this.visitorId)
|
|
51582
52066
|
};
|
|
51583
52067
|
if (payload.keyFrame) {
|
|
51584
52068
|
params.kf = 1;
|
|
@@ -51971,19 +52455,27 @@ var WorkerFactory = /*#__PURE__*/createInlineWorkerFactory(/* rollup-plugin-web-
|
|
|
51971
52455
|
method: 'POST',
|
|
51972
52456
|
body
|
|
51973
52457
|
}).then(function (response) {
|
|
51974
|
-
cancelResponseBody(response);
|
|
51975
52458
|
if (response.status < 200 || response.status >= 300) {
|
|
52459
|
+
cancelResponseBody(response);
|
|
51976
52460
|
postMessage({
|
|
51977
52461
|
error: new Error(`received status code ${response.status}: ${response.statusText}`),
|
|
51978
52462
|
status: response.status,
|
|
51979
52463
|
sequence
|
|
51980
52464
|
});
|
|
52465
|
+
return;
|
|
51981
52466
|
}
|
|
51982
|
-
|
|
51983
|
-
|
|
51984
|
-
|
|
51985
|
-
|
|
51986
|
-
|
|
52467
|
+
postMessage({
|
|
52468
|
+
sequence
|
|
52469
|
+
});
|
|
52470
|
+
response.json().then(function (responseBody) {
|
|
52471
|
+
if (responseBody && responseBody.live !== undefined) {
|
|
52472
|
+
postMessage({
|
|
52473
|
+
type: 'liveFlag',
|
|
52474
|
+
live: responseBody.live,
|
|
52475
|
+
visitorId: payload.visitorId
|
|
52476
|
+
});
|
|
52477
|
+
}
|
|
52478
|
+
}).catch(function () { });
|
|
51987
52479
|
}).catch(function (e) {
|
|
51988
52480
|
postMessage({
|
|
51989
52481
|
error: e,
|
|
@@ -55467,7 +55959,7 @@ var underscore = {exports: {}};
|
|
|
55467
55959
|
var underscoreExports = underscore.exports;
|
|
55468
55960
|
var _ = /*@__PURE__*/getDefaultExportFromCjs(underscoreExports);
|
|
55469
55961
|
|
|
55470
|
-
var encodeURIComponent = _.isFunction(
|
|
55962
|
+
var encodeURIComponent = _.isFunction(globalScope.encodeURIComponent) ? globalScope.encodeURIComponent : _.identity;
|
|
55471
55963
|
|
|
55472
55964
|
const WIDGET_SETTINGS_API = '/external/widget/settings';
|
|
55473
55965
|
class PredictApiService {
|