@pendo/agent 2.338.2 → 2.339.1
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 +1 -1
- package/dist/pendo.debugger.min.js +10 -10
- package/dist/pendo.module.js +1325 -817
- 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
|
@@ -4046,8 +4046,8 @@ let SERVER = '';
|
|
|
4046
4046
|
let ASSET_HOST = '';
|
|
4047
4047
|
let ASSET_PATH = '';
|
|
4048
4048
|
let DESIGNER_SERVER = '';
|
|
4049
|
-
let VERSION = '2.
|
|
4050
|
-
let PACKAGE_VERSION = '2.
|
|
4049
|
+
let VERSION = '2.339.1_';
|
|
4050
|
+
let PACKAGE_VERSION = '2.339.1';
|
|
4051
4051
|
let LOADER = 'xhr';
|
|
4052
4052
|
/* eslint-enable web-sdk-eslint-rules/no-gulp-env-references */
|
|
4053
4053
|
/**
|
|
@@ -4528,7 +4528,7 @@ function createStorageShim(storageAccessor) {
|
|
|
4528
4528
|
return fake;
|
|
4529
4529
|
}
|
|
4530
4530
|
}
|
|
4531
|
-
var pendoLocalStorage$
|
|
4531
|
+
var pendoLocalStorage$3 = createStorageShim(function () { return window.localStorage; });
|
|
4532
4532
|
var pendoSessionStorage$2 = createStorageShim(function () { return window.sessionStorage; });
|
|
4533
4533
|
|
|
4534
4534
|
/**
|
|
@@ -4916,7 +4916,7 @@ var agentStorage = (function () {
|
|
|
4916
4916
|
}
|
|
4917
4917
|
const wrappedPendoSessionStorage = _.extend({}, pendoSessionStorage$2);
|
|
4918
4918
|
wrappedPendoSessionStorage.setItem = wrapStorageWriteMethod(wrappedPendoSessionStorage.setItem, 'hasSession');
|
|
4919
|
-
const wrappedPendoLocalStorage = _.extend({}, pendoLocalStorage$
|
|
4919
|
+
const wrappedPendoLocalStorage = _.extend({}, pendoLocalStorage$3);
|
|
4920
4920
|
wrappedPendoLocalStorage.setItem = wrapStorageWriteMethod(wrappedPendoLocalStorage.setItem, 'hasLocal');
|
|
4921
4921
|
return {
|
|
4922
4922
|
/* API */
|
|
@@ -11826,7 +11826,7 @@ _.extend(dom.prototype, DomEvent.$, DomQuery.$);
|
|
|
11826
11826
|
|
|
11827
11827
|
// Domains that Pendo owns, will be swapped in by build patches
|
|
11828
11828
|
const pendoDomainPatterns = [
|
|
11829
|
-
/^https:\/\/((adopt|oem)\.)?((us1)\.)?(app|via|adopt|cdn|oem|novus-api|novus)(\.(au|eu|gov|hsbc|jpn|tv))?\.pendo\.io$/,
|
|
11829
|
+
/^https:\/\/((adopt|oem)\.)?((us1)\.)?(app|via|adopt|cdn|oem|novus-api|novus|portal)(\.(au|eu|gov|hsbc|jpn|tv))?\.pendo\.io$/,
|
|
11830
11830
|
/^https:\/\/([0-9]{8}t[0-9]{4}-dot-)(pendo-au|pendo-eu|pendo-govramp|pendo-hsbc|pendo-io|pendo-jp-prod|pendo-tv|pendo-us1)\.appspot\.com$/,
|
|
11831
11831
|
/^https:\/\/hotfix-(ops|app)-([0-9]+-dot-)(pendo-au|pendo-eu|pendo-govramp|pendo-hsbc|pendo-io|pendo-jp-prod|pendo-tv|pendo-us1)\.appspot\.com$/,
|
|
11832
11832
|
/^https:\/\/(pendo-au|pendo-eu|pendo-govramp|pendo-hsbc|pendo-io|pendo-jp-prod|pendo-tv|pendo-us1)-static\.storage\.googleapis\.com$/
|
|
@@ -20548,6 +20548,23 @@ var GuideStateModule = (function () {
|
|
|
20548
20548
|
};
|
|
20549
20549
|
})();
|
|
20550
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
|
+
|
|
20551
20568
|
var AutoDisplay = (function () {
|
|
20552
20569
|
return {
|
|
20553
20570
|
canDisplay,
|
|
@@ -20631,24 +20648,6 @@ var AutoDisplay = (function () {
|
|
|
20631
20648
|
var prioritizeAdopt = ConfigReader.get('adoptPrioritizeAdoptGuides');
|
|
20632
20649
|
return prioritizeAdoptOrCoreGuides(sortedGuides, prioritizeAdopt);
|
|
20633
20650
|
}
|
|
20634
|
-
function prioritizeAdoptOrCoreGuides(guides, shouldPrioritizeAdopt) {
|
|
20635
|
-
var adoptGuides = [];
|
|
20636
|
-
var coreGuides = [];
|
|
20637
|
-
_.each(guides, function (guide) {
|
|
20638
|
-
if (guide.isTraining) {
|
|
20639
|
-
adoptGuides.push(guide);
|
|
20640
|
-
}
|
|
20641
|
-
else {
|
|
20642
|
-
coreGuides.push(guide);
|
|
20643
|
-
}
|
|
20644
|
-
});
|
|
20645
|
-
if (shouldPrioritizeAdopt) {
|
|
20646
|
-
return adoptGuides.concat(coreGuides);
|
|
20647
|
-
}
|
|
20648
|
-
else {
|
|
20649
|
-
return coreGuides.concat(adoptGuides);
|
|
20650
|
-
}
|
|
20651
|
-
}
|
|
20652
20651
|
function lastDismissedTime(pendo) {
|
|
20653
20652
|
return Math.max(pendo.latestDismissedAutoAt || -Infinity, pendo.finalAdvancedAutoAt || -Infinity, pendo.latestSnoozedAutoAt || -Infinity);
|
|
20654
20653
|
}
|
|
@@ -20858,642 +20857,6 @@ class AutoDisplayPhase {
|
|
|
20858
20857
|
}
|
|
20859
20858
|
}
|
|
20860
20859
|
|
|
20861
|
-
/*
|
|
20862
|
-
* Guide Loop
|
|
20863
|
-
*
|
|
20864
|
-
* The function that gets called to process and handle the ever
|
|
20865
|
-
* changing landscape of a web app to best handle showing / hiding of
|
|
20866
|
-
* guides as needed.
|
|
20867
|
-
*/
|
|
20868
|
-
/*
|
|
20869
|
-
* Holds the timeout handle for the startGuides process.
|
|
20870
|
-
* This should work as a simple monitor on that process
|
|
20871
|
-
* to prevent it from being run too often.
|
|
20872
|
-
*/
|
|
20873
|
-
var guidesProcessingThreadHandle = null;
|
|
20874
|
-
var DEFAULT_TIMER_LENGTH = 500;
|
|
20875
|
-
var waitThenLoop = function (delayAmount) {
|
|
20876
|
-
delayAmount = delayAmount || DEFAULT_TIMER_LENGTH;
|
|
20877
|
-
guidesProcessingThreadHandle = _.delay(function () {
|
|
20878
|
-
guidesProcessingThreadHandle = null;
|
|
20879
|
-
startGuides();
|
|
20880
|
-
}, delayAmount);
|
|
20881
|
-
pendo$1.guidesProcessingThreadHandle = guidesProcessingThreadHandle;
|
|
20882
|
-
};
|
|
20883
|
-
var clearLoopTimer = function () {
|
|
20884
|
-
if (guidesProcessingThreadHandle) {
|
|
20885
|
-
clearTimeout(guidesProcessingThreadHandle);
|
|
20886
|
-
guidesProcessingThreadHandle = null;
|
|
20887
|
-
pendo$1.guidesProcessingThreadHandle = guidesProcessingThreadHandle;
|
|
20888
|
-
}
|
|
20889
|
-
store.dispatch('guideUpdate/stopScheduledUpdate');
|
|
20890
|
-
};
|
|
20891
|
-
/**
|
|
20892
|
-
* Clears any showing guides and prevents any loaded guides from rendering.
|
|
20893
|
-
* Loaded guides remain in memory, so calling startGuides will work just fine
|
|
20894
|
-
* after a stopGuides call.
|
|
20895
|
-
*
|
|
20896
|
-
* @access public
|
|
20897
|
-
* @category Guides
|
|
20898
|
-
* @example
|
|
20899
|
-
* pendo.stopGuides()
|
|
20900
|
-
*/
|
|
20901
|
-
var stopGuides = function () {
|
|
20902
|
-
hideGuides();
|
|
20903
|
-
updateRunner.cancel();
|
|
20904
|
-
// Remove other stuff.
|
|
20905
|
-
for (var i = 0; i < activeElements.length; i++) {
|
|
20906
|
-
var elem = activeElements[i];
|
|
20907
|
-
elem.parentNode.removeChild(elem);
|
|
20908
|
-
}
|
|
20909
|
-
activeElements.length = 0;
|
|
20910
|
-
store.dispatch('guideUpdate/stopObserver');
|
|
20911
|
-
Events.guideLoopStopped.trigger();
|
|
20912
|
-
};
|
|
20913
|
-
/**
|
|
20914
|
-
* Starts the process that loops over all currently loaded guides and
|
|
20915
|
-
* determines what to show. Checks multi-step guide continuation,
|
|
20916
|
-
* auto-display guides, launcher guides, and badges.
|
|
20917
|
-
*
|
|
20918
|
-
* @access public
|
|
20919
|
-
* @category Guides
|
|
20920
|
-
* @example
|
|
20921
|
-
* pendo.startGuides()
|
|
20922
|
-
*/
|
|
20923
|
-
var startGuides = function () {
|
|
20924
|
-
clearLoopTimer();
|
|
20925
|
-
if (isGuideRequestPending()) {
|
|
20926
|
-
log.info('guides are loading.', { contexts: ['guides', 'loading'] });
|
|
20927
|
-
return;
|
|
20928
|
-
}
|
|
20929
|
-
if (areGuidesDisabled()) {
|
|
20930
|
-
log.info('guides are disabled.', { contexts: ['guides', 'disabled'] });
|
|
20931
|
-
return;
|
|
20932
|
-
}
|
|
20933
|
-
if (areGuidesDelayed()) {
|
|
20934
|
-
log.info('guides are delayed.', { contexts: ['guides', 'delayed'] });
|
|
20935
|
-
return;
|
|
20936
|
-
}
|
|
20937
|
-
if (store.getters['guideState/tabLostFocus']())
|
|
20938
|
-
return;
|
|
20939
|
-
try {
|
|
20940
|
-
const activeGuides = getActiveGuides();
|
|
20941
|
-
if (!activeGuides || activeGuides.length === 0)
|
|
20942
|
-
return;
|
|
20943
|
-
store.dispatch('guideUpdate/startObserverIfNeeded');
|
|
20944
|
-
if (updateRunner.isComplete()) {
|
|
20945
|
-
updateRunner.markDirty();
|
|
20946
|
-
}
|
|
20947
|
-
if (leaderExists()) {
|
|
20948
|
-
updateRunner.process();
|
|
20949
|
-
}
|
|
20950
|
-
}
|
|
20951
|
-
catch (e) {
|
|
20952
|
-
updateRunner.cancel();
|
|
20953
|
-
log.critical('ERROR in guide-loop', { error: e });
|
|
20954
|
-
}
|
|
20955
|
-
finally {
|
|
20956
|
-
if (!updateRunner.paused) {
|
|
20957
|
-
continueOrCompleteUpdate();
|
|
20958
|
-
}
|
|
20959
|
-
}
|
|
20960
|
-
};
|
|
20961
|
-
function continueOrCompleteUpdate() {
|
|
20962
|
-
if (!updateRunner.isComplete()) {
|
|
20963
|
-
waitThenLoop(50);
|
|
20964
|
-
}
|
|
20965
|
-
else {
|
|
20966
|
-
if (!store.state.guideUpdate.useObserver || store.state.guideUpdate.needsUpdate) {
|
|
20967
|
-
waitThenLoop();
|
|
20968
|
-
}
|
|
20969
|
-
Events.guideUpdateComplete.trigger();
|
|
20970
|
-
}
|
|
20971
|
-
}
|
|
20972
|
-
/*
|
|
20973
|
-
* If delayGuides is true, removes the delay and starts guides.
|
|
20974
|
-
* Otherwise, it is the same as calling startGuides.
|
|
20975
|
-
*/
|
|
20976
|
-
var manuallyStartGuides = function () {
|
|
20977
|
-
setGuidesDelayed(false);
|
|
20978
|
-
startGuides();
|
|
20979
|
-
};
|
|
20980
|
-
var manuallyStopGuides = function () {
|
|
20981
|
-
if (!areGuidesDisabled()) {
|
|
20982
|
-
setGuidesDelayed(true);
|
|
20983
|
-
}
|
|
20984
|
-
resetPendoUI();
|
|
20985
|
-
};
|
|
20986
|
-
// a phase that only has one unit of work per update
|
|
20987
|
-
class SimplePhase {
|
|
20988
|
-
constructor(name, processFn) {
|
|
20989
|
-
this.dirty = false;
|
|
20990
|
-
this.name = name;
|
|
20991
|
-
this.processFn = _.isFunction(processFn) ? processFn : _.noop;
|
|
20992
|
-
}
|
|
20993
|
-
markDirty() {
|
|
20994
|
-
this.dirty = true;
|
|
20995
|
-
}
|
|
20996
|
-
process() {
|
|
20997
|
-
this.processFn();
|
|
20998
|
-
this.dirty = false;
|
|
20999
|
-
}
|
|
21000
|
-
cancel() {
|
|
21001
|
-
this.dirty = false;
|
|
21002
|
-
}
|
|
21003
|
-
isComplete() {
|
|
21004
|
-
return !this.dirty;
|
|
21005
|
-
}
|
|
21006
|
-
}
|
|
21007
|
-
/**
|
|
21008
|
-
* Runs on a timer whenever a guide is displayed.
|
|
21009
|
-
*
|
|
21010
|
-
* @access private
|
|
21011
|
-
* @param {Guide} guide The currently visible guide
|
|
21012
|
-
* @param {GuideStep} step The currently visible step
|
|
21013
|
-
*/
|
|
21014
|
-
function guideShowingProc() {
|
|
21015
|
-
const shownGuides = _.compact(_.map(_.filter(frameLocalGuides(getActiveGuides()), (guide) => guide.isShown()), (guide) => {
|
|
21016
|
-
if (BuildingBlockResourceCenter.isModule(guide)) {
|
|
21017
|
-
return BuildingBlockResourceCenter.getResourceCenter();
|
|
21018
|
-
}
|
|
21019
|
-
else {
|
|
21020
|
-
return guide;
|
|
21021
|
-
}
|
|
21022
|
-
}));
|
|
21023
|
-
_.each(shownGuides, function (guide) {
|
|
21024
|
-
_.each(guide.steps, function (step) {
|
|
21025
|
-
if (step.isShown()) {
|
|
21026
|
-
stepShowingProc(guide, step);
|
|
21027
|
-
}
|
|
21028
|
-
});
|
|
21029
|
-
guide.checkForHiddenGroupSteps();
|
|
21030
|
-
});
|
|
21031
|
-
}
|
|
21032
|
-
function stepShowingProc(guide, step) {
|
|
21033
|
-
if (step.isLocked()) {
|
|
21034
|
-
return;
|
|
21035
|
-
}
|
|
21036
|
-
if (store.getters['frames/isShownInAnotherFrame']()(step)) {
|
|
21037
|
-
return;
|
|
21038
|
-
}
|
|
21039
|
-
var element = step.element;
|
|
21040
|
-
var targetElement = step.targetElement;
|
|
21041
|
-
var guideElement = step.guideElement;
|
|
21042
|
-
var guideElm = dom('.' + GUIDE_CSS_NAME);
|
|
21043
|
-
// As part of the guides accessibility work, we need to make sure that all
|
|
21044
|
-
// guide content remain visible and/or accessible during text-only (font) zoom.
|
|
21045
|
-
// Since text zoom does NOT trigger our existing resize listener, the
|
|
21046
|
-
// alternative is to track text zoom via font-size changes.
|
|
21047
|
-
var guideContainer = guideElement.find('#pendo-guide-container')[0];
|
|
21048
|
-
var guideHasTextContent = guideContainer && guideContainer.textContent;
|
|
21049
|
-
if (guideHasTextContent) {
|
|
21050
|
-
var currentBrowserFontSize = getComputedStyle_safe(guideContainer).fontSize;
|
|
21051
|
-
var hasBrowserFontSizeChanged = step.attributes.currentTextZoomFontSize !== currentBrowserFontSize;
|
|
21052
|
-
if (hasBrowserFontSizeChanged) {
|
|
21053
|
-
if (step.attributes.currentTextZoomFontSize) {
|
|
21054
|
-
step.redraw();
|
|
21055
|
-
}
|
|
21056
|
-
step.attributes.currentTextZoomFontSize = currentBrowserFontSize;
|
|
21057
|
-
}
|
|
21058
|
-
}
|
|
21059
|
-
syncColorMode(step, true);
|
|
21060
|
-
if (step.elementPathRule && targetElement && !SizzleProxy.matchesSelector(targetElement, step.elementPathRule)) {
|
|
21061
|
-
step.hide();
|
|
21062
|
-
return;
|
|
21063
|
-
}
|
|
21064
|
-
const embedSelector = _.get(guide, 'attributes.embedConfig.selector');
|
|
21065
|
-
if (embedSelector && !dom(embedSelector).length) {
|
|
21066
|
-
step.hide();
|
|
21067
|
-
return;
|
|
21068
|
-
}
|
|
21069
|
-
var elementIsStillValidTarget = isElementVisible(element);
|
|
21070
|
-
var elementMatchesContainsRules = true;
|
|
21071
|
-
// targetElement is specifically the elementPathRule dom node
|
|
21072
|
-
// element is either the element path rule OR the badge element
|
|
21073
|
-
// we can rely on the isElementVisible check as a shortcut for having to call isElementVisible twice
|
|
21074
|
-
if (targetElement && elementIsStillValidTarget && step.elementContainsRulesList) {
|
|
21075
|
-
// Only bother trying to check element contains rules if the element is still visible
|
|
21076
|
-
elementMatchesContainsRules = doesElementMatchContainsRules(targetElement, step.elementContainsRulesList);
|
|
21077
|
-
elementIsStillValidTarget = elementMatchesContainsRules;
|
|
21078
|
-
}
|
|
21079
|
-
if (element && (elementIsStillValidTarget || guideElm.hasClass('mouseover'))) {
|
|
21080
|
-
step.checkPlacement();
|
|
21081
|
-
return;
|
|
21082
|
-
}
|
|
21083
|
-
var isTooltip = (step.type === 'tooltip' || step.attributes.calculatedType === 'tooltip');
|
|
21084
|
-
if (isTooltip && wouldBeVisibleAfterAutoScroll(element) && elementMatchesContainsRules) {
|
|
21085
|
-
// the element was scrolled out of view, the guide should be invisible for now, but still "active"
|
|
21086
|
-
return;
|
|
21087
|
-
}
|
|
21088
|
-
step.hide();
|
|
21089
|
-
}
|
|
21090
|
-
class BadgePhase {
|
|
21091
|
-
constructor() {
|
|
21092
|
-
this.name = 'badge';
|
|
21093
|
-
this.queue = [];
|
|
21094
|
-
}
|
|
21095
|
-
markDirty() {
|
|
21096
|
-
this.queue.push.apply(this.queue, _.filter(frameLocalGuides(getActiveGuides()), isBadge));
|
|
21097
|
-
}
|
|
21098
|
-
process(monitor) {
|
|
21099
|
-
var badge = this.queue.shift();
|
|
21100
|
-
monitor.saveContext(badge);
|
|
21101
|
-
if (badge) {
|
|
21102
|
-
badge.placeBadge();
|
|
21103
|
-
}
|
|
21104
|
-
}
|
|
21105
|
-
handleProcessTimeExceeded(monitor) {
|
|
21106
|
-
const badge = monitor.getContext();
|
|
21107
|
-
log.warn('Badge (' + badge.id + ') timeout exceeded');
|
|
21108
|
-
}
|
|
21109
|
-
cancel() {
|
|
21110
|
-
this.queue.length = 0;
|
|
21111
|
-
}
|
|
21112
|
-
isComplete() {
|
|
21113
|
-
return !this.queue.length;
|
|
21114
|
-
}
|
|
21115
|
-
}
|
|
21116
|
-
function resourceCenterShowingProc(resourceCenter, resourceCenterBadgeProc) {
|
|
21117
|
-
var reEvaluateModuleContent = false;
|
|
21118
|
-
// Find eligible modules
|
|
21119
|
-
var controlGuidesInAllModules = [];
|
|
21120
|
-
var eligibleModules = _.filter(resourceCenter.modules, function (module) {
|
|
21121
|
-
if (!module)
|
|
21122
|
-
return false;
|
|
21123
|
-
// Iframe modules can not dynamically show/hide.
|
|
21124
|
-
if (BuildingBlockResourceCenter.isIframeModule(module))
|
|
21125
|
-
return true;
|
|
21126
|
-
// Sandbox modules can not dynamically show/hide.
|
|
21127
|
-
if (BuildingBlockResourceCenter.isSandboxModule(module))
|
|
21128
|
-
return true;
|
|
21129
|
-
if (BuildingBlockResourceCenter.isIntegrationModule(module)) {
|
|
21130
|
-
if (!BuildingBlockResourceCenter.integrationModuleHasContent(module)) {
|
|
21131
|
-
module.hasResourceCenterContent = false;
|
|
21132
|
-
return false;
|
|
21133
|
-
}
|
|
21134
|
-
else {
|
|
21135
|
-
return true;
|
|
21136
|
-
}
|
|
21137
|
-
}
|
|
21138
|
-
var eligibleGuidesInModule = _.filter(module.guidesInModule, function (guide) {
|
|
21139
|
-
if (!guide.shouldBeAddedToResourceCenter()) {
|
|
21140
|
-
guide.ineligibleForRC = true;
|
|
21141
|
-
if (guide.control) {
|
|
21142
|
-
controlGuidesInAllModules.push(guide);
|
|
21143
|
-
}
|
|
21144
|
-
return false;
|
|
21145
|
-
}
|
|
21146
|
-
guide.ineligibleForRC = false;
|
|
21147
|
-
return true;
|
|
21148
|
-
});
|
|
21149
|
-
var isEligible = eligibleGuidesInModule.length;
|
|
21150
|
-
module.hasResourceCenterContent = !!isEligible;
|
|
21151
|
-
// We only need to hash the active module, no other module has content rendered
|
|
21152
|
-
if (resourceCenter.activeModule && module.id === resourceCenter.activeModule.id) {
|
|
21153
|
-
var moduleContentHash = crc32(_.map(eligibleGuidesInModule, function (guide) {
|
|
21154
|
-
// don't change content hash when announcements update their seen state, as that will cause
|
|
21155
|
-
// the RC to re-render (and lose scroll position)
|
|
21156
|
-
var isAnnouncementOrWhatsNew = guide.isAnnouncement() || guide.isWhatsNew();
|
|
21157
|
-
var seenState = isAnnouncementOrWhatsNew ? null : _.pluck(guide.steps, 'seenState');
|
|
21158
|
-
return {
|
|
21159
|
-
id: guide.id,
|
|
21160
|
-
seenState
|
|
21161
|
-
};
|
|
21162
|
-
}));
|
|
21163
|
-
if (module.eligibleGuidesInModuleHash && moduleContentHash !== module.eligibleGuidesInModuleHash)
|
|
21164
|
-
reEvaluateModuleContent = true;
|
|
21165
|
-
module.eligibleGuidesInModuleHash = moduleContentHash;
|
|
21166
|
-
}
|
|
21167
|
-
return isEligible;
|
|
21168
|
-
});
|
|
21169
|
-
// If we are rendering the home view and its modules change re-render it
|
|
21170
|
-
var reEvaluateHomeViewContent = false;
|
|
21171
|
-
if (!resourceCenter.activeModule) {
|
|
21172
|
-
var eligibleModulesHash = crc32(_.map(eligibleModules, function (module) {
|
|
21173
|
-
return {
|
|
21174
|
-
id: module.id
|
|
21175
|
-
};
|
|
21176
|
-
}));
|
|
21177
|
-
if (resourceCenter.eligibleModulesHash && resourceCenter.eligibleModulesHash !== eligibleModulesHash) {
|
|
21178
|
-
reEvaluateHomeViewContent = true;
|
|
21179
|
-
BuildingBlockResourceCenter.replaceResourceCenterContent(resourceCenter.id, undefined, true);
|
|
21180
|
-
}
|
|
21181
|
-
resourceCenter.eligibleModulesHash = eligibleModulesHash;
|
|
21182
|
-
}
|
|
21183
|
-
resourceCenterBadgeProc(resourceCenter, eligibleModules, controlGuidesInAllModules);
|
|
21184
|
-
// Skip directly to the one eligible module
|
|
21185
|
-
if (eligibleModules.length === 1) {
|
|
21186
|
-
resourceCenter.skipResourceCenterHomeView = true;
|
|
21187
|
-
resourceCenter.moduleIdToReplaceHomeViewWith = eligibleModules[0].id;
|
|
21188
|
-
var noActiveModule = !resourceCenter.activeModule && reEvaluateHomeViewContent;
|
|
21189
|
-
var differentActiveModule = resourceCenter.activeModule && resourceCenter.activeModule.id !== eligibleModules[0].id;
|
|
21190
|
-
// If the one module is different or content needs to be reevaluated, reload
|
|
21191
|
-
// or if we are on the home view and the modules on the home view have changed
|
|
21192
|
-
if (noActiveModule || differentActiveModule || reEvaluateModuleContent) {
|
|
21193
|
-
BuildingBlockResourceCenter.replaceResourceCenterContent(resourceCenter.moduleIdToReplaceHomeViewWith, undefined, true);
|
|
21194
|
-
}
|
|
21195
|
-
}
|
|
21196
|
-
if (eligibleModules) {
|
|
21197
|
-
// find the in-progress native integration module and set continueToNativeModule on both the
|
|
21198
|
-
// RC container and the module since both will try to render
|
|
21199
|
-
var continuedModule = _.find(eligibleModules, function (module) {
|
|
21200
|
-
return module.isContinuation(getLastGuideStepSeen());
|
|
21201
|
-
});
|
|
21202
|
-
var shouldNotResume = _.get(continuedModule, 'attributes.doNotResume');
|
|
21203
|
-
var integrationProvider = _.get(continuedModule, 'attributes.resourceCenter.integrationProvider');
|
|
21204
|
-
var isNativeModule = BuildingBlockResourceCenter.isSupportedNativeIntegration(integrationProvider);
|
|
21205
|
-
if (isNativeModule && !shouldNotResume && resourceCenter) {
|
|
21206
|
-
resourceCenter.continueToNativeModule = true;
|
|
21207
|
-
continuedModule.continueToNativeModule = true;
|
|
21208
|
-
}
|
|
21209
|
-
}
|
|
21210
|
-
// Ensure the active module is still eligible and re-evaluate its content if need be.
|
|
21211
|
-
// Go to Home View if active module is no longer eligible
|
|
21212
|
-
var activeModuleisEligible;
|
|
21213
|
-
if (resourceCenter.activeModule) {
|
|
21214
|
-
activeModuleisEligible = _.find(eligibleModules, function (module) {
|
|
21215
|
-
return module.id === resourceCenter.activeModule.id;
|
|
21216
|
-
});
|
|
21217
|
-
if (activeModuleisEligible && reEvaluateModuleContent) {
|
|
21218
|
-
BuildingBlockResourceCenter.replaceResourceCenterContent(resourceCenter.activeModule.id, undefined, true);
|
|
21219
|
-
}
|
|
21220
|
-
if (!activeModuleisEligible) {
|
|
21221
|
-
BuildingBlockResourceCenter.replaceResourceCenterContent(resourceCenter.id);
|
|
21222
|
-
}
|
|
21223
|
-
}
|
|
21224
|
-
BuildingBlockResourceCenter.repositionResourceCenter();
|
|
21225
|
-
}
|
|
21226
|
-
function resourceCenterNotShowingProc(resourceCenter, resourceCenterBadgeProc) {
|
|
21227
|
-
var eligibleModules = [];
|
|
21228
|
-
var controlGuidesInAllModules = [];
|
|
21229
|
-
var staticModule = _.find(resourceCenter.modules, function (module) {
|
|
21230
|
-
if (BuildingBlockResourceCenter.isIframeModule(module))
|
|
21231
|
-
return true;
|
|
21232
|
-
if (BuildingBlockResourceCenter.isSandboxModule(module))
|
|
21233
|
-
return true;
|
|
21234
|
-
if (BuildingBlockResourceCenter.isIntegrationModule(module)) {
|
|
21235
|
-
if (!BuildingBlockResourceCenter.integrationModuleHasContent(module)) {
|
|
21236
|
-
module.hasResourceCenterContent = false;
|
|
21237
|
-
return false;
|
|
21238
|
-
}
|
|
21239
|
-
else {
|
|
21240
|
-
return true;
|
|
21241
|
-
}
|
|
21242
|
-
}
|
|
21243
|
-
return false;
|
|
21244
|
-
});
|
|
21245
|
-
if (staticModule) {
|
|
21246
|
-
eligibleModules.push(staticModule);
|
|
21247
|
-
}
|
|
21248
|
-
var allGuidesInModules = _.flatten(_.pluck(resourceCenter.modules, 'guidesInModule'));
|
|
21249
|
-
controlGuidesInAllModules = _.filter(_.compact(allGuidesInModules), function (guide) {
|
|
21250
|
-
return guide.control;
|
|
21251
|
-
});
|
|
21252
|
-
if (!eligibleModules.length) {
|
|
21253
|
-
var moduleLookup = _.reduce(resourceCenter.modules, function (lookup, module) {
|
|
21254
|
-
_.each(module.guidesInModule, function (guide) {
|
|
21255
|
-
lookup[guide.id] = module;
|
|
21256
|
-
});
|
|
21257
|
-
return lookup;
|
|
21258
|
-
}, {});
|
|
21259
|
-
var guidesWithSteps = _.filter(allGuidesInModules, function (guide) {
|
|
21260
|
-
return guide && guide.steps && guide.steps.length;
|
|
21261
|
-
});
|
|
21262
|
-
// Sort guides so that untargeted guides are first
|
|
21263
|
-
var sortedGuides = _.sortBy(guidesWithSteps, function (guide) {
|
|
21264
|
-
return guide.steps[0].elementPathRule ? 1 : 0;
|
|
21265
|
-
});
|
|
21266
|
-
var firstEligibleGuide = _.find(sortedGuides, function (guide) {
|
|
21267
|
-
return guide.shouldBeAddedToResourceCenter();
|
|
21268
|
-
});
|
|
21269
|
-
var firstEligibleModule = moduleLookup[firstEligibleGuide && firstEligibleGuide.id];
|
|
21270
|
-
if (firstEligibleModule) {
|
|
21271
|
-
eligibleModules.push(firstEligibleModule);
|
|
21272
|
-
}
|
|
21273
|
-
}
|
|
21274
|
-
resourceCenterBadgeProc(resourceCenter, eligibleModules, controlGuidesInAllModules);
|
|
21275
|
-
}
|
|
21276
|
-
function resourceCenterBadgeProc(resourceCenter, eligibleModules, controlGuidesInAllModules) {
|
|
21277
|
-
var RCBadge = badgesShown[resourceCenter.id];
|
|
21278
|
-
// If we get no modules back, hide the RC and RC Badge, clear active module
|
|
21279
|
-
if (!eligibleModules || !eligibleModules.length) {
|
|
21280
|
-
var isBadgeActivated = resourceCenter.launchMethod.indexOf('badge') !== -1;
|
|
21281
|
-
var isElementActivated = resourceCenter.launchMethod.indexOf('dom') !== -1;
|
|
21282
|
-
resourceCenter.hasResourceCenterContent = false;
|
|
21283
|
-
// If the RC is badge activated, hide it from the dom completely. Otherwise, leave it alone
|
|
21284
|
-
// This is important for element-activated RC, which should still display with an empty state
|
|
21285
|
-
// even if it has no content
|
|
21286
|
-
if (isBadgeActivated) {
|
|
21287
|
-
if (resourceCenter.isShown())
|
|
21288
|
-
resourceCenter.hide();
|
|
21289
|
-
if (RCBadge) {
|
|
21290
|
-
BuildingBlockResourceCenter.hideRCBadge(RCBadge, resourceCenter);
|
|
21291
|
-
}
|
|
21292
|
-
// If all modules are not eligible because of control group guides
|
|
21293
|
-
// Send guideNotSeen events where appropriate
|
|
21294
|
-
_.each(controlGuidesInAllModules, function (guide) {
|
|
21295
|
-
if (!guide.hasGuideBeenControlled()) {
|
|
21296
|
-
guide.steps[0].onControlGuide('control');
|
|
21297
|
-
}
|
|
21298
|
-
});
|
|
21299
|
-
resourceCenter.badgeHidden = true;
|
|
21300
|
-
delete resourceCenter.activeModule;
|
|
21301
|
-
}
|
|
21302
|
-
else if (isElementActivated) {
|
|
21303
|
-
resourceCenter.showEmptyState = true;
|
|
21304
|
-
resourceCenter.controlGuidesInAllModules = controlGuidesInAllModules;
|
|
21305
|
-
}
|
|
21306
|
-
}
|
|
21307
|
-
// Show the badge if it was hidden but now has content
|
|
21308
|
-
if (resourceCenter.badgeHidden && resourceCenter.hasResourceCenterContent && !resourceCenter.isNativeModuleActive) {
|
|
21309
|
-
if (!RCBadge) {
|
|
21310
|
-
if (_.isFunction(resourceCenter.placeBadge))
|
|
21311
|
-
resourceCenter.placeBadge();
|
|
21312
|
-
RCBadge = badgesShown[resourceCenter.id];
|
|
21313
|
-
}
|
|
21314
|
-
if (RCBadge)
|
|
21315
|
-
RCBadge.show();
|
|
21316
|
-
resourceCenter.badgeHidden = false;
|
|
21317
|
-
}
|
|
21318
|
-
}
|
|
21319
|
-
function resourceCenterProc(resourceCenter) {
|
|
21320
|
-
if (!resourceCenter || !resourceCenter.launchMethod)
|
|
21321
|
-
return;
|
|
21322
|
-
// Reset state to start each loop
|
|
21323
|
-
resourceCenter.skipResourceCenterHomeView = false;
|
|
21324
|
-
resourceCenter.hasResourceCenterContent = true;
|
|
21325
|
-
delete resourceCenter.moduleIdToReplaceHomeViewWith;
|
|
21326
|
-
var activeModule = resourceCenter.activeModule;
|
|
21327
|
-
var activeIntegrationProvider = _.get(activeModule, 'attributes.resourceCenter.integrationProvider');
|
|
21328
|
-
resourceCenter.isNativeModuleActive = activeModule && BuildingBlockResourceCenter.isSupportedNativeIntegration(activeIntegrationProvider);
|
|
21329
|
-
if (resourceCenter.isShown() || !store.getters['frames/isLeader']()) {
|
|
21330
|
-
resourceCenterShowingProc(resourceCenter, resourceCenterBadgeProc);
|
|
21331
|
-
}
|
|
21332
|
-
else {
|
|
21333
|
-
resourceCenterNotShowingProc(resourceCenter, resourceCenterBadgeProc);
|
|
21334
|
-
}
|
|
21335
|
-
// Hide the badge if we're displaying a native integration module
|
|
21336
|
-
if (resourceCenter.isNativeModuleActive) {
|
|
21337
|
-
if (resourceCenter.isShown())
|
|
21338
|
-
resourceCenter.hide();
|
|
21339
|
-
var RCBadge = badgesShown[resourceCenter.id];
|
|
21340
|
-
if (RCBadge) {
|
|
21341
|
-
BuildingBlockResourceCenter.hideRCBadge(RCBadge, resourceCenter);
|
|
21342
|
-
}
|
|
21343
|
-
}
|
|
21344
|
-
// Place Element Activated Notification Badge
|
|
21345
|
-
if (resourceCenter.launchMethod === 'dom' && resourceCenter.canShowOnDevice()) {
|
|
21346
|
-
BuildingBlockResourceCenter.updateNotificationBubbles();
|
|
21347
|
-
BuildingBlockResourceCenter.adjustNotificationBubbleForResize();
|
|
21348
|
-
}
|
|
21349
|
-
}
|
|
21350
|
-
class PhaseProcessTimeMonitor {
|
|
21351
|
-
constructor(timeLimit = 50) {
|
|
21352
|
-
this.startTime = -1;
|
|
21353
|
-
this.timeLimit = timeLimit;
|
|
21354
|
-
this.context = null;
|
|
21355
|
-
}
|
|
21356
|
-
start() {
|
|
21357
|
-
this.context = null;
|
|
21358
|
-
this.startTime = getNow();
|
|
21359
|
-
}
|
|
21360
|
-
isTimeExceeded() {
|
|
21361
|
-
const currTime = getNow();
|
|
21362
|
-
return (currTime - this.startTime) > this.timeLimit;
|
|
21363
|
-
}
|
|
21364
|
-
saveContext(context) {
|
|
21365
|
-
this.context = context;
|
|
21366
|
-
}
|
|
21367
|
-
getContext() {
|
|
21368
|
-
return this.context;
|
|
21369
|
-
}
|
|
21370
|
-
}
|
|
21371
|
-
class UpdateRunner {
|
|
21372
|
-
constructor(phases) {
|
|
21373
|
-
this.phases = phases;
|
|
21374
|
-
this.queue = [];
|
|
21375
|
-
this.paused = false;
|
|
21376
|
-
}
|
|
21377
|
-
markDirty() {
|
|
21378
|
-
for (let phase of this.phases) {
|
|
21379
|
-
phase.markDirty();
|
|
21380
|
-
}
|
|
21381
|
-
this.queue = this.phases.slice();
|
|
21382
|
-
}
|
|
21383
|
-
runGuidePhase() {
|
|
21384
|
-
const currentPhase = this.queue[0];
|
|
21385
|
-
var promise;
|
|
21386
|
-
const monitor = new PhaseProcessTimeMonitor(50);
|
|
21387
|
-
while (!this.timedOut() && !currentPhase.isComplete() && !promise) {
|
|
21388
|
-
monitor.start();
|
|
21389
|
-
promise = currentPhase.process(monitor);
|
|
21390
|
-
if (!promise && monitor.isTimeExceeded()) {
|
|
21391
|
-
performanceMonitor.count(GUIDE_LOOP_TIMEOUT);
|
|
21392
|
-
if (currentPhase.handleProcessTimeExceeded) {
|
|
21393
|
-
currentPhase.handleProcessTimeExceeded(monitor);
|
|
21394
|
-
}
|
|
21395
|
-
this.ejectGuide(monitor.getContext());
|
|
21396
|
-
}
|
|
21397
|
-
}
|
|
21398
|
-
if (isPromise(promise)) {
|
|
21399
|
-
var id = this.updateId;
|
|
21400
|
-
var resume = _.bind(function () {
|
|
21401
|
-
if (this.updateId === id) {
|
|
21402
|
-
this.paused = false;
|
|
21403
|
-
startGuides();
|
|
21404
|
-
}
|
|
21405
|
-
}, this);
|
|
21406
|
-
this.paused = true;
|
|
21407
|
-
promise.then(resume).catch(resume);
|
|
21408
|
-
}
|
|
21409
|
-
else if (currentPhase.isComplete()) {
|
|
21410
|
-
this.queue.shift();
|
|
21411
|
-
}
|
|
21412
|
-
}
|
|
21413
|
-
addPhase(phaseFn, phaseName) {
|
|
21414
|
-
phaseName = phaseName || phaseFn.name || this.phases.length;
|
|
21415
|
-
if (_.isFunction(phaseFn)) {
|
|
21416
|
-
phaseFn = new SimplePhase(phaseName, phaseFn);
|
|
21417
|
-
}
|
|
21418
|
-
this.phases.push(phaseFn);
|
|
21419
|
-
}
|
|
21420
|
-
removePhase(phaseName) {
|
|
21421
|
-
var i = _.findIndex(this.phases, function (phase) {
|
|
21422
|
-
return phase.name === phaseName;
|
|
21423
|
-
});
|
|
21424
|
-
if (i >= 0) {
|
|
21425
|
-
this.phases.splice(i, 1);
|
|
21426
|
-
}
|
|
21427
|
-
}
|
|
21428
|
-
timedOut() {
|
|
21429
|
-
if (!this.iterationStartTime)
|
|
21430
|
-
return false;
|
|
21431
|
-
var elapsed = getNow() - this.iterationStartTime;
|
|
21432
|
-
return elapsed > store.state.guideUpdate.iterationTimeLimit;
|
|
21433
|
-
}
|
|
21434
|
-
ejectGuide(guide) {
|
|
21435
|
-
if (this.ejectOnTimeout === undefined) {
|
|
21436
|
-
this.ejectOnTimeout = ConfigReader.get('guides.ejectOnTimeout');
|
|
21437
|
-
}
|
|
21438
|
-
if (this.ejectOnTimeout && guide && !guide.isShown()) {
|
|
21439
|
-
Tombstone.addGuide(guide);
|
|
21440
|
-
}
|
|
21441
|
-
}
|
|
21442
|
-
process() {
|
|
21443
|
-
var now = getNow();
|
|
21444
|
-
if (!this.updateId) {
|
|
21445
|
-
this.updateId = now;
|
|
21446
|
-
this.updateTotalTime = 0;
|
|
21447
|
-
store.commit('guideUpdate/startUpdate', now);
|
|
21448
|
-
}
|
|
21449
|
-
this.iterationStartTime = now;
|
|
21450
|
-
while (!this.paused &&
|
|
21451
|
-
this.queue.length &&
|
|
21452
|
-
!this.timedOut()) {
|
|
21453
|
-
this.runGuidePhase();
|
|
21454
|
-
}
|
|
21455
|
-
now = getNow();
|
|
21456
|
-
this.updateTotalTime += now - this.iterationStartTime;
|
|
21457
|
-
if (this.isComplete()) {
|
|
21458
|
-
this.complete(now);
|
|
21459
|
-
}
|
|
21460
|
-
}
|
|
21461
|
-
complete(time) {
|
|
21462
|
-
store.commit('guideUpdate/completeUpdate', time);
|
|
21463
|
-
this.cancel();
|
|
21464
|
-
}
|
|
21465
|
-
cancel() {
|
|
21466
|
-
this.updateId = null;
|
|
21467
|
-
this.paused = false;
|
|
21468
|
-
this.queue.length = 0;
|
|
21469
|
-
for (let phase of this.phases) {
|
|
21470
|
-
if (_.isFunction(phase.cancel)) {
|
|
21471
|
-
phase.cancel();
|
|
21472
|
-
}
|
|
21473
|
-
}
|
|
21474
|
-
}
|
|
21475
|
-
isComplete() {
|
|
21476
|
-
return !this.queue.length;
|
|
21477
|
-
}
|
|
21478
|
-
}
|
|
21479
|
-
var updateRunner = new UpdateRunner([
|
|
21480
|
-
new SimplePhase('guideShowing', guideShowingProc),
|
|
21481
|
-
new AutoDisplayPhase(),
|
|
21482
|
-
new BadgePhase(),
|
|
21483
|
-
new SimplePhase('resourceCenter', function () {
|
|
21484
|
-
resourceCenterProc(BuildingBlockResourceCenter.getResourceCenter());
|
|
21485
|
-
}),
|
|
21486
|
-
new SimplePhase('frameVisibility', function () {
|
|
21487
|
-
store.dispatch('frames/checkFrameVisibility');
|
|
21488
|
-
})
|
|
21489
|
-
]);
|
|
21490
|
-
function addUpdatePhase() {
|
|
21491
|
-
updateRunner.addPhase(...arguments);
|
|
21492
|
-
}
|
|
21493
|
-
function removeUpdatePhase() {
|
|
21494
|
-
updateRunner.removePhase(...arguments);
|
|
21495
|
-
}
|
|
21496
|
-
|
|
21497
20860
|
function Wrappable() {
|
|
21498
20861
|
var wrappers = {};
|
|
21499
20862
|
var wrap = function (wrappedMethod, before, after) {
|
|
@@ -22690,119 +22053,810 @@ function ShowOnHover() {
|
|
|
22690
22053
|
attached = true;
|
|
22691
22054
|
mouseout();
|
|
22692
22055
|
}
|
|
22693
|
-
});
|
|
22694
|
-
badge.after('hide', function () {
|
|
22695
|
-
if (attached) {
|
|
22696
|
-
detachEvent(target, 'mouseover', mouseover);
|
|
22697
|
-
attached = false;
|
|
22056
|
+
});
|
|
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;
|
|
22698
22606
|
}
|
|
22699
|
-
|
|
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();
|
|
22700
22633
|
});
|
|
22634
|
+
var firstEligibleModule = moduleLookup[firstEligibleGuide && firstEligibleGuide.id];
|
|
22635
|
+
if (firstEligibleModule) {
|
|
22636
|
+
eligibleModules.push(firstEligibleModule);
|
|
22637
|
+
}
|
|
22701
22638
|
}
|
|
22702
|
-
|
|
22639
|
+
resourceCenterBadgeProc(resourceCenter, eligibleModules, controlGuidesInAllModules);
|
|
22703
22640
|
}
|
|
22704
|
-
|
|
22705
|
-
|
|
22706
|
-
|
|
22707
|
-
|
|
22708
|
-
|
|
22709
|
-
|
|
22710
|
-
|
|
22711
|
-
|
|
22712
|
-
|
|
22713
|
-
|
|
22714
|
-
|
|
22715
|
-
|
|
22716
|
-
|
|
22717
|
-
|
|
22718
|
-
|
|
22719
|
-
|
|
22720
|
-
|
|
22721
|
-
|
|
22722
|
-
|
|
22723
|
-
|
|
22724
|
-
|
|
22725
|
-
|
|
22726
|
-
|
|
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;
|
|
22727
22670
|
}
|
|
22728
|
-
return;
|
|
22729
22671
|
}
|
|
22730
|
-
//
|
|
22731
|
-
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)
|
|
22732
22686
|
return;
|
|
22733
|
-
|
|
22734
|
-
|
|
22735
|
-
|
|
22736
|
-
|
|
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);
|
|
22737
22696
|
}
|
|
22738
|
-
|
|
22739
|
-
|
|
22740
|
-
|
|
22741
|
-
|
|
22742
|
-
if (
|
|
22743
|
-
if (
|
|
22744
|
-
|
|
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);
|
|
22745
22707
|
}
|
|
22746
|
-
badge.show();
|
|
22747
|
-
badgesShownObj[guide.id] = badge;
|
|
22748
22708
|
}
|
|
22749
|
-
|
|
22750
|
-
|
|
22751
|
-
|
|
22752
|
-
|
|
22753
|
-
|
|
22754
|
-
|
|
22755
|
-
|
|
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();
|
|
22756
22745
|
}
|
|
22746
|
+
this.queue = this.phases.slice();
|
|
22757
22747
|
}
|
|
22758
|
-
|
|
22759
|
-
|
|
22760
|
-
|
|
22761
|
-
|
|
22762
|
-
|
|
22763
|
-
|
|
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;
|
|
22764
22788
|
});
|
|
22789
|
+
if (i >= 0) {
|
|
22790
|
+
this.phases.splice(i, 1);
|
|
22791
|
+
}
|
|
22765
22792
|
}
|
|
22766
|
-
|
|
22767
|
-
|
|
22768
|
-
|
|
22769
|
-
|
|
22770
|
-
|
|
22771
|
-
};
|
|
22772
|
-
var removeBadge = function (badge, guide) {
|
|
22773
|
-
if (!badge)
|
|
22774
|
-
return;
|
|
22775
|
-
if (_.isFunction(badge.hide)) {
|
|
22776
|
-
badge.hide();
|
|
22793
|
+
timedOut() {
|
|
22794
|
+
if (!this.iterationStartTime)
|
|
22795
|
+
return false;
|
|
22796
|
+
var elapsed = getNow() - this.iterationStartTime;
|
|
22797
|
+
return elapsed > store.state.guideUpdate.iterationTimeLimit;
|
|
22777
22798
|
}
|
|
22778
|
-
|
|
22779
|
-
|
|
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
|
+
}
|
|
22780
22806
|
}
|
|
22781
|
-
|
|
22782
|
-
var
|
|
22783
|
-
|
|
22784
|
-
|
|
22785
|
-
|
|
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
|
+
}
|
|
22786
22825
|
}
|
|
22787
|
-
|
|
22788
|
-
|
|
22789
|
-
|
|
22790
|
-
|
|
22791
|
-
|
|
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
|
+
}
|
|
22792
22838
|
}
|
|
22793
|
-
}
|
|
22839
|
+
}
|
|
22840
|
+
isComplete() {
|
|
22841
|
+
return !this.queue.length;
|
|
22842
|
+
}
|
|
22794
22843
|
}
|
|
22795
|
-
var
|
|
22796
|
-
|
|
22797
|
-
|
|
22798
|
-
|
|
22799
|
-
|
|
22800
|
-
|
|
22801
|
-
|
|
22802
|
-
|
|
22803
|
-
|
|
22804
|
-
|
|
22805
|
-
|
|
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);
|
|
22806
22860
|
}
|
|
22807
22861
|
|
|
22808
22862
|
/**
|
|
@@ -23517,7 +23571,7 @@ function registerPostrenderScripts(runtime) {
|
|
|
23517
23571
|
});
|
|
23518
23572
|
}
|
|
23519
23573
|
|
|
23520
|
-
const pendoLocalStorage$
|
|
23574
|
+
const pendoLocalStorage$2 = agentStorage.getLocal();
|
|
23521
23575
|
var guideEvtCache = [];
|
|
23522
23576
|
var controlGuideLogMessage = 'Guide was not shown because this visitor is in a control group of an active experiment for the guide';
|
|
23523
23577
|
/**
|
|
@@ -25315,7 +25369,7 @@ var handleResizeOnVideoFullScreenChange = function (event) {
|
|
|
25315
25369
|
function shouldReloadGuides(url, visitorId, accountId, metadataHash) {
|
|
25316
25370
|
if (!url || !pendo$1.apiKey)
|
|
25317
25371
|
return false;
|
|
25318
|
-
if (areGuidesDisabled() && !(pendoLocalStorage$
|
|
25372
|
+
if (areGuidesDisabled() && !(pendoLocalStorage$2.getItem('pendo-designer-mode') === 'true'))
|
|
25319
25373
|
return false;
|
|
25320
25374
|
return (url !== reloadGuides.lastUrl ||
|
|
25321
25375
|
visitorId !== reloadGuides.lastVisitorId ||
|
|
@@ -25399,6 +25453,180 @@ function getActiveGuide(options) {
|
|
|
25399
25453
|
};
|
|
25400
25454
|
}
|
|
25401
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
|
+
|
|
25402
25630
|
var REMOVE_FRAME_ACTION = 'frames/removeFrame';
|
|
25403
25631
|
function tryParseJson(messageData) {
|
|
25404
25632
|
if (!_.isString(messageData)) {
|
|
@@ -25913,7 +26141,8 @@ function initializeCrossFrameChannel(store) {
|
|
|
25913
26141
|
return () => crossFrameChannel.close();
|
|
25914
26142
|
}
|
|
25915
26143
|
|
|
25916
|
-
const pendoLocalStorage$
|
|
26144
|
+
const pendoLocalStorage$1 = agentStorage.getLocal();
|
|
26145
|
+
var pendoLivePreview$1 = 'pendo-live-preview';
|
|
25917
26146
|
var pendoPreview$1 = 'pendo-preview';
|
|
25918
26147
|
var designerPreview$1 = 'current-guide-preview';
|
|
25919
26148
|
var previewFrame;
|
|
@@ -25927,6 +26156,11 @@ function startPreviewMode(window) {
|
|
|
25927
26156
|
return;
|
|
25928
26157
|
addPreviewUI(window);
|
|
25929
26158
|
store.dispatch('preview/start', _.extend(config));
|
|
26159
|
+
if (config.livePreview) {
|
|
26160
|
+
startLivePreviewRelay(config.livePreview, function () {
|
|
26161
|
+
exitPreviewMode({ preventWindowClose: false });
|
|
26162
|
+
});
|
|
26163
|
+
}
|
|
25930
26164
|
agentStorage.clear(LAST_STEP_ADVANCED_COOKIE);
|
|
25931
26165
|
enableCookies(false);
|
|
25932
26166
|
buffers.lock();
|
|
@@ -25990,7 +26224,7 @@ function restartPreview(activeGuides, lastGuideStepSeen, language) {
|
|
|
25990
26224
|
var config = findStoredPreviewConfig();
|
|
25991
26225
|
// Go back to 1st step of walkthrough on explicit restart
|
|
25992
26226
|
if (config) {
|
|
25993
|
-
var previewGuide = _.findWhere(activeGuides, { id: config
|
|
26227
|
+
var previewGuide = _.findWhere(activeGuides, { id: getPreviewGuideId(config) });
|
|
25994
26228
|
if (previewGuide) {
|
|
25995
26229
|
var restartConfig = { stepId: previewGuide.steps[0].id };
|
|
25996
26230
|
// language is used in `previewGuideRequest` to generate the proper urls for rendering translations
|
|
@@ -26114,6 +26348,7 @@ function prepareDesignerPreviewGuide() {
|
|
|
26114
26348
|
currentPreviewGuideConfig.guide.expiresAfter = null;
|
|
26115
26349
|
return [currentPreviewGuideConfig.guide];
|
|
26116
26350
|
}
|
|
26351
|
+
return [];
|
|
26117
26352
|
}
|
|
26118
26353
|
function preparePreviewGuides(activeGuides) {
|
|
26119
26354
|
var config = findStoredPreviewConfig();
|
|
@@ -26192,7 +26427,8 @@ function preparePreviewLastGuideStepSeen(activeGuides, lastGuideStepSeen) {
|
|
|
26192
26427
|
var config = findStoredPreviewConfig();
|
|
26193
26428
|
if (!config)
|
|
26194
26429
|
return lastGuideStepSeen;
|
|
26195
|
-
var
|
|
26430
|
+
var guideId = getPreviewGuideId(config);
|
|
26431
|
+
var previewGuide = _.findWhere(activeGuides, { id: guideId });
|
|
26196
26432
|
if (!previewGuide)
|
|
26197
26433
|
return lastGuideStepSeen;
|
|
26198
26434
|
if (config.state)
|
|
@@ -26205,7 +26441,7 @@ function preparePreviewLastGuideStepSeen(activeGuides, lastGuideStepSeen) {
|
|
|
26205
26441
|
return {};
|
|
26206
26442
|
}
|
|
26207
26443
|
return {
|
|
26208
|
-
guideId
|
|
26444
|
+
guideId,
|
|
26209
26445
|
guideStepId: config.stepId,
|
|
26210
26446
|
state: 'active'
|
|
26211
26447
|
};
|
|
@@ -26253,9 +26489,13 @@ function updatePreview(document, activeGuides, lastGuideStepSeen) {
|
|
|
26253
26489
|
}
|
|
26254
26490
|
}, location.origin);
|
|
26255
26491
|
}
|
|
26492
|
+
var previewGuideId = getPreviewGuideId(config);
|
|
26256
26493
|
var guide = _.find(activeGuides, function (guide) {
|
|
26257
|
-
return guide.id ===
|
|
26494
|
+
return guide.id === previewGuideId;
|
|
26258
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;
|
|
26259
26499
|
var currentStepIndex = 0;
|
|
26260
26500
|
var stepCount = guide.steps.length;
|
|
26261
26501
|
var lastSeen = lastGuideStepSeen || {};
|
|
@@ -26417,7 +26657,7 @@ function exitPreviewMode(designerConfig) {
|
|
|
26417
26657
|
if (currentPreviewGuideConfig) {
|
|
26418
26658
|
wasInDesignerSameTabPreview = currentPreviewGuideConfig.previewModeType === 'SAME_TAB';
|
|
26419
26659
|
}
|
|
26420
|
-
var storedNavigationMode = pendoLocalStorage$
|
|
26660
|
+
var storedNavigationMode = pendoLocalStorage$1.getItem('pendo-navigation-state');
|
|
26421
26661
|
store.dispatch('preview/exit');
|
|
26422
26662
|
hideGuides({ stayHidden: true });
|
|
26423
26663
|
agentStorage.clear(LAST_STEP_ADVANCED_COOKIE);
|
|
@@ -26426,30 +26666,37 @@ function exitPreviewMode(designerConfig) {
|
|
|
26426
26666
|
restoreGuideLoader();
|
|
26427
26667
|
removeUpdatePhase(updatePreviewUI);
|
|
26428
26668
|
enableCookies(true);
|
|
26429
|
-
|
|
26430
|
-
|
|
26431
|
-
|
|
26432
|
-
|
|
26433
|
-
|
|
26434
|
-
|
|
26435
|
-
|
|
26436
|
-
|
|
26437
|
-
|
|
26438
|
-
|
|
26439
|
-
|
|
26440
|
-
|
|
26441
|
-
|
|
26442
|
-
|
|
26443
|
-
|
|
26444
|
-
|
|
26445
|
-
|
|
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;
|
|
26446
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);
|
|
26447
26697
|
return;
|
|
26448
26698
|
}
|
|
26449
|
-
|
|
26450
|
-
window.close();
|
|
26451
|
-
// For cases when window.close() does not work, as when exiting restored preview sessions in newly opened windows, resume normal guide display
|
|
26452
|
-
forceGuideReload();
|
|
26699
|
+
finishExit();
|
|
26453
26700
|
}
|
|
26454
26701
|
function clonePreviewConfig(getStore, path) {
|
|
26455
26702
|
var previewConfig = _.get(getStore(), path);
|
|
@@ -26462,6 +26709,14 @@ function getStore() {
|
|
|
26462
26709
|
}
|
|
26463
26710
|
var findStoredPreviewConfig = _.partial(clonePreviewConfig, getStore, 'state.preview.preview');
|
|
26464
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
|
+
}
|
|
26465
26720
|
function getGuideUrl(config) {
|
|
26466
26721
|
if (!config.subscriptionId || !config.guideId)
|
|
26467
26722
|
return;
|
|
@@ -26510,6 +26765,35 @@ function previewGuideRequest(config, id) {
|
|
|
26510
26765
|
log.info('Invalid origin, exiting preview');
|
|
26511
26766
|
return q.reject('invalid origin');
|
|
26512
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
|
+
}
|
|
26513
26797
|
var guideURL = getGuideUrl(config);
|
|
26514
26798
|
if (!guideURL) {
|
|
26515
26799
|
log.info('Invalid guide url, exiting preview');
|
|
@@ -26599,9 +26883,16 @@ function previewGuideRequest(config, id) {
|
|
|
26599
26883
|
}
|
|
26600
26884
|
function previewLoadGuideJs(url, callback) {
|
|
26601
26885
|
var config = findStoredPreviewConfig();
|
|
26602
|
-
if (!config
|
|
26886
|
+
if (!config)
|
|
26603
26887
|
return q.reject();
|
|
26604
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();
|
|
26605
26896
|
if (!match || match.length < 1)
|
|
26606
26897
|
return q.reject();
|
|
26607
26898
|
return previewGuideRequest(config, match[1]).then(callback);
|
|
@@ -26725,7 +27016,7 @@ const Keys = (function () {
|
|
|
26725
27016
|
};
|
|
26726
27017
|
})();
|
|
26727
27018
|
|
|
26728
|
-
const pendoLocalStorage
|
|
27019
|
+
const pendoLocalStorage = agentStorage.getLocal();
|
|
26729
27020
|
var DesignerV2 = (function () {
|
|
26730
27021
|
// Always use the production server for CNAME customers
|
|
26731
27022
|
var host = DESIGNER_SERVER || SERVER;
|
|
@@ -26739,7 +27030,7 @@ var DesignerV2 = (function () {
|
|
|
26739
27030
|
function localStorageNavigation(options) {
|
|
26740
27031
|
if (options.ignoreLocalStorageNavigation)
|
|
26741
27032
|
return;
|
|
26742
|
-
var storedNavigationMode = pendoLocalStorage
|
|
27033
|
+
var storedNavigationMode = pendoLocalStorage.getItem('pendo-navigation-state');
|
|
26743
27034
|
if (!storedNavigationMode)
|
|
26744
27035
|
return;
|
|
26745
27036
|
// only launch from local storage if you are the leader frame
|
|
@@ -27456,6 +27747,16 @@ function launchDesignerOrPreview(options) {
|
|
|
27456
27747
|
* @label designerPreview
|
|
27457
27748
|
*/
|
|
27458
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);
|
|
27459
27760
|
// in order, try to launch:
|
|
27460
27761
|
// 1) designer
|
|
27461
27762
|
// 2) preview
|
|
@@ -28704,6 +29005,7 @@ const PluginAPI = {
|
|
|
28704
29005
|
SERVER,
|
|
28705
29006
|
DESIGNER_SERVER
|
|
28706
29007
|
},
|
|
29008
|
+
isTrustedOrigin,
|
|
28707
29009
|
NodeSerializer,
|
|
28708
29010
|
q,
|
|
28709
29011
|
SendQueue,
|
|
@@ -34447,7 +34749,16 @@ var FramesModule = (function () {
|
|
|
34447
34749
|
testSelector(context, payload) {
|
|
34448
34750
|
const { selector, sourceFrame, promiseId } = payload;
|
|
34449
34751
|
const startTime = performance.now();
|
|
34450
|
-
|
|
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
|
+
}
|
|
34451
34762
|
const timing = performance.now() - startTime;
|
|
34452
34763
|
if (promiseId && sourceFrame) {
|
|
34453
34764
|
crossFrameChannel.postMessage({
|
|
@@ -35035,7 +35346,7 @@ var MetadataModule = (function () {
|
|
|
35035
35346
|
};
|
|
35036
35347
|
})();
|
|
35037
35348
|
|
|
35038
|
-
|
|
35349
|
+
var pendoLivePreview = 'pendo-live-preview';
|
|
35039
35350
|
var pendoPreview = 'pendo-preview';
|
|
35040
35351
|
var designerPreview = 'current-guide-preview';
|
|
35041
35352
|
var PreviewModule = (function () {
|
|
@@ -35059,11 +35370,15 @@ var PreviewModule = (function () {
|
|
|
35059
35370
|
load(context) {
|
|
35060
35371
|
try {
|
|
35061
35372
|
var storage = context.getters.storage();
|
|
35062
|
-
var config = JSON.parse(storage.getItem(pendoPreview));
|
|
35373
|
+
var config = JSON.parse(storage.getLocal().getItem(pendoPreview));
|
|
35063
35374
|
if (config.apiKey === pendo$1.apiKey) {
|
|
35375
|
+
var livePreviewConfig = JSON.parse(storage.getSession().getItem(pendoLivePreview));
|
|
35376
|
+
if (livePreviewConfig) {
|
|
35377
|
+
config.livePreview = livePreviewConfig;
|
|
35378
|
+
}
|
|
35064
35379
|
context.commit('setPreview', config);
|
|
35065
35380
|
}
|
|
35066
|
-
var designerPreviewConfig = storage.getItem(designerPreview);
|
|
35381
|
+
var designerPreviewConfig = storage.getLocal().getItem(designerPreview);
|
|
35067
35382
|
if (designerPreviewConfig) {
|
|
35068
35383
|
context.commit('setDesignerPreview', JSON.parse(designerPreviewConfig));
|
|
35069
35384
|
}
|
|
@@ -35074,11 +35389,19 @@ var PreviewModule = (function () {
|
|
|
35074
35389
|
write(context) {
|
|
35075
35390
|
try {
|
|
35076
35391
|
var storage = context.getters.storage();
|
|
35077
|
-
|
|
35078
|
-
|
|
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));
|
|
35079
35402
|
}
|
|
35080
35403
|
else {
|
|
35081
|
-
storage.removeItem(
|
|
35404
|
+
storage.getSession().removeItem(pendoLivePreview);
|
|
35082
35405
|
}
|
|
35083
35406
|
}
|
|
35084
35407
|
catch (e) {
|
|
@@ -35090,8 +35413,7 @@ var PreviewModule = (function () {
|
|
|
35090
35413
|
},
|
|
35091
35414
|
exitDesignerPreview(context) {
|
|
35092
35415
|
context.commit('setDesignerPreview', null);
|
|
35093
|
-
|
|
35094
|
-
storage.removeItem(designerPreview);
|
|
35416
|
+
context.getters.storage().getLocal().removeItem(designerPreview);
|
|
35095
35417
|
},
|
|
35096
35418
|
launchInAppDesigner(context, launchOptions) {
|
|
35097
35419
|
DesignerV2.launchInAppDesigner(launchOptions);
|
|
@@ -35118,7 +35440,7 @@ var PreviewModule = (function () {
|
|
|
35118
35440
|
return !!state.designerPreview && getters.isInPreviewMode();
|
|
35119
35441
|
},
|
|
35120
35442
|
storage() {
|
|
35121
|
-
return
|
|
35443
|
+
return agentStorage;
|
|
35122
35444
|
}
|
|
35123
35445
|
};
|
|
35124
35446
|
return {
|
|
@@ -35274,13 +35596,10 @@ function highlight(dom, elements) {
|
|
|
35274
35596
|
}
|
|
35275
35597
|
function highlightElements(dom, selector) {
|
|
35276
35598
|
try {
|
|
35277
|
-
|
|
35278
|
-
|
|
35279
|
-
|
|
35280
|
-
|
|
35281
|
-
elements.push(query[i]);
|
|
35282
|
-
}
|
|
35283
|
-
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));
|
|
35284
35603
|
}
|
|
35285
35604
|
catch (e) {
|
|
35286
35605
|
clearHighlights();
|
|
@@ -40919,6 +41238,14 @@ class DOMPrompt {
|
|
|
40919
41238
|
});
|
|
40920
41239
|
}
|
|
40921
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
|
+
}
|
|
40922
41249
|
this.promptContainer = new this.dom.Observer();
|
|
40923
41250
|
const validInputEls = this._.filter(this.inputEls, el => el);
|
|
40924
41251
|
const validSubmitEls = this._.filter(this.submitEls, el => el);
|
|
@@ -40994,6 +41321,9 @@ class DOMPrompt {
|
|
|
40994
41321
|
});
|
|
40995
41322
|
}
|
|
40996
41323
|
teardown() {
|
|
41324
|
+
if (this.observerReadyListener) {
|
|
41325
|
+
document.removeEventListener('DOMContentLoaded', this.observerReadyListener);
|
|
41326
|
+
}
|
|
40997
41327
|
if (this.promptContainer) {
|
|
40998
41328
|
this.promptContainer.teardown();
|
|
40999
41329
|
}
|
|
@@ -41038,7 +41368,8 @@ class PromptPlugin {
|
|
|
41038
41368
|
// register config schema
|
|
41039
41369
|
this.api.ConfigReader.addOption(this.configName, ['pendoconfig', 'snippet']);
|
|
41040
41370
|
this.loadConfig(this.configName)
|
|
41041
|
-
.then(config => this.onConfigLoaded(config))
|
|
41371
|
+
.then(config => this.onConfigLoaded(config))
|
|
41372
|
+
.catch(err => this.api.log.error('[PromptPlugin] Failed to load config:', err));
|
|
41042
41373
|
// Listen for URL changes to re-evaluate page rules
|
|
41043
41374
|
// guidesLoaded: normalizedUrl comes back from the guides payload
|
|
41044
41375
|
// urlChanged: handles SPA navigation where guides may already be cached and guidesLoaded won't re-fire
|
|
@@ -41104,7 +41435,12 @@ class PromptPlugin {
|
|
|
41104
41435
|
const isCurrentlyActive = this._.contains(currentlyActiveIds, aiAgent.id);
|
|
41105
41436
|
const shouldBeActive = this.testPageRule(aiAgent.pageRule, currentUrl, aiAgent.id);
|
|
41106
41437
|
if (shouldBeActive && !isCurrentlyActive) {
|
|
41107
|
-
|
|
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
|
+
}
|
|
41108
41444
|
}
|
|
41109
41445
|
});
|
|
41110
41446
|
}
|
|
@@ -41153,7 +41489,12 @@ class PromptPlugin {
|
|
|
41153
41489
|
this._.each(domAgents, aiAgent => {
|
|
41154
41490
|
const currentUrl = getNormalizedUrl();
|
|
41155
41491
|
if (this.testPageRule(aiAgent.pageRule, currentUrl, aiAgent.id)) {
|
|
41156
|
-
|
|
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
|
+
}
|
|
41157
41498
|
}
|
|
41158
41499
|
});
|
|
41159
41500
|
}
|
|
@@ -43468,6 +43809,7 @@ const MESSAGE_PROMISE = 'promise';
|
|
|
43468
43809
|
* @property {*} requestId - The id of the request. This is used to match responses to requests.
|
|
43469
43810
|
* @property {*} sender - The name of the sender. This is used to determine if the message is from a trusted source.
|
|
43470
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.
|
|
43471
43813
|
*/
|
|
43472
43814
|
function Message(name, type, data) {
|
|
43473
43815
|
this.name = name;
|
|
@@ -43496,6 +43838,7 @@ const fromEvent = (event) => {
|
|
|
43496
43838
|
message.requestId = requestId;
|
|
43497
43839
|
message.sender = sender;
|
|
43498
43840
|
message.destination = event.source;
|
|
43841
|
+
message.origin = event.origin;
|
|
43499
43842
|
message.isResponse = isResponse;
|
|
43500
43843
|
|
|
43501
43844
|
return message;
|
|
@@ -43505,7 +43848,7 @@ const fromEvent = (event) => {
|
|
|
43505
43848
|
};
|
|
43506
43849
|
|
|
43507
43850
|
const serialize = (message) => {
|
|
43508
|
-
const redacted = omit(message, ['destination', 'toString', 'data']);
|
|
43851
|
+
const redacted = omit(message, ['destination', 'origin', 'toString', 'data']);
|
|
43509
43852
|
redacted.data = JSON.stringify(message.data, (key, value) => {
|
|
43510
43853
|
if (typeof value === 'function' || value instanceof RegExp) {
|
|
43511
43854
|
return value.toString ? value.toString() : undefined; // return undefined to omit
|
|
@@ -43621,14 +43964,24 @@ const state = {
|
|
|
43621
43964
|
topics: {},
|
|
43622
43965
|
senderIds: {},
|
|
43623
43966
|
originProvider: undefined,
|
|
43967
|
+
isOriginAllowed: undefined,
|
|
43624
43968
|
destinationProvider: undefined
|
|
43625
43969
|
};
|
|
43626
43970
|
|
|
43627
43971
|
const getListener = () => `${state.appName}${state.connectionName}(${state.frameId})`;
|
|
43628
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
|
+
|
|
43629
43981
|
const _send = (message) => {
|
|
43630
43982
|
if (!message.sender) message.sender = getListener();
|
|
43631
43983
|
const win = message.destination || state.destinationProvider();
|
|
43984
|
+
const targetOrigin = getTargetOrigin(message);
|
|
43632
43985
|
const redactedMessage = serialize(message);
|
|
43633
43986
|
|
|
43634
43987
|
const port = getPortForSender(message.sender);
|
|
@@ -43638,11 +43991,11 @@ const _send = (message) => {
|
|
|
43638
43991
|
// XXX Need to handle the scenario where MessageChannel is unavailable
|
|
43639
43992
|
const channel = new MessageChannel();
|
|
43640
43993
|
addPort(channel.port1, message.sender);
|
|
43641
|
-
win.postMessage(redactedMessage,
|
|
43994
|
+
win.postMessage(redactedMessage, targetOrigin, [channel.port2]);
|
|
43642
43995
|
} else if (port) {
|
|
43643
43996
|
port.postMessage(redactedMessage);
|
|
43644
43997
|
} else {
|
|
43645
|
-
win.postMessage(redactedMessage,
|
|
43998
|
+
win.postMessage(redactedMessage, targetOrigin);
|
|
43646
43999
|
}
|
|
43647
44000
|
};
|
|
43648
44001
|
|
|
@@ -43772,6 +44125,8 @@ const commonFns = pipe(
|
|
|
43772
44125
|
);
|
|
43773
44126
|
|
|
43774
44127
|
const filterWrongOrigin = (event) => {
|
|
44128
|
+
if (state.isOriginAllowed) return state.isOriginAllowed(event.origin) ? event : false;
|
|
44129
|
+
|
|
43775
44130
|
if (state.originProvider() === '*') return event;
|
|
43776
44131
|
|
|
43777
44132
|
return event.origin === state.originProvider() ? event : false;
|
|
@@ -43836,7 +44191,8 @@ const startListening = (
|
|
|
43836
44191
|
originProvider,
|
|
43837
44192
|
destinationProvider,
|
|
43838
44193
|
usePingAutoReconnect = false,
|
|
43839
|
-
connectionName
|
|
44194
|
+
connectionName,
|
|
44195
|
+
isOriginAllowed
|
|
43840
44196
|
) => {
|
|
43841
44197
|
if (!window.addEventListener) return;
|
|
43842
44198
|
state.appName = appName;
|
|
@@ -43845,6 +44201,7 @@ const startListening = (
|
|
|
43845
44201
|
state.usePingAutoReconnect = usePingAutoReconnect;
|
|
43846
44202
|
state.connectionName = connectionName || '';
|
|
43847
44203
|
|
|
44204
|
+
state.isOriginAllowed = typeof isOriginAllowed === 'function' ? isOriginAllowed : undefined;
|
|
43848
44205
|
state.originProvider = originProvider || (() => window.location.origin || '*');
|
|
43849
44206
|
state.destinationProvider = destinationProvider || (() => window.parent || window.top);
|
|
43850
44207
|
|
|
@@ -43920,7 +44277,11 @@ function VocPortal() {
|
|
|
43920
44277
|
pluginAPI = PluginAPI;
|
|
43921
44278
|
const { frameId } = PluginAPI.EventTracer.addTracerIds({});
|
|
43922
44279
|
this.removeResizeEvent = pendoGlobal.attachEvent(window, 'resize', resizePortalIframe);
|
|
43923
|
-
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);
|
|
43924
44285
|
try {
|
|
43925
44286
|
observePortalContainer();
|
|
43926
44287
|
}
|
|
@@ -50646,6 +51007,113 @@ class Transport {
|
|
|
50646
51007
|
}
|
|
50647
51008
|
}
|
|
50648
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
|
+
|
|
50649
51117
|
const ELEMENT_NODE = 1;
|
|
50650
51118
|
const INPUT_MASK = '*'.repeat(10);
|
|
50651
51119
|
const NUMBER_INPUT_MASK = '0'.repeat(10);
|
|
@@ -50891,6 +51359,7 @@ class SessionRecorder {
|
|
|
50891
51359
|
this.transport = new Transport(this.WorkerClass, this.pendo, this.api);
|
|
50892
51360
|
this.transport.onError = bind(this.sendFailure, this, 'WORKER_ERROR');
|
|
50893
51361
|
this.transport.onWorkerMessage = bind(this.onWorkerMessage, this);
|
|
51362
|
+
this.liveReplayClient = new LiveReplayClient(this.pendo, this.api);
|
|
50894
51363
|
this.sendQueue = new this.api.SendQueue(bind(this.transport.send, this.transport));
|
|
50895
51364
|
this.sendQueue.onTimeout = bind(this.sendFailure, this, 'SEND_TIMEOUT');
|
|
50896
51365
|
this.errorHandler = this.pendo._.throttle(bind(this.logStopReason, this, 'RRWEB_ERROR'), ONE_MINUTE_IN_MILLISECONDS, { trailing: false });
|
|
@@ -50981,6 +51450,7 @@ class SessionRecorder {
|
|
|
50981
51450
|
this.subscriptions.length = 0;
|
|
50982
51451
|
this.stop();
|
|
50983
51452
|
this.transport.stop();
|
|
51453
|
+
this.liveReplayClient.stop();
|
|
50984
51454
|
}
|
|
50985
51455
|
ready() {
|
|
50986
51456
|
if (this.api.ConfigReader.get(RECORDING_CONFIG_AUTO_START) !== false) {
|
|
@@ -51055,6 +51525,7 @@ class SessionRecorder {
|
|
|
51055
51525
|
this.stop();
|
|
51056
51526
|
}
|
|
51057
51527
|
if (visitorConfig && visitorConfig.enable && this.isRecording()) {
|
|
51528
|
+
this.liveReplayClient.onLiveFlag(visitorConfig.live, this.visitorId, () => this.snapshot());
|
|
51058
51529
|
if (continuationCallback) {
|
|
51059
51530
|
continuationCallback.call(this);
|
|
51060
51531
|
}
|
|
@@ -51197,6 +51668,7 @@ class SessionRecorder {
|
|
|
51197
51668
|
var bind = this.pendo._.bind;
|
|
51198
51669
|
this.visitorConfig = visitorConfig;
|
|
51199
51670
|
this.clearOldSessionId();
|
|
51671
|
+
this.liveReplayClient.onLiveFlag(visitorConfig.live, this.visitorId);
|
|
51200
51672
|
this.sendQueue.start();
|
|
51201
51673
|
this.interval = setInterval(() => {
|
|
51202
51674
|
if (!this.sendQueue.failed()) {
|
|
@@ -51395,9 +51867,11 @@ class SessionRecorder {
|
|
|
51395
51867
|
this.sessionId(this.recordingId);
|
|
51396
51868
|
this.restartPtm();
|
|
51397
51869
|
this._sendIds();
|
|
51870
|
+
this.pendo._.each(this.buffer.data, (bufferedEvent) => this._sendLiveReplayEvent(bufferedEvent));
|
|
51398
51871
|
this.send({ keyframe: true });
|
|
51399
51872
|
}
|
|
51400
51873
|
else if (!isMeta) {
|
|
51874
|
+
this._sendLiveReplayEvent(event);
|
|
51401
51875
|
this.eventsSinceLastKeyFrame += 1;
|
|
51402
51876
|
const timeSinceLastKeyFrame = event.timestamp - this.lastKeyFrameTime;
|
|
51403
51877
|
const exceeds24Hours = timeSinceLastKeyFrame >= ONE_DAY_IN_MILLISECONDS;
|
|
@@ -51421,6 +51895,23 @@ class SessionRecorder {
|
|
|
51421
51895
|
updateCurrentRecordingSize(recordingPayloadSize) {
|
|
51422
51896
|
this.currentRecordingSize += recordingPayloadSize;
|
|
51423
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
|
+
}
|
|
51424
51915
|
onWorkerMessage(messageData) {
|
|
51425
51916
|
switch (messageData.type) {
|
|
51426
51917
|
case 'workerDied': {
|
|
@@ -51455,6 +51946,13 @@ class SessionRecorder {
|
|
|
51455
51946
|
this.logStopReason(`MISSING_${messageData.missingData}`);
|
|
51456
51947
|
break;
|
|
51457
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
|
+
}
|
|
51458
51956
|
}
|
|
51459
51957
|
}
|
|
51460
51958
|
addRecordingId(event) {
|
|
@@ -51493,6 +51991,7 @@ class SessionRecorder {
|
|
|
51493
51991
|
}
|
|
51494
51992
|
// send existing events
|
|
51495
51993
|
this.send();
|
|
51994
|
+
this.liveReplayClient.stop();
|
|
51496
51995
|
// now, finish stopping any more collection or sending
|
|
51497
51996
|
this.sendQueue.stop();
|
|
51498
51997
|
this.recordingId = null;
|
|
@@ -51562,7 +52061,8 @@ class SessionRecorder {
|
|
|
51562
52061
|
this.api.Events.eventCaptured.trigger(payload);
|
|
51563
52062
|
const params = {
|
|
51564
52063
|
v: this.pendo.VERSION,
|
|
51565
|
-
recordingId: this.recordingId
|
|
52064
|
+
recordingId: this.recordingId,
|
|
52065
|
+
vis: this.api.util.base64EncodeString(this.visitorId)
|
|
51566
52066
|
};
|
|
51567
52067
|
if (payload.keyFrame) {
|
|
51568
52068
|
params.kf = 1;
|
|
@@ -51955,19 +52455,27 @@ var WorkerFactory = /*#__PURE__*/createInlineWorkerFactory(/* rollup-plugin-web-
|
|
|
51955
52455
|
method: 'POST',
|
|
51956
52456
|
body
|
|
51957
52457
|
}).then(function (response) {
|
|
51958
|
-
cancelResponseBody(response);
|
|
51959
52458
|
if (response.status < 200 || response.status >= 300) {
|
|
52459
|
+
cancelResponseBody(response);
|
|
51960
52460
|
postMessage({
|
|
51961
52461
|
error: new Error(`received status code ${response.status}: ${response.statusText}`),
|
|
51962
52462
|
status: response.status,
|
|
51963
52463
|
sequence
|
|
51964
52464
|
});
|
|
52465
|
+
return;
|
|
51965
52466
|
}
|
|
51966
|
-
|
|
51967
|
-
|
|
51968
|
-
|
|
51969
|
-
|
|
51970
|
-
|
|
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 () { });
|
|
51971
52479
|
}).catch(function (e) {
|
|
51972
52480
|
postMessage({
|
|
51973
52481
|
error: e,
|