@pendo/agent 2.342.0 → 2.343.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dom.esm.js +2 -25
- package/dist/pendo.module.js +467 -191
- package/dist/pendo.module.min.js +12 -12
- package/dist/servers.json +8 -8
- package/package.json +1 -1
package/dist/pendo.module.js
CHANGED
|
@@ -4058,8 +4058,8 @@ let SERVER = '';
|
|
|
4058
4058
|
let ASSET_HOST = '';
|
|
4059
4059
|
let ASSET_PATH = '';
|
|
4060
4060
|
let DESIGNER_SERVER = '';
|
|
4061
|
-
let VERSION = '2.
|
|
4062
|
-
let PACKAGE_VERSION = '2.
|
|
4061
|
+
let VERSION = '2.343.0_';
|
|
4062
|
+
let PACKAGE_VERSION = '2.343.0';
|
|
4063
4063
|
let LOADER = 'xhr';
|
|
4064
4064
|
/* eslint-enable web-sdk-eslint-rules/no-gulp-env-references */
|
|
4065
4065
|
/**
|
|
@@ -4476,15 +4476,35 @@ class LogEmitter extends EventTarget {
|
|
|
4476
4476
|
}
|
|
4477
4477
|
const log = new LogEmitter();
|
|
4478
4478
|
|
|
4479
|
-
/** Key used to lookup the consent collection policy in the pendo configuration. */
|
|
4480
4479
|
const CONSENT_COLLECTION = 'cookieConsentCollection';
|
|
4481
|
-
/** Key used to lookup the analytics events in the consent collection policy. */
|
|
4482
4480
|
const ANALYTICS = 'analytics';
|
|
4483
|
-
/** Key used to lookup the guide analytics events in the consent collection policy. */
|
|
4484
4481
|
const GUIDE_ANALYTICS = 'guideAnalytics';
|
|
4485
|
-
/** Key used to lookup the text collection mode in the consent collection policy. */
|
|
4486
4482
|
const TEXT_COLLECTION = 'textCollection';
|
|
4487
4483
|
const TEXT_COLLECTION_LEVELS = ['none', 'whitelist', 'all'];
|
|
4484
|
+
const ANALYTICS_EVENT_TYPES = {
|
|
4485
|
+
agentResponse: 'agent_response',
|
|
4486
|
+
clicks: 'click',
|
|
4487
|
+
fieldChanges: 'change',
|
|
4488
|
+
focus: 'focus',
|
|
4489
|
+
formSubmits: 'submit',
|
|
4490
|
+
identify: 'identify',
|
|
4491
|
+
meta: 'meta',
|
|
4492
|
+
pageLoads: 'load',
|
|
4493
|
+
prompt: 'prompt',
|
|
4494
|
+
trackEvents: 'track',
|
|
4495
|
+
userReaction: 'user_reaction'
|
|
4496
|
+
};
|
|
4497
|
+
const GUIDE_EVENT_TYPES = [
|
|
4498
|
+
'guideActivity',
|
|
4499
|
+
'guideAdvanced',
|
|
4500
|
+
'guideDismissed',
|
|
4501
|
+
'guideNotSeen',
|
|
4502
|
+
'guideSeen',
|
|
4503
|
+
'guideSnoozeCanceled',
|
|
4504
|
+
'guideSnoozed',
|
|
4505
|
+
'guideTimeout',
|
|
4506
|
+
'pollResponse'
|
|
4507
|
+
];
|
|
4488
4508
|
let enabledProducts = null;
|
|
4489
4509
|
function getClassification() {
|
|
4490
4510
|
return ConfigReader.get(CONSENT_COLLECTION) || {};
|
|
@@ -4504,30 +4524,59 @@ function warnOnUnconfiguredTiers(grantedTiers, classification) {
|
|
|
4504
4524
|
function moreTextPermissive(a, b) {
|
|
4505
4525
|
return TEXT_COLLECTION_LEVELS.indexOf(b) > TEXT_COLLECTION_LEVELS.indexOf(a) ? b : a;
|
|
4506
4526
|
}
|
|
4507
|
-
function
|
|
4508
|
-
return
|
|
4527
|
+
function toTextCollectionLevel(mode) {
|
|
4528
|
+
return _.contains(TEXT_COLLECTION_LEVELS, mode) ? mode : TEXT_COLLECTION_LEVELS[0];
|
|
4509
4529
|
}
|
|
4510
|
-
function
|
|
4511
|
-
return _.
|
|
4530
|
+
function toAnalyticsEventTypes(eventCollection) {
|
|
4531
|
+
return _.reduce(ANALYTICS_EVENT_TYPES, function (types, eventType, key) {
|
|
4532
|
+
if (eventCollection[key] === true) {
|
|
4533
|
+
types.push(eventType);
|
|
4534
|
+
}
|
|
4535
|
+
return types;
|
|
4536
|
+
}, []);
|
|
4537
|
+
}
|
|
4538
|
+
function normalizeTier(tier) {
|
|
4539
|
+
const { visitorIdentity = {}, analytics = {}, guides = {}, sessionReplay = {} } = tier || {};
|
|
4540
|
+
const eventCollection = analytics.eventCollection || {};
|
|
4541
|
+
const analyticsEnabled = analytics.enabled === true;
|
|
4542
|
+
return {
|
|
4543
|
+
identity: visitorIdentity.trackVisitors === true,
|
|
4544
|
+
guides: guides.enabled === true,
|
|
4545
|
+
replay: sessionReplay.enabled === true,
|
|
4546
|
+
analytics: analyticsEnabled ? toAnalyticsEventTypes(eventCollection) : [],
|
|
4547
|
+
guideAnalytics: analyticsEnabled && eventCollection.guideServing === true ? GUIDE_EVENT_TYPES.slice() : [],
|
|
4548
|
+
textCollection: analyticsEnabled ? toTextCollectionLevel(eventCollection.textCollectionMode) : TEXT_COLLECTION_LEVELS[0]
|
|
4549
|
+
};
|
|
4550
|
+
}
|
|
4551
|
+
function deniedProducts() {
|
|
4552
|
+
return {
|
|
4553
|
+
identity: false,
|
|
4554
|
+
guides: false,
|
|
4555
|
+
replay: false,
|
|
4556
|
+
[ANALYTICS]: [],
|
|
4557
|
+
[GUIDE_ANALYTICS]: [],
|
|
4558
|
+
[TEXT_COLLECTION]: TEXT_COLLECTION_LEVELS[0]
|
|
4559
|
+
};
|
|
4560
|
+
}
|
|
4561
|
+
function combineTiers(resolved, granted) {
|
|
4562
|
+
return {
|
|
4563
|
+
identity: resolved.identity || granted.identity,
|
|
4564
|
+
guides: resolved.guides || granted.guides,
|
|
4565
|
+
replay: resolved.replay || granted.replay,
|
|
4566
|
+
[ANALYTICS]: _.union(resolved[ANALYTICS], granted[ANALYTICS]),
|
|
4567
|
+
[GUIDE_ANALYTICS]: _.union(resolved[GUIDE_ANALYTICS], granted[GUIDE_ANALYTICS]),
|
|
4568
|
+
[TEXT_COLLECTION]: moreTextPermissive(resolved[TEXT_COLLECTION], granted[TEXT_COLLECTION])
|
|
4569
|
+
};
|
|
4512
4570
|
}
|
|
4513
|
-
// How a granted tier's value for a product combines with what's been resolved so far.
|
|
4514
|
-
// `initial` doubles as what an ungranted tier contributes -- an unlisted product falls
|
|
4515
|
-
// back to DEFAULT_RESOLVER, the plain "any granted tier turns it on" boolean rule.
|
|
4516
|
-
const RESOLVERS = {
|
|
4517
|
-
[TEXT_COLLECTION]: { initial: TEXT_COLLECTION_LEVELS[0], combine: moreTextPermissive },
|
|
4518
|
-
[ANALYTICS]: { initial: [], combine: unionArrays },
|
|
4519
|
-
[GUIDE_ANALYTICS]: { initial: [], combine: unionArrays }
|
|
4520
|
-
};
|
|
4521
|
-
const DEFAULT_RESOLVER = { initial: false, combine: orBoolean };
|
|
4522
4571
|
/**
|
|
4523
|
-
* Resolves the
|
|
4572
|
+
* Resolves the tiers a visitor consented to into what each product is allowed to do.
|
|
4573
|
+
*
|
|
4574
|
+
* Every product starts denied and only granted tiers contribute, so a tier the visitor
|
|
4575
|
+
* hasn't opted into has no say -- it can't deny anything, and it can't allow anything.
|
|
4524
4576
|
*
|
|
4525
|
-
* A
|
|
4526
|
-
*
|
|
4527
|
-
*
|
|
4528
|
-
* value is combined in, so opting into an additional tier only ever adds capability. Most
|
|
4529
|
-
* products are plain booleans (DEFAULT_RESOLVER); `textCollection`, `analytics`, and
|
|
4530
|
-
* `guideAnalytics` have their own entries in RESOLVERS since none of them is a boolean.
|
|
4577
|
+
* A subscription with no cookie consent configured resolves to an empty map instead, which
|
|
4578
|
+
* every read below treats as unconstrained: consent that was never set up can't deny
|
|
4579
|
+
* anything either.
|
|
4531
4580
|
*
|
|
4532
4581
|
* @access private
|
|
4533
4582
|
* @param {string | string[]} tiers tier or tiers the visitor consented to
|
|
@@ -4536,26 +4585,22 @@ const DEFAULT_RESOLVER = { initial: false, combine: orBoolean };
|
|
|
4536
4585
|
function applyConsentTiers(tiers) {
|
|
4537
4586
|
const cookieConsentCollection = getClassification();
|
|
4538
4587
|
const consentedCookieTiers = toTierList(tiers);
|
|
4539
|
-
const resolved = {};
|
|
4540
4588
|
warnOnUnconfiguredTiers(consentedCookieTiers, cookieConsentCollection);
|
|
4541
|
-
_.
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
});
|
|
4550
|
-
enabledProducts = resolved;
|
|
4589
|
+
if (_.isEmpty(cookieConsentCollection)) {
|
|
4590
|
+
enabledProducts = {};
|
|
4591
|
+
return getEnabledProducts();
|
|
4592
|
+
}
|
|
4593
|
+
enabledProducts = _.reduce(consentedCookieTiers, function (resolved, tier) {
|
|
4594
|
+
const granted = cookieConsentCollection[tier];
|
|
4595
|
+
return granted ? combineTiers(resolved, normalizeTier(granted)) : resolved;
|
|
4596
|
+
}, deniedProducts());
|
|
4551
4597
|
return getEnabledProducts();
|
|
4552
4598
|
}
|
|
4553
4599
|
function getEnabledProducts() {
|
|
4554
4600
|
return enabledProducts ? _.extend({}, enabledProducts) : null;
|
|
4555
4601
|
}
|
|
4556
4602
|
/**
|
|
4557
|
-
* A product is enabled
|
|
4558
|
-
* consent configured at all, or a product no tier ever mentions, there's nothing to deny.
|
|
4603
|
+
* A product is enabled only if some granted tier turned it on or cookie consent has not been set.
|
|
4559
4604
|
*
|
|
4560
4605
|
* @access private
|
|
4561
4606
|
* @param {string} product
|
|
@@ -4569,6 +4614,9 @@ function isProductEnabled(product) {
|
|
|
4569
4614
|
function isTrackingConsentDenied() {
|
|
4570
4615
|
return !isProductEnabled('identity');
|
|
4571
4616
|
}
|
|
4617
|
+
function isGuideConsentDenied() {
|
|
4618
|
+
return !isProductEnabled('guides');
|
|
4619
|
+
}
|
|
4572
4620
|
/**
|
|
4573
4621
|
* Session replay is denied whenever tracking itself is denied (DNT) -- a visitor
|
|
4574
4622
|
* who can't be identified shouldn't have their session recorded either -- or when a
|
|
@@ -4604,6 +4652,9 @@ function hasConsentForAnalyticsEvent(type) {
|
|
|
4604
4652
|
function hasConsentForGuideEvent(type) {
|
|
4605
4653
|
return hasConsentForProduct(type, GUIDE_ANALYTICS);
|
|
4606
4654
|
}
|
|
4655
|
+
function resetConsentPolicy() {
|
|
4656
|
+
enabledProducts = null;
|
|
4657
|
+
}
|
|
4607
4658
|
|
|
4608
4659
|
const allowKeys = function () {
|
|
4609
4660
|
var allowKeys = ConfigReader.get('storage.allowKeys') || '*';
|
|
@@ -22161,14 +22212,29 @@ function ClickActivation() {
|
|
|
22161
22212
|
}
|
|
22162
22213
|
stopEvent(evt);
|
|
22163
22214
|
};
|
|
22215
|
+
// Safari does not focus the badge on mousedown, so focus leaves the app instead.
|
|
22216
|
+
// Apps that re-render on blur can remove the badge before mouseup, and the browser
|
|
22217
|
+
// then generates no click at all. Holding focus where it is avoids that (APP-168412)
|
|
22218
|
+
var preserveFocus = function (evt) {
|
|
22219
|
+
if (evt.preventDefault) {
|
|
22220
|
+
evt.preventDefault();
|
|
22221
|
+
}
|
|
22222
|
+
else {
|
|
22223
|
+
evt.returnValue = false;
|
|
22224
|
+
}
|
|
22225
|
+
};
|
|
22164
22226
|
badge.after('show', function () {
|
|
22165
22227
|
if (!attached) {
|
|
22166
22228
|
attachEvent(badgeElem, 'click', click);
|
|
22229
|
+
if (badge.preserveFocusOnClick) {
|
|
22230
|
+
attachEvent(badgeElem, 'mousedown', preserveFocus);
|
|
22231
|
+
}
|
|
22167
22232
|
attached = true;
|
|
22168
22233
|
}
|
|
22169
22234
|
});
|
|
22170
22235
|
badge.after('hide', function () {
|
|
22171
22236
|
detachEvent(badgeElem, 'click', click);
|
|
22237
|
+
detachEvent(badgeElem, 'mousedown', preserveFocus);
|
|
22172
22238
|
attached = false;
|
|
22173
22239
|
});
|
|
22174
22240
|
return badge;
|
|
@@ -22639,7 +22705,7 @@ function stepShowingProc(guide, step) {
|
|
|
22639
22705
|
return;
|
|
22640
22706
|
}
|
|
22641
22707
|
const embedSelector = _.get(guide, 'attributes.embedConfig.selector');
|
|
22642
|
-
if (embedSelector && !dom(embedSelector).length) {
|
|
22708
|
+
if (embedSelector && (!dom(embedSelector).length || (guideElement && !isInDocument(guideElement[0])))) {
|
|
22643
22709
|
step.hide();
|
|
22644
22710
|
return;
|
|
22645
22711
|
}
|
|
@@ -25383,6 +25449,9 @@ var initGuides = function (observer) {
|
|
|
25383
25449
|
return;
|
|
25384
25450
|
guides ? enableGuides() : disableGuides();
|
|
25385
25451
|
});
|
|
25452
|
+
if (isGuideConsentDenied()) {
|
|
25453
|
+
disableGuides();
|
|
25454
|
+
}
|
|
25386
25455
|
teardownFns.push(onTurbolinksPageLoad(document, function onPageLoad() {
|
|
25387
25456
|
if (pendoDotUrl.get() === reloadGuides.lastUrl) {
|
|
25388
25457
|
// Force a reload if Turbolinks replaces the document body without changing the url
|
|
@@ -29325,6 +29394,7 @@ const PluginAPI = {
|
|
|
29325
29394
|
q,
|
|
29326
29395
|
SendQueue,
|
|
29327
29396
|
sessionStorage: agentStorage.getSession(),
|
|
29397
|
+
storageIsDisabled,
|
|
29328
29398
|
sizeElements,
|
|
29329
29399
|
store,
|
|
29330
29400
|
Tombstone,
|
|
@@ -29503,6 +29573,34 @@ function initIdentityEvents() {
|
|
|
29503
29573
|
};
|
|
29504
29574
|
}
|
|
29505
29575
|
|
|
29576
|
+
/**
|
|
29577
|
+
* Enforces the cookie consent policy a visitor has agreed to. The policy is defined by the subscription's cookie consent configuration,
|
|
29578
|
+
* and is used to determine whether each product the web SDK may run.
|
|
29579
|
+
*
|
|
29580
|
+
* @access public
|
|
29581
|
+
* @category Core
|
|
29582
|
+
* @param {string | string[]} tiers tier or tiers the visitor consented to
|
|
29583
|
+
* @returns {object} map of product to its resolved value -- `guides` and `identity` are
|
|
29584
|
+
* booleans (identity disabled means DNT), `textCollection` is `none`/`whitelist`/`all`,
|
|
29585
|
+
* `analytics` and `guideAnalytics` are each an array of allowed event types
|
|
29586
|
+
* @example
|
|
29587
|
+
* pendo.updateCookieConsent('strictlyNecessary');
|
|
29588
|
+
* @example
|
|
29589
|
+
* pendo.updateCookieConsent(['strictlyNecessary', 'functional']);
|
|
29590
|
+
*/
|
|
29591
|
+
function updateCookieConsent(tiers) {
|
|
29592
|
+
const enabledProducts = applyConsentTiers(tiers);
|
|
29593
|
+
Events['consent:changed'].trigger(enabledProducts);
|
|
29594
|
+
log.debug('Cookie consent updated for ' + JSON.stringify(tiers) + '.');
|
|
29595
|
+
return enabledProducts;
|
|
29596
|
+
}
|
|
29597
|
+
function initializeCookieConsent(tiers) {
|
|
29598
|
+
resetConsentPolicy();
|
|
29599
|
+
if (tiers) {
|
|
29600
|
+
updateCookieConsent(tiers);
|
|
29601
|
+
}
|
|
29602
|
+
}
|
|
29603
|
+
|
|
29506
29604
|
/*
|
|
29507
29605
|
* ResourceCenterActivity Event
|
|
29508
29606
|
*
|
|
@@ -29986,6 +30084,8 @@ function initialize(options) {
|
|
|
29986
30084
|
}
|
|
29987
30085
|
// Save the options somewhere
|
|
29988
30086
|
ConfigReader.setLocalConfig(options);
|
|
30087
|
+
const cookieConsentTiers = _.get(options, 'cookieConsentTiers');
|
|
30088
|
+
initializeCookieConsent(cookieConsentTiers);
|
|
29989
30089
|
initDataHost();
|
|
29990
30090
|
exportDataHost(pendo$1);
|
|
29991
30091
|
const cookieDomain = ConfigReader.get('cookieDomain');
|
|
@@ -31232,28 +31332,6 @@ logPublic.setActiveContexts = setActiveContexts;
|
|
|
31232
31332
|
logPublic.showLogHistory = showLogHistory;
|
|
31233
31333
|
logPublic.getLoggedContexts = getLoggedContexts;
|
|
31234
31334
|
|
|
31235
|
-
/**
|
|
31236
|
-
* Enforces the cookie consent policy a visitor has agreed to. The policy is defined by the subscription's cookie consent configuration,
|
|
31237
|
-
* and is used to determine whether each product the web SDK may run.
|
|
31238
|
-
*
|
|
31239
|
-
* @access public
|
|
31240
|
-
* @category Core
|
|
31241
|
-
* @param {string | string[]} tiers tier or tiers the visitor consented to
|
|
31242
|
-
* @returns {object} map of product to its resolved value -- `guides` and `identity` are
|
|
31243
|
-
* booleans (identity disabled means DNT), `textCollection` is `none`/`whitelist`/`all`,
|
|
31244
|
-
* `analytics` and `guideAnalytics` are each an array of allowed event types
|
|
31245
|
-
* @example
|
|
31246
|
-
* pendo.updateCookieConsent('strictlyNecessary');
|
|
31247
|
-
* @example
|
|
31248
|
-
* pendo.updateCookieConsent(['strictlyNecessary', 'functional']);
|
|
31249
|
-
*/
|
|
31250
|
-
function updateCookieConsent(tiers) {
|
|
31251
|
-
const enabledProducts = applyConsentTiers(tiers);
|
|
31252
|
-
Events['consent:changed'].trigger(enabledProducts);
|
|
31253
|
-
log.debug('Cookie consent updated for ' + JSON.stringify(tiers) + '.');
|
|
31254
|
-
return enabledProducts;
|
|
31255
|
-
}
|
|
31256
|
-
|
|
31257
31335
|
/**
|
|
31258
31336
|
* Shared helper for event collection
|
|
31259
31337
|
*/
|
|
@@ -42735,6 +42813,310 @@ var guideMarkdownUtil = {
|
|
|
42735
42813
|
removeMarkdownSyntax
|
|
42736
42814
|
};
|
|
42737
42815
|
|
|
42816
|
+
const AUTO_ADVANCE_DELAY_MS = 250;
|
|
42817
|
+
function isAutoAdvanceRequested(step, _) {
|
|
42818
|
+
return _.get(step, 'attributes.autoAdvance') === true;
|
|
42819
|
+
}
|
|
42820
|
+
// Survey-authored DOM carries no question-type classes, so eligibility keys off
|
|
42821
|
+
// answer-control shape: every required poll id must be radio-backed, which also
|
|
42822
|
+
// refuses a future non-radio type by default.
|
|
42823
|
+
function isStepEligibleInDom(step, _, requiredPollIds) {
|
|
42824
|
+
const guideElement = step && step.guideElement;
|
|
42825
|
+
if (!guideElement)
|
|
42826
|
+
return false;
|
|
42827
|
+
if (guideElement.find('textarea, input:text, input:checkbox, select').length)
|
|
42828
|
+
return false;
|
|
42829
|
+
const pollIds = _.uniq(_.map(guideElement.find('[data-pendo-poll-id]'), (element) => element.getAttribute('data-pendo-poll-id')));
|
|
42830
|
+
if (!pollIds.length)
|
|
42831
|
+
return false;
|
|
42832
|
+
return _.all(pollIds, (pollId) => _.contains(requiredPollIds, pollId) &&
|
|
42833
|
+
guideElement.find(`input:radio[data-pendo-poll-id="${pollId}"]`).length > 0);
|
|
42834
|
+
}
|
|
42835
|
+
function hideSubmitButtons(step, _, submitButtons) {
|
|
42836
|
+
const hiddenButtons = [];
|
|
42837
|
+
_.each(submitButtons, (button) => {
|
|
42838
|
+
const element = step.guideElement.find(`#${button.props.id}`)[0];
|
|
42839
|
+
if (!element)
|
|
42840
|
+
return;
|
|
42841
|
+
// display, not visibility: a reserved-but-invisible box leaves an empty gap
|
|
42842
|
+
// at the foot of the step. Authored styles reach the button inline
|
|
42843
|
+
// (build.js:151), so the original value is kept rather than blanked.
|
|
42844
|
+
hiddenButtons.push({ element, display: element.style.display });
|
|
42845
|
+
element.style.display = 'none';
|
|
42846
|
+
});
|
|
42847
|
+
return function restoreSubmitButtons() {
|
|
42848
|
+
_.each(hiddenButtons, (hiddenButton) => {
|
|
42849
|
+
hiddenButton.element.style.display = hiddenButton.display;
|
|
42850
|
+
});
|
|
42851
|
+
};
|
|
42852
|
+
}
|
|
42853
|
+
// The container's pixel height is measured during render (layout.js) before custom
|
|
42854
|
+
// scripts run, so collapsing a row afterwards leaves the box stale and the step keeps
|
|
42855
|
+
// the empty space. Same reflow pair metadata-substitution uses, scoped to this step so
|
|
42856
|
+
// a second guide on screen is left alone.
|
|
42857
|
+
function reflowGuide(step, _, pendo) {
|
|
42858
|
+
const recalculateGuideHeight = _.get(pendo, 'BuildingBlocks.BuildingBlockGuides.recalculateGuideHeight');
|
|
42859
|
+
try {
|
|
42860
|
+
if (_.isFunction(pendo.flexElement)) {
|
|
42861
|
+
pendo.flexElement(step.guideElement.find(guideMarkdownUtil.containerSelector));
|
|
42862
|
+
}
|
|
42863
|
+
if (_.isFunction(recalculateGuideHeight)) {
|
|
42864
|
+
recalculateGuideHeight(`pendo-g-${step.id}`, step.guideElement);
|
|
42865
|
+
}
|
|
42866
|
+
}
|
|
42867
|
+
catch (error) {
|
|
42868
|
+
pendo.log('[auto-advance] guide reflow failed, the step may keep an empty row', error && error.message);
|
|
42869
|
+
}
|
|
42870
|
+
}
|
|
42871
|
+
function createInertController() {
|
|
42872
|
+
return { isActive: false, notifyAnswered() { }, cancel() { } };
|
|
42873
|
+
}
|
|
42874
|
+
function createAutoAdvanceController(step, pendo, submitButtons, requiredPollIds) {
|
|
42875
|
+
const _ = pendo._;
|
|
42876
|
+
const inert = createInertController();
|
|
42877
|
+
if (!isAutoAdvanceRequested(step, _))
|
|
42878
|
+
return inert;
|
|
42879
|
+
// Only reachable for API-authored guides; choosing between two
|
|
42880
|
+
// submitPollAndGoToStep targets would be a guess.
|
|
42881
|
+
if (_.size(submitButtons) !== 1) {
|
|
42882
|
+
pendo.log(`[auto-advance] not arming: step ${step.id} needs exactly one submit button, found ${_.size(submitButtons)}`);
|
|
42883
|
+
return inert;
|
|
42884
|
+
}
|
|
42885
|
+
if (!isStepEligibleInDom(step, _, requiredPollIds)) {
|
|
42886
|
+
pendo.log(`[auto-advance] not arming: rendered step ${step.id} failed the eligibility re-check`);
|
|
42887
|
+
return inert;
|
|
42888
|
+
}
|
|
42889
|
+
const submitButton = submitButtons[0];
|
|
42890
|
+
// A click binds every action on the node and dispatches one event each
|
|
42891
|
+
// (build.js:205-207); we dispatch one, so a second action would be dropped.
|
|
42892
|
+
if (_.size(submitButton.actions) !== 1) {
|
|
42893
|
+
pendo.log(`[auto-advance] not arming: step ${step.id} submit button carries ${_.size(submitButton.actions)} actions, expected one`);
|
|
42894
|
+
return inert;
|
|
42895
|
+
}
|
|
42896
|
+
const submitAction = _.find(submitButton.actions, (action) => action.action === 'submitPoll' || action.action === 'submitPollAndGoToStep');
|
|
42897
|
+
if (!submitAction)
|
|
42898
|
+
return inert;
|
|
42899
|
+
let hasFired = false;
|
|
42900
|
+
let restoreSubmitButtons = _.noop;
|
|
42901
|
+
function fire() {
|
|
42902
|
+
if (hasFired)
|
|
42903
|
+
return;
|
|
42904
|
+
hasFired = true;
|
|
42905
|
+
try {
|
|
42906
|
+
// Shape-identical to what bindActionToNode emits for a click, so
|
|
42907
|
+
// beforeAdvance consumers intercept an auto-advance the same way.
|
|
42908
|
+
step.eventRouter.eventable.trigger('pendoEvent', {
|
|
42909
|
+
action: submitAction.action,
|
|
42910
|
+
params: submitAction.parameters,
|
|
42911
|
+
step,
|
|
42912
|
+
ignore: !!submitAction.ignore,
|
|
42913
|
+
srcElement: step.guideElement.find(`#${submitButton.props.id}`)[0]
|
|
42914
|
+
});
|
|
42915
|
+
}
|
|
42916
|
+
catch (error) {
|
|
42917
|
+
restoreSubmitButtons();
|
|
42918
|
+
reflowGuide(step, _, pendo);
|
|
42919
|
+
pendo.log('[auto-advance] submit dispatch failed, restored the submit button', error && error.message);
|
|
42920
|
+
}
|
|
42921
|
+
}
|
|
42922
|
+
const debouncedFire = _.debounce(fire, AUTO_ADVANCE_DELAY_MS);
|
|
42923
|
+
// Last, so nothing between here and the return can throw and leave the step
|
|
42924
|
+
// with a hidden button and no controller to un-hide it.
|
|
42925
|
+
restoreSubmitButtons = hideSubmitButtons(step, _, submitButtons);
|
|
42926
|
+
reflowGuide(step, _, pendo);
|
|
42927
|
+
return {
|
|
42928
|
+
isActive: true,
|
|
42929
|
+
notifyAnswered: debouncedFire,
|
|
42930
|
+
cancel: debouncedFire.cancel
|
|
42931
|
+
};
|
|
42932
|
+
}
|
|
42933
|
+
|
|
42934
|
+
const SURVEY_ANSWERS_KEY = 'pendo_survey_answers';
|
|
42935
|
+
const ANSWER_CONTROLS = 'input:radio, input:text, textarea, select';
|
|
42936
|
+
const TEXT_CONTROLS = 'input:text, textarea';
|
|
42937
|
+
// Matches MULTISTEP_CONTINUATION_TIME_LIMIT (walkthrough-guide.js:67), past which a guide is not continued.
|
|
42938
|
+
const ANSWER_TTL_MS = 12 * 60 * 60 * 1000;
|
|
42939
|
+
let pluginApi = null;
|
|
42940
|
+
let inMemoryAnswers = {};
|
|
42941
|
+
let persistedAnswersDropped = false;
|
|
42942
|
+
// A restricted storage.allowKeys silently rejects the session write, so mirror the visit here.
|
|
42943
|
+
const visitedSteps = {};
|
|
42944
|
+
function setPluginApi(api) {
|
|
42945
|
+
pluginApi = api;
|
|
42946
|
+
if (!api || !api.agentStorage)
|
|
42947
|
+
return;
|
|
42948
|
+
/**
|
|
42949
|
+
* Answers the visitor has given on each step of a survey, so a step re-shown
|
|
42950
|
+
* after Previous comes back filled in rather than blank.
|
|
42951
|
+
*
|
|
42952
|
+
* @name pendo_survey_answers
|
|
42953
|
+
* @category Cookies/sessionStorage
|
|
42954
|
+
* @access public
|
|
42955
|
+
* @label SURVEY_ANSWERS_KEY
|
|
42956
|
+
*/
|
|
42957
|
+
api.agentStorage.registry.addSession(SURVEY_ANSWERS_KEY);
|
|
42958
|
+
}
|
|
42959
|
+
// getSession() checks the allow-list but not storageIsDisabled, which the cookie path applies.
|
|
42960
|
+
function persistenceAllowed() {
|
|
42961
|
+
const allowed = !!pluginApi &&
|
|
42962
|
+
!!pluginApi.sessionStorage &&
|
|
42963
|
+
typeof pluginApi.storageIsDisabled === 'function' &&
|
|
42964
|
+
!pluginApi.storageIsDisabled();
|
|
42965
|
+
if (allowed) {
|
|
42966
|
+
persistedAnswersDropped = false;
|
|
42967
|
+
}
|
|
42968
|
+
else {
|
|
42969
|
+
dropPersistedAnswers();
|
|
42970
|
+
}
|
|
42971
|
+
return allowed;
|
|
42972
|
+
}
|
|
42973
|
+
// agentStorage.clearAll only walks local keys, so a session key has to be dropped here.
|
|
42974
|
+
function dropPersistedAnswers() {
|
|
42975
|
+
if (persistedAnswersDropped || !pluginApi || !pluginApi.sessionStorage)
|
|
42976
|
+
return;
|
|
42977
|
+
persistedAnswersDropped = true;
|
|
42978
|
+
pluginApi.sessionStorage.removeItem(SURVEY_ANSWERS_KEY);
|
|
42979
|
+
}
|
|
42980
|
+
function readAll() {
|
|
42981
|
+
if (!persistenceAllowed())
|
|
42982
|
+
return inMemoryAnswers;
|
|
42983
|
+
try {
|
|
42984
|
+
return JSON.parse(pluginApi.sessionStorage.getItem(SURVEY_ANSWERS_KEY)) || {};
|
|
42985
|
+
}
|
|
42986
|
+
catch (error) {
|
|
42987
|
+
return {};
|
|
42988
|
+
}
|
|
42989
|
+
}
|
|
42990
|
+
function writeAll(answers) {
|
|
42991
|
+
if (!persistenceAllowed()) {
|
|
42992
|
+
inMemoryAnswers = answers;
|
|
42993
|
+
return;
|
|
42994
|
+
}
|
|
42995
|
+
pluginApi.sessionStorage.setItem(SURVEY_ANSWERS_KEY, JSON.stringify(answers));
|
|
42996
|
+
}
|
|
42997
|
+
// identify() sets the new id before the reload tears the step down, so resolve this once per render.
|
|
42998
|
+
function visitorKey(pendo) {
|
|
42999
|
+
return (pendo._.isFunction(pendo.get_visitor_id) && pendo.get_visitor_id()) || 'unknown';
|
|
43000
|
+
}
|
|
43001
|
+
function stateKey(step, guide, visitor) {
|
|
43002
|
+
return `${visitor}:${guide.id}:${step.id}`;
|
|
43003
|
+
}
|
|
43004
|
+
// Host pages reassign Date.now to return a Date, which JSON writes out as a string.
|
|
43005
|
+
function getNow() {
|
|
43006
|
+
return new Date().getTime();
|
|
43007
|
+
}
|
|
43008
|
+
function isFresh(writtenAt) {
|
|
43009
|
+
return !!writtenAt && (getNow() - writtenAt) <= ANSWER_TTL_MS;
|
|
43010
|
+
}
|
|
43011
|
+
function readRecord(step, guide, visitor) {
|
|
43012
|
+
const forVisitor = readAll()[visitor];
|
|
43013
|
+
const forGuide = forVisitor && forVisitor[guide.id];
|
|
43014
|
+
const record = (forGuide && forGuide[step.id]) || null;
|
|
43015
|
+
return isFresh(record && record.at) ? record : null;
|
|
43016
|
+
}
|
|
43017
|
+
function updateRecord(step, guide, visitor, changes) {
|
|
43018
|
+
const all = readAll();
|
|
43019
|
+
const current = readRecord(step, guide, visitor) || {};
|
|
43020
|
+
if (!all[visitor])
|
|
43021
|
+
all[visitor] = {};
|
|
43022
|
+
if (!all[visitor][guide.id])
|
|
43023
|
+
all[visitor][guide.id] = {};
|
|
43024
|
+
all[visitor][guide.id][step.id] = {
|
|
43025
|
+
at: getNow(),
|
|
43026
|
+
visited: changes.visited || current.visited || 0,
|
|
43027
|
+
answers: changes.answers || current.answers || {}
|
|
43028
|
+
};
|
|
43029
|
+
writeAll(all);
|
|
43030
|
+
}
|
|
43031
|
+
// Previous only renders after an auto-advance step, so any other guide needs none of this.
|
|
43032
|
+
function guideAutoAdvances(guide, _) {
|
|
43033
|
+
return _.any(_.get(guide, 'steps', []), (step) => isAutoAdvanceRequested(step, _));
|
|
43034
|
+
}
|
|
43035
|
+
function pollIdFor(control) {
|
|
43036
|
+
let node = control;
|
|
43037
|
+
while (node && node.getAttribute) {
|
|
43038
|
+
const pollId = node.getAttribute('data-pendo-poll-id');
|
|
43039
|
+
if (pollId)
|
|
43040
|
+
return pollId;
|
|
43041
|
+
node = node.parentNode;
|
|
43042
|
+
}
|
|
43043
|
+
return null;
|
|
43044
|
+
}
|
|
43045
|
+
// submitPoll never reads a checkbox back (event-router.js:264), so restoring one shows nothing sent.
|
|
43046
|
+
function isRadio(control) {
|
|
43047
|
+
return control.type === 'radio';
|
|
43048
|
+
}
|
|
43049
|
+
function collectAnswers(step, pendo) {
|
|
43050
|
+
const answers = {};
|
|
43051
|
+
pendo._.each(step.guideElement.find(ANSWER_CONTROLS), (control) => {
|
|
43052
|
+
const pollId = pollIdFor(control);
|
|
43053
|
+
// One value per poll id, first in document order, as submitPoll reads it.
|
|
43054
|
+
if (!pollId || answers[pollId])
|
|
43055
|
+
return;
|
|
43056
|
+
if (isRadio(control) && !control.checked)
|
|
43057
|
+
return;
|
|
43058
|
+
if (!control.value)
|
|
43059
|
+
return;
|
|
43060
|
+
answers[pollId] = [control.value];
|
|
43061
|
+
});
|
|
43062
|
+
return answers;
|
|
43063
|
+
}
|
|
43064
|
+
// No event dispatched: a restored answer must not reach the gate listeners or re-arm auto-advance.
|
|
43065
|
+
function restoreAnswers(step, pendo, answers) {
|
|
43066
|
+
pendo._.each(step.guideElement.find(ANSWER_CONTROLS), (control) => {
|
|
43067
|
+
const pollId = pollIdFor(control);
|
|
43068
|
+
const values = pollId && answers[pollId];
|
|
43069
|
+
if (!values)
|
|
43070
|
+
return;
|
|
43071
|
+
if (isRadio(control)) {
|
|
43072
|
+
if (pendo._.contains(values, control.value))
|
|
43073
|
+
control.checked = true;
|
|
43074
|
+
return;
|
|
43075
|
+
}
|
|
43076
|
+
control.value = values[0];
|
|
43077
|
+
});
|
|
43078
|
+
}
|
|
43079
|
+
function isRevisit(step, guide, pendo) {
|
|
43080
|
+
if (!guideAutoAdvances(guide, pendo._))
|
|
43081
|
+
return false;
|
|
43082
|
+
const visitor = visitorKey(pendo);
|
|
43083
|
+
if (isFresh(visitedSteps[stateKey(step, guide, visitor)]))
|
|
43084
|
+
return true;
|
|
43085
|
+
const record = readRecord(step, guide, visitor);
|
|
43086
|
+
return !!record && (!!record.visited || !pendo._.isEmpty(record.answers));
|
|
43087
|
+
}
|
|
43088
|
+
const AnswerCache = {
|
|
43089
|
+
name: 'AnswerCache',
|
|
43090
|
+
script(step, guide, pendo) {
|
|
43091
|
+
const visitor = visitorKey(pendo);
|
|
43092
|
+
const record = readRecord(step, guide, visitor);
|
|
43093
|
+
restoreAnswers(step, pendo, (record && record.answers) || {});
|
|
43094
|
+
// Marked on leaving, not on render: sessionStorage survives a reload, which is not a revisit.
|
|
43095
|
+
this.on('beforeUnmount', (event) => {
|
|
43096
|
+
if (event && event.reason === 'hidden')
|
|
43097
|
+
return;
|
|
43098
|
+
visitedSteps[stateKey(step, guide, visitor)] = getNow();
|
|
43099
|
+
updateRecord(step, guide, visitor, { visited: 1 });
|
|
43100
|
+
});
|
|
43101
|
+
const stepRoot = step.guideElement[0];
|
|
43102
|
+
// change does not bubble on legacy browsers; it is here only for keyboard selection.
|
|
43103
|
+
step.attachEvent(stepRoot, 'click', capture);
|
|
43104
|
+
step.attachEvent(stepRoot, 'change', capture);
|
|
43105
|
+
pendo._.each(step.guideElement.find(TEXT_CONTROLS), (control) => {
|
|
43106
|
+
step.attachEvent(control, 'input', capture);
|
|
43107
|
+
});
|
|
43108
|
+
function capture() {
|
|
43109
|
+
updateRecord(step, guide, visitor, { answers: collectAnswers(step, pendo) });
|
|
43110
|
+
}
|
|
43111
|
+
},
|
|
43112
|
+
test(step, guide, pendo) {
|
|
43113
|
+
const guideElement = step && step.guideElement;
|
|
43114
|
+
return !!guideElement &&
|
|
43115
|
+
guideAutoAdvances(guide, pendo._) &&
|
|
43116
|
+
guideElement.find(ANSWER_CONTROLS).length > 0;
|
|
43117
|
+
}
|
|
43118
|
+
};
|
|
43119
|
+
|
|
42738
43120
|
function getZoneSafeMethod(_, method, target = window) {
|
|
42739
43121
|
var zoneSymbol = '__symbol__';
|
|
42740
43122
|
/* global Zone */
|
|
@@ -43211,124 +43593,6 @@ function substitutionIcon(color, size) {
|
|
|
43211
43593
|
13.7239 15.5 14V17Z" fill="${color}"></path> </g></svg></div>`);
|
|
43212
43594
|
}
|
|
43213
43595
|
|
|
43214
|
-
const AUTO_ADVANCE_DELAY_MS = 250;
|
|
43215
|
-
function isAutoAdvanceRequested(step, _) {
|
|
43216
|
-
return _.get(step, 'attributes.autoAdvance') === true;
|
|
43217
|
-
}
|
|
43218
|
-
// Survey-authored DOM carries no question-type classes, so eligibility keys off
|
|
43219
|
-
// answer-control shape: every required poll id must be radio-backed, which also
|
|
43220
|
-
// refuses a future non-radio type by default.
|
|
43221
|
-
function isStepEligibleInDom(step, _, requiredPollIds) {
|
|
43222
|
-
const guideElement = step && step.guideElement;
|
|
43223
|
-
if (!guideElement)
|
|
43224
|
-
return false;
|
|
43225
|
-
if (guideElement.find('textarea, input:text, input:checkbox, select').length)
|
|
43226
|
-
return false;
|
|
43227
|
-
const pollIds = _.uniq(_.map(guideElement.find('[data-pendo-poll-id]'), (element) => element.getAttribute('data-pendo-poll-id')));
|
|
43228
|
-
if (!pollIds.length)
|
|
43229
|
-
return false;
|
|
43230
|
-
return _.all(pollIds, (pollId) => _.contains(requiredPollIds, pollId) &&
|
|
43231
|
-
guideElement.find(`input:radio[data-pendo-poll-id="${pollId}"]`).length > 0);
|
|
43232
|
-
}
|
|
43233
|
-
function hideSubmitButtons(step, _, submitButtons) {
|
|
43234
|
-
const hiddenButtons = [];
|
|
43235
|
-
_.each(submitButtons, (button) => {
|
|
43236
|
-
const element = step.guideElement.find(`#${button.props.id}`)[0];
|
|
43237
|
-
if (!element)
|
|
43238
|
-
return;
|
|
43239
|
-
// display, not visibility: a reserved-but-invisible box leaves an empty gap
|
|
43240
|
-
// at the foot of the step. Authored styles reach the button inline
|
|
43241
|
-
// (build.js:151), so the original value is kept rather than blanked.
|
|
43242
|
-
hiddenButtons.push({ element, display: element.style.display });
|
|
43243
|
-
element.style.display = 'none';
|
|
43244
|
-
});
|
|
43245
|
-
return function restoreSubmitButtons() {
|
|
43246
|
-
_.each(hiddenButtons, (hiddenButton) => {
|
|
43247
|
-
hiddenButton.element.style.display = hiddenButton.display;
|
|
43248
|
-
});
|
|
43249
|
-
};
|
|
43250
|
-
}
|
|
43251
|
-
// The container's pixel height is measured during render (layout.js) before custom
|
|
43252
|
-
// scripts run, so collapsing a row afterwards leaves the box stale and the step keeps
|
|
43253
|
-
// the empty space. Same reflow pair metadata-substitution uses, scoped to this step so
|
|
43254
|
-
// a second guide on screen is left alone.
|
|
43255
|
-
function reflowGuide(step, _, pendo) {
|
|
43256
|
-
const recalculateGuideHeight = _.get(pendo, 'BuildingBlocks.BuildingBlockGuides.recalculateGuideHeight');
|
|
43257
|
-
try {
|
|
43258
|
-
if (_.isFunction(pendo.flexElement)) {
|
|
43259
|
-
pendo.flexElement(step.guideElement.find(guideMarkdownUtil.containerSelector));
|
|
43260
|
-
}
|
|
43261
|
-
if (_.isFunction(recalculateGuideHeight)) {
|
|
43262
|
-
recalculateGuideHeight(`pendo-g-${step.id}`, step.guideElement);
|
|
43263
|
-
}
|
|
43264
|
-
}
|
|
43265
|
-
catch (error) {
|
|
43266
|
-
pendo.log('[auto-advance] guide reflow failed, the step may keep an empty row', error && error.message);
|
|
43267
|
-
}
|
|
43268
|
-
}
|
|
43269
|
-
function createInertController() {
|
|
43270
|
-
return { isActive: false, notifyAnswered() { }, cancel() { } };
|
|
43271
|
-
}
|
|
43272
|
-
function createAutoAdvanceController(step, pendo, submitButtons, requiredPollIds) {
|
|
43273
|
-
const _ = pendo._;
|
|
43274
|
-
const inert = createInertController();
|
|
43275
|
-
if (!isAutoAdvanceRequested(step, _))
|
|
43276
|
-
return inert;
|
|
43277
|
-
// Only reachable for API-authored guides; choosing between two
|
|
43278
|
-
// submitPollAndGoToStep targets would be a guess.
|
|
43279
|
-
if (_.size(submitButtons) !== 1) {
|
|
43280
|
-
pendo.log(`[auto-advance] not arming: step ${step.id} needs exactly one submit button, found ${_.size(submitButtons)}`);
|
|
43281
|
-
return inert;
|
|
43282
|
-
}
|
|
43283
|
-
if (!isStepEligibleInDom(step, _, requiredPollIds)) {
|
|
43284
|
-
pendo.log(`[auto-advance] not arming: rendered step ${step.id} failed the eligibility re-check`);
|
|
43285
|
-
return inert;
|
|
43286
|
-
}
|
|
43287
|
-
const submitButton = submitButtons[0];
|
|
43288
|
-
// A click binds every action on the node and dispatches one event each
|
|
43289
|
-
// (build.js:205-207); we dispatch one, so a second action would be dropped.
|
|
43290
|
-
if (_.size(submitButton.actions) !== 1) {
|
|
43291
|
-
pendo.log(`[auto-advance] not arming: step ${step.id} submit button carries ${_.size(submitButton.actions)} actions, expected one`);
|
|
43292
|
-
return inert;
|
|
43293
|
-
}
|
|
43294
|
-
const submitAction = _.find(submitButton.actions, (action) => action.action === 'submitPoll' || action.action === 'submitPollAndGoToStep');
|
|
43295
|
-
if (!submitAction)
|
|
43296
|
-
return inert;
|
|
43297
|
-
let hasFired = false;
|
|
43298
|
-
let restoreSubmitButtons = _.noop;
|
|
43299
|
-
function fire() {
|
|
43300
|
-
if (hasFired)
|
|
43301
|
-
return;
|
|
43302
|
-
hasFired = true;
|
|
43303
|
-
try {
|
|
43304
|
-
// Shape-identical to what bindActionToNode emits for a click, so
|
|
43305
|
-
// beforeAdvance consumers intercept an auto-advance the same way.
|
|
43306
|
-
step.eventRouter.eventable.trigger('pendoEvent', {
|
|
43307
|
-
action: submitAction.action,
|
|
43308
|
-
params: submitAction.parameters,
|
|
43309
|
-
step,
|
|
43310
|
-
ignore: !!submitAction.ignore,
|
|
43311
|
-
srcElement: step.guideElement.find(`#${submitButton.props.id}`)[0]
|
|
43312
|
-
});
|
|
43313
|
-
}
|
|
43314
|
-
catch (error) {
|
|
43315
|
-
restoreSubmitButtons();
|
|
43316
|
-
reflowGuide(step, _, pendo);
|
|
43317
|
-
pendo.log('[auto-advance] submit dispatch failed, restored the submit button', error && error.message);
|
|
43318
|
-
}
|
|
43319
|
-
}
|
|
43320
|
-
const debouncedFire = _.debounce(fire, AUTO_ADVANCE_DELAY_MS);
|
|
43321
|
-
// Last, so nothing between here and the return can throw and leave the step
|
|
43322
|
-
// with a hidden button and no controller to un-hide it.
|
|
43323
|
-
restoreSubmitButtons = hideSubmitButtons(step, _, submitButtons);
|
|
43324
|
-
reflowGuide(step, _, pendo);
|
|
43325
|
-
return {
|
|
43326
|
-
isActive: true,
|
|
43327
|
-
notifyAnswered: debouncedFire,
|
|
43328
|
-
cancel: debouncedFire.cancel
|
|
43329
|
-
};
|
|
43330
|
-
}
|
|
43331
|
-
|
|
43332
43596
|
const requiredElement = '<span class="_pendo-required-indicator" style="color:red; font-style:italic;" title="Question is required"> *</span>';
|
|
43333
43597
|
const requiredSyntax = '{required/}';
|
|
43334
43598
|
const RequiredQuestions = {
|
|
@@ -43354,9 +43618,14 @@ const RequiredQuestions = {
|
|
|
43354
43618
|
}
|
|
43355
43619
|
});
|
|
43356
43620
|
}
|
|
43621
|
+
// Restored answers reach no listener, and the controller is still inert here.
|
|
43622
|
+
const revisited = isRevisit(step, guide, pendo);
|
|
43623
|
+
if (revisited) {
|
|
43624
|
+
evaluateRequiredQuestions(requiredQuestions);
|
|
43625
|
+
}
|
|
43357
43626
|
// Armed last, after the listeners above: runCustomScripts abandons the rest of
|
|
43358
43627
|
// this script on a throw, and the submit button is already disabled by now.
|
|
43359
|
-
if (isAutoAdvanceRequested(step, pendo._)) {
|
|
43628
|
+
if (isAutoAdvanceRequested(step, pendo._) && !revisited) {
|
|
43360
43629
|
this.on('beforeUnmount', () => autoAdvance.cancel());
|
|
43361
43630
|
try {
|
|
43362
43631
|
autoAdvance = createAutoAdvanceController(step, pendo, submitButtons, requiredPollIds);
|
|
@@ -43707,7 +43976,9 @@ function GuideMarkdown() {
|
|
|
43707
43976
|
const markdownScriptsEnabled = configReader.get(GUIDEMARKDOWN_CONFIG);
|
|
43708
43977
|
if (!markdownScriptsEnabled)
|
|
43709
43978
|
return;
|
|
43710
|
-
|
|
43979
|
+
setPluginApi(PluginAPI);
|
|
43980
|
+
// AnswerCache first: it restores the answers RequiredQuestions reads the gate against.
|
|
43981
|
+
guideMarkdown = [AnswerCache, PollBranching, MetadataSubstitution, RequiredQuestions, SkipToEligibleStep];
|
|
43711
43982
|
if (!pendo.designer) {
|
|
43712
43983
|
pendo._.each(guideMarkdown, function (script) {
|
|
43713
43984
|
PluginAPI.GlobalRuntime.addGlobalScript(script);
|
|
@@ -51498,11 +51769,13 @@ class Transport {
|
|
|
51498
51769
|
}
|
|
51499
51770
|
}
|
|
51500
51771
|
|
|
51501
|
-
const CATCHALL_TYPES = ['load', 'click', 'track'];
|
|
51772
|
+
const CATCHALL_TYPES = ['load', 'click', 'track', 'guideSeen'];
|
|
51773
|
+
const AGENTIC_TYPES = ["prompt", "agent_response", "user_reaction"];
|
|
51502
51774
|
const EVENT_KINDS = {
|
|
51503
|
-
|
|
51504
|
-
|
|
51505
|
-
|
|
51775
|
+
AGENTIC: 'agentic',
|
|
51776
|
+
CATCHALL: 'catchall',
|
|
51777
|
+
DEVLOG: 'devlog',
|
|
51778
|
+
RECORDING: 'recording'
|
|
51506
51779
|
};
|
|
51507
51780
|
class LiveReplayClient {
|
|
51508
51781
|
constructor(pendo, api) {
|
|
@@ -51583,18 +51856,21 @@ class LiveReplayClient {
|
|
|
51583
51856
|
catch (_a) { }
|
|
51584
51857
|
}
|
|
51585
51858
|
sendRecordingEvent(fields) {
|
|
51586
|
-
this.send(Object.assign(Object.assign({}, fields), { kind: EVENT_KINDS.
|
|
51859
|
+
this.send(Object.assign(Object.assign({}, fields), { kind: EVENT_KINDS.RECORDING, sequence: this.sequence++ }));
|
|
51587
51860
|
}
|
|
51588
51861
|
forwardEvent(event) {
|
|
51589
51862
|
if (!event || !event.data || !event.data.length)
|
|
51590
51863
|
return;
|
|
51591
51864
|
const capturedEvent = event.data[0];
|
|
51592
51865
|
const config = { event: capturedEvent };
|
|
51866
|
+
if (this.pendo._.contains(AGENTIC_TYPES, capturedEvent.type)) {
|
|
51867
|
+
config.kind = EVENT_KINDS.AGENTIC;
|
|
51868
|
+
}
|
|
51593
51869
|
if (this.pendo._.contains(CATCHALL_TYPES, capturedEvent.type)) {
|
|
51594
|
-
config.kind = EVENT_KINDS.
|
|
51870
|
+
config.kind = EVENT_KINDS.CATCHALL;
|
|
51595
51871
|
}
|
|
51596
|
-
if (capturedEvent.type === EVENT_KINDS.
|
|
51597
|
-
config.kind = EVENT_KINDS.
|
|
51872
|
+
if (capturedEvent.type === EVENT_KINDS.DEVLOG) {
|
|
51873
|
+
config.kind = EVENT_KINDS.DEVLOG;
|
|
51598
51874
|
}
|
|
51599
51875
|
if (!config.kind)
|
|
51600
51876
|
return;
|
|
@@ -52181,7 +52457,6 @@ class SessionRecorder {
|
|
|
52181
52457
|
var bind = this.pendo._.bind;
|
|
52182
52458
|
this.visitorConfig = visitorConfig;
|
|
52183
52459
|
this.clearOldSessionId();
|
|
52184
|
-
this.liveReplayClient.onLiveFlag(visitorConfig.live, this.visitorId);
|
|
52185
52460
|
this.sendQueue.start();
|
|
52186
52461
|
this.interval = setInterval(() => {
|
|
52187
52462
|
if (!this.sendQueue.failed()) {
|
|
@@ -52194,6 +52469,7 @@ class SessionRecorder {
|
|
|
52194
52469
|
errorHandler: this.errorHandler
|
|
52195
52470
|
}, config));
|
|
52196
52471
|
this._refreshIds();
|
|
52472
|
+
this.liveReplayClient.onLiveFlag(visitorConfig.live, this.visitorId, () => this.snapshot());
|
|
52197
52473
|
this.onRecordingStart();
|
|
52198
52474
|
}
|
|
52199
52475
|
catch (e) {
|