posthog-js
Version:
Posthog-js allows you to automatically capture usage and send events to PostHog.
978 lines • 50.7 kB
JavaScript
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProductTourManager = void 0;
exports.doesTourUrlMatch = doesTourUrlMatch;
var jsx_runtime_1 = require("preact/jsx-runtime");
var preact_1 = require("preact");
var posthog_product_tours_types_1 = require("../../posthog-product-tours-types");
var posthog_surveys_types_1 = require("../../posthog-surveys-types");
var product_tours_utils_1 = require("./product-tours-utils");
var ProductTourTooltip_1 = require("./components/ProductTourTooltip");
var ProductTourBanner_1 = require("./components/ProductTourBanner");
var logger_1 = require("@posthog/browser-common/utils/logger");
var globals_1 = require("@posthog/browser-common/utils/globals");
var storage_1 = require("../../storage");
var general_utils_1 = require("@posthog/browser-common/utils/general-utils");
var core_1 = require("@posthog/core");
var property_utils_1 = require("@posthog/browser-common/utils/property-utils");
var url_targeting_utils_1 = require("@posthog/browser-common/utils/url-targeting-utils");
var constants_1 = require("./constants");
var product_tour_utils_1 = require("../../utils/product-tour-utils");
var constants_2 = require("@posthog/browser-common/constants");
var product_tour_event_receiver_1 = require("../../utils/product-tour-event-receiver");
var event_utils_1 = require("@posthog/browser-common/utils/event-utils");
var matcher_utils_1 = require("@posthog/browser-common/utils/matcher-utils");
var logger = (0, logger_1.createLogger)('[Product Tours]');
var document = globals_1.document;
var window = globals_1.window;
// cache the last-checked URL to avoid unnecessary repeated checks on every tick
var _lastUrlMatchHref;
var _urlMatchCache = new Map(); // tour ID : match result
function doesTourUrlMatch(tour, instance) {
var conditions = tour.conditions;
if (!(conditions === null || conditions === void 0 ? void 0 : conditions.url)) {
return true;
}
var href = (0, url_targeting_utils_1.getTargetingUrl)(instance);
if (!href) {
return false;
}
if (href !== _lastUrlMatchHref) {
_urlMatchCache.clear();
_lastUrlMatchHref = href;
}
var cached = _urlMatchCache.get(tour.id);
if (!(0, core_1.isUndefined)(cached)) {
return cached;
}
var matchType = conditions.urlMatchType || core_1.SurveyMatchType.Icontains;
var result;
if (matchType === core_1.SurveyMatchType.Exact) {
result = (0, product_tours_utils_1.normalizeUrl)(href) === (0, product_tours_utils_1.normalizeUrl)(conditions.url);
}
else {
var targets = [conditions.url];
result = property_utils_1.propertyComparisons[matchType](targets, [href]);
}
_urlMatchCache.set(tour.id, result);
return result;
}
function isTourInDateRange(tour) {
var now = new Date();
if (tour.start_date) {
var startDate = new Date(tour.start_date);
if (now < startDate) {
return false;
}
}
if (tour.end_date) {
var endDate = new Date(tour.end_date);
if (now > endDate) {
return false;
}
}
return true;
}
function checkTourConditions(tour, instance) {
var _a;
return (isTourInDateRange(tour) && doesTourUrlMatch(tour, instance) && (0, matcher_utils_1.doesDeviceTypeMatch)((_a = tour.conditions) === null || _a === void 0 ? void 0 : _a.deviceTypes));
}
var CONTAINER_CLASS = 'ph-product-tour-container';
var TRIGGER_LISTENER_ATTRIBUTE = 'data-ph-tour-trigger';
var CHECK_INTERVAL_MS = 1000;
function retrieveTourShadow(tour, posthog) {
var containerClass = "".concat(CONTAINER_CLASS, "-").concat(tour.id);
var existingDiv = document.querySelector(".".concat(containerClass));
if (existingDiv && existingDiv.shadowRoot) {
return {
shadow: existingDiv.shadowRoot,
isNewlyCreated: false,
};
}
var div = document.createElement('div');
div.className = containerClass;
(0, product_tours_utils_1.addProductTourCSSVariablesToElement)(div, tour.appearance);
var shadow = div.attachShadow({ mode: 'open' });
var stylesheet = (0, product_tours_utils_1.getProductTourStylesheet)(posthog);
if (stylesheet) {
shadow.appendChild(stylesheet);
}
document.body.appendChild(div);
return {
shadow: shadow,
isNewlyCreated: true,
};
}
function retrieveBannerShadow(tour, posthog, bannerConfig) {
var _a;
var containerClass = "".concat(CONTAINER_CLASS, "-").concat(tour.id);
var existingDiv = document.querySelector(".".concat(containerClass));
if (existingDiv && existingDiv.shadowRoot) {
return {
shadow: existingDiv.shadowRoot,
isNewlyCreated: false,
};
}
var div = document.createElement('div');
div.className = containerClass;
(0, product_tours_utils_1.addProductTourCSSVariablesToElement)(div, tour.appearance);
if (!(0, core_1.isUndefined)((_a = bannerConfig === null || bannerConfig === void 0 ? void 0 : bannerConfig.animation) === null || _a === void 0 ? void 0 : _a.duration)) {
div.style.setProperty('--ph-tour-banner-animation-duration', "".concat(bannerConfig.animation.duration, "ms"));
}
var shadow = div.attachShadow({ mode: 'open' });
var stylesheet = (0, product_tours_utils_1.getProductTourStylesheet)(posthog);
if (stylesheet) {
shadow.appendChild(stylesheet);
}
if ((bannerConfig === null || bannerConfig === void 0 ? void 0 : bannerConfig.behavior) === 'custom' && bannerConfig.selector) {
var customContainer = document.querySelector(bannerConfig.selector);
if (customContainer) {
customContainer.appendChild(div);
}
else {
logger.warn("Custom banner container not found: ".concat(bannerConfig.selector, ". Banner will not be displayed."));
return null;
}
}
else {
document.body.insertBefore(div, document.body.firstChild);
}
return {
shadow: shadow,
isNewlyCreated: true,
};
}
function removeTourFromDom(tourId) {
var containerClass = "".concat(CONTAINER_CLASS, "-").concat(tourId);
var container = document.querySelector(".".concat(containerClass));
if (container === null || container === void 0 ? void 0 : container.shadowRoot) {
(0, preact_1.render)(null, container.shadowRoot);
}
container === null || container === void 0 ? void 0 : container.remove();
}
var PRODUCT_TOUR_TARGETING_FLAG_PREFIX = 'product-tour-targeting-';
var ProductTourManager = /** @class */ (function () {
function ProductTourManager(instance) {
var _this = this;
this._activeTour = null;
this._currentStepIndex = 0;
this._isPreviewMode = false;
this._isResuming = false;
this._checkInterval = null;
this._triggerSelectorListeners = new Map();
this._pendingTourTimeouts = new Map();
this._registeredEventTourIds = new Set();
this._preloadedImageUrls = new Set();
this._handleVisibilityChange = function () {
if (document.hidden && _this._checkInterval) {
clearInterval(_this._checkInterval);
_this._checkInterval = null;
}
else if (!document.hidden && !_this._checkInterval) {
_this._checkInterval = setInterval(function () {
_this._evaluateAndDisplayTours();
}, CHECK_INTERVAL_MS);
_this._evaluateAndDisplayTours();
}
};
this.nextStep = function () {
var _a;
if (!_this._activeTour) {
return;
}
var currentStep = _this._activeTour.steps[_this._currentStepIndex];
_this._captureEvent(posthog_product_tours_types_1.ProductTourEventName.STEP_COMPLETED, (_a = {},
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_ID] = _this._activeTour.id,
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_STEP_ID] = currentStep.id,
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_STEP_ORDER] = _this._currentStepIndex,
_a));
if (_this._currentStepIndex < _this._activeTour.steps.length - 1) {
_this._setStepIndex(_this._currentStepIndex + 1);
_this._renderCurrentStep();
}
else {
_this._completeTour();
}
};
this.previousStep = function () {
if (!_this._activeTour || _this._currentStepIndex === 0) {
return;
}
_this._setStepIndex(_this._currentStepIndex - 1);
_this._renderCurrentStep();
};
this.dismissTour = function (reason) {
var _a, _b;
if (reason === void 0) { reason = 'user_clicked_skip'; }
if (!_this._activeTour) {
return;
}
var currentStep = _this._activeTour.steps[_this._currentStepIndex];
_this._captureEvent(posthog_product_tours_types_1.ProductTourEventName.DISMISSED, (_a = {},
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_ID] = _this._activeTour.id,
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_STEP_ID] = currentStep.id,
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_STEP_ORDER] = _this._currentStepIndex,
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_DISMISS_REASON] = reason,
_a));
if (!_this._isPreviewMode) {
storage_1.localStore._set("".concat(constants_1.TOUR_DISMISSED_KEY_PREFIX).concat(_this._activeTour.id), true);
_this._instance.capture('$set', {
$set: (_b = {}, _b["$product_tour_dismissed/".concat(_this._activeTour.id)] = true, _b),
});
}
window.dispatchEvent(new CustomEvent('PHProductTourDismissed', { detail: { tourId: _this._activeTour.id, reason: reason } }));
_this._cleanup();
};
this._handleBannerActionClick = function () {
var _a;
var _b;
if (!_this._activeTour) {
return;
}
var step = _this._getCurrentStep();
if (!step) {
return;
}
var action = (_b = step.bannerConfig) === null || _b === void 0 ? void 0 : _b.action;
_this._captureEvent(posthog_product_tours_types_1.ProductTourEventName.BANNER_ACTION_CLICKED, (_a = {},
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_ID] = _this._activeTour.id,
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_NAME] = _this._activeTour.name,
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_ITERATION] = _this._activeTour.current_iteration || 1,
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_STEP_ID] = step.id,
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_STEP_ORDER] = _this._currentStepIndex,
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_BUTTON_ACTION] = action === null || action === void 0 ? void 0 : action.type,
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_BUTTON_LINK] = action === null || action === void 0 ? void 0 : action.link,
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_BUTTON_TOUR_ID] = action === null || action === void 0 ? void 0 : action.tourId,
_a));
if ((action === null || action === void 0 ? void 0 : action.type) === 'trigger_tour' && action.tourId) {
_this._cleanup();
_this.showTourById(action.tourId);
}
};
this._handleButtonClick = function (button) {
var _a, _b, _c;
if (_this._activeTour) {
var currentStep = _this._activeTour.steps[_this._currentStepIndex];
if (currentStep) {
_this._captureEvent(posthog_product_tours_types_1.ProductTourEventName.BUTTON_CLICKED, __assign(__assign((_a = {}, _a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_ID] = _this._activeTour.id, _a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_NAME] = _this._activeTour.name, _a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_ITERATION] = _this._activeTour.current_iteration || 1, _a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_STEP_ID] = currentStep.id, _a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_STEP_ORDER] = _this._currentStepIndex, _a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_BUTTON_TEXT] = button.text, _a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_BUTTON_ACTION] = button.action, _a), (button.link && (_b = {}, _b[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_BUTTON_LINK] = button.link, _b))), (button.tourId && (_c = {}, _c[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_BUTTON_TOUR_ID] = button.tourId, _c))));
}
}
switch (button.action) {
case 'dismiss':
_this.dismissTour('user_clicked_skip');
break;
case 'next_step':
_this.nextStep();
break;
case 'previous_step':
_this.previousStep();
break;
case 'link':
_this._completeTour();
break;
case 'trigger_tour':
if (button.tourId) {
_this._completeTour();
_this.showTourById(button.tourId);
}
break;
}
};
this._instance = instance;
this._eventReceiver = new product_tour_event_receiver_1.ProductTourEventReceiver(instance);
}
ProductTourManager.prototype._preloadTourImages = function (tours) {
var e_1, _a;
var urls = tours
.filter(function (tour) { return !tour.disable_image_preload; })
.flatMap(function (tour) { return tour.steps.flatMap(product_tours_utils_1.getStepImageUrls); });
try {
for (var urls_1 = __values(urls), urls_1_1 = urls_1.next(); !urls_1_1.done; urls_1_1 = urls_1.next()) {
var url = urls_1_1.value;
if (!this._preloadedImageUrls.has(url)) {
this._preloadedImageUrls.add(url);
new Image().src = url;
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (urls_1_1 && !urls_1_1.done && (_a = urls_1.return)) _a.call(urls_1);
}
finally { if (e_1) throw e_1.error; }
}
};
ProductTourManager.prototype._getCurrentStep = function () {
var _a;
if (!this._activeTour) {
return null;
}
var rawStep = this._activeTour.steps[this._currentStepIndex];
if (!rawStep) {
return null;
}
var language = (_a = this._instance.config.override_display_language) !== null && _a !== void 0 ? _a : (0, event_utils_1.getBrowserLanguage)();
return (0, product_tours_utils_1.resolveStepTranslation)(rawStep, language !== null && language !== void 0 ? language : null);
};
ProductTourManager.prototype._setStepIndex = function (index) {
this._currentStepIndex = index;
this._saveSessionState();
};
ProductTourManager.prototype._saveSessionState = function () {
if (!this._activeTour || this._isPreviewMode) {
return;
}
storage_1.sessionStore._set(constants_1.ACTIVE_TOUR_SESSION_KEY, {
tourId: this._activeTour.id,
stepIndex: this._currentStepIndex,
});
};
ProductTourManager.prototype._clearSessionState = function () {
storage_1.sessionStore._remove(constants_1.ACTIVE_TOUR_SESSION_KEY);
};
ProductTourManager.prototype._getSessionState = function () {
var stored = storage_1.sessionStore._get(constants_1.ACTIVE_TOUR_SESSION_KEY);
if (!stored) {
return null;
}
try {
return JSON.parse(stored);
}
catch (_a) {
return null;
}
};
ProductTourManager.prototype.start = function () {
var _this = this;
if (this._checkInterval) {
return;
}
// Check for saved session state before starting the evaluation loop
var savedState = this._getSessionState();
if (savedState) {
this._resumeSavedTour(savedState.tourId, savedState.stepIndex, function () {
_this._startEvaluationLoop();
});
}
else {
this._startEvaluationLoop();
}
};
ProductTourManager.prototype._startEvaluationLoop = function () {
var _this = this;
this._checkInterval = setInterval(function () {
_this._evaluateAndDisplayTours();
}, CHECK_INTERVAL_MS);
this._evaluateAndDisplayTours(true);
(0, general_utils_1.addEventListener)(document, 'visibilitychange', this._handleVisibilityChange);
};
ProductTourManager.prototype._resumeSavedTour = function (tourId, stepIndex, onComplete) {
var _this = this;
var _a;
(_a = this._instance.productTours) === null || _a === void 0 ? void 0 : _a.getProductTours(function (tours) {
var tour = tours.find(function (t) { return t.id === tourId; });
if (!tour) {
if (tours.length > 0) {
_this._clearSessionState();
}
}
else {
_this._activeTour = tour;
_this._currentStepIndex = stepIndex;
_this._isResuming = true;
_this._renderCurrentStep();
}
onComplete();
});
};
ProductTourManager.prototype.stop = function () {
if (this._checkInterval) {
clearInterval(this._checkInterval);
this._checkInterval = null;
}
document.removeEventListener('visibilitychange', this._handleVisibilityChange);
this._removeAllTriggerListeners();
this._cancelAllPendingTours();
this._cleanup();
};
ProductTourManager.prototype._evaluateAndDisplayTours = function (forceReload) {
var _this = this;
var _a;
if (document === null || document === void 0 ? void 0 : document.getElementById(constants_2.TOOLBAR_ID)) {
return;
}
// Use getProductTours (not getActiveProductTours) because selector-triggered tours
// should work even if completed/dismissed
(_a = this._instance.productTours) === null || _a === void 0 ? void 0 : _a.getProductTours(function (tours) {
var e_2, _a;
var _b;
if (tours.length === 0) {
_this._removeAllTriggerListeners();
return;
}
_this._preloadTourImages(tours);
var activeTriggerTourIds = new Set();
var unregisteredEventTours = tours.filter(function (tour) {
return !_this._registeredEventTourIds.has(tour.id) &&
((0, product_tour_utils_1.doesTourActivateByEvent)(tour) || (0, product_tour_utils_1.doesTourActivateByAction)(tour));
});
if (unregisteredEventTours.length > 0) {
_this._eventReceiver.register(unregisteredEventTours);
unregisteredEventTours.forEach(function (tour) { return _this._registeredEventTourIds.add(tour.id); });
}
var eventActivatedTourIds = _this._activeTour ? [] : _this._eventReceiver.getTours();
try {
/**
* tours can be shown three ways, really:
*
* 1) selector clicks
* 2a) auto-show immediately
* 2b) auto-show after event/action
*
* (1) and (2[a|b]) are fully independent of each other
*/
for (var tours_1 = __values(tours), tours_1_1 = tours_1.next(); !tours_1_1.done; tours_1_1 = tours_1.next()) {
var tour = tours_1_1.value;
// 1) SELECTOR CLICK TRIGGER - just attach an event listener and keep going
var triggerSelector = (_b = tour.conditions) === null || _b === void 0 ? void 0 : _b.selector;
if (triggerSelector) {
activeTriggerTourIds.add(tour.id);
_this._manageTriggerSelectorListener(tour, triggerSelector);
}
// skip auto-launch checks if a tour is already active
if (_this._activeTour) {
continue;
}
// 2) AUTO-LAUNCH
var hasEventOrActionTriggers = (0, product_tour_utils_1.doesTourActivateByAction)(tour) || (0, product_tour_utils_1.doesTourActivateByEvent)(tour);
if (tour.auto_launch && _this._isTourEligible(tour)) {
// tour should auto-launch, and the current session is eligible
if (!hasEventOrActionTriggers) {
// 2a) AUTO-SHOW WITH NO EVENT/ACTION
_this._showOrQueueTour(tour, 'auto');
continue;
}
// 2b) AUTO-SHOW, BUT WAIT FOR EVENT/ACTION
if (eventActivatedTourIds.includes(tour.id)) {
_this._showOrQueueTour(tour, 'event');
}
}
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (tours_1_1 && !tours_1_1.done && (_a = tours_1.return)) _a.call(tours_1);
}
finally { if (e_2) throw e_2.error; }
}
_this._triggerSelectorListeners.forEach(function (_a) {
var tour = _a.tour;
if (!activeTriggerTourIds.has(tour.id)) {
_this._removeTriggerSelectorListener(tour.id);
}
});
}, forceReload);
};
ProductTourManager.prototype._showOrQueueTour = function (tour, reason) {
var _a;
var delaySeconds = ((_a = tour.conditions) === null || _a === void 0 ? void 0 : _a.autoShowDelaySeconds) || 0;
if (delaySeconds > 0) {
if (!this.isTourPending(tour.id)) {
this.queueTourWithDelay(tour.id, delaySeconds, reason);
}
}
else {
this.showTour(tour, { reason: reason });
}
};
ProductTourManager.prototype._isTourEligible = function (tour) {
var _a, _b, _c, _d, _e, _f, _g;
if (!checkTourConditions(tour, this._instance)) {
logger.info("Tour ".concat(tour.id, " failed conditions check"));
return false;
}
var displayFrequency = (_a = tour.display_frequency) !== null && _a !== void 0 ? _a : 'until_interacted';
var shownKey = "".concat(constants_1.TOUR_SHOWN_KEY_PREFIX).concat(tour.id);
var completedKey = "".concat(constants_1.TOUR_COMPLETED_KEY_PREFIX).concat(tour.id);
var dismissedKey = "".concat(constants_1.TOUR_DISMISSED_KEY_PREFIX).concat(tour.id);
switch (displayFrequency) {
case 'show_once':
if (storage_1.localStore._get(shownKey)) {
logger.info("Tour ".concat(tour.id, " already shown (show_once frequency)"));
return false;
}
break;
case 'until_interacted':
if (storage_1.localStore._get(completedKey) || storage_1.localStore._get(dismissedKey)) {
logger.info("Tour ".concat(tour.id, " already completed or dismissed"));
return false;
}
break;
case 'always':
default:
break;
}
if (!(0, product_tours_utils_1.hasTourWaitPeriodPassed)((_b = tour.conditions) === null || _b === void 0 ? void 0 : _b.seenTourWaitPeriod)) {
logger.info("Cannot show tour ".concat(tour.id, ": user has seen a ").concat((_d = (_c = tour.conditions) === null || _c === void 0 ? void 0 : _c.seenTourWaitPeriod) === null || _d === void 0 ? void 0 : _d.types, " tour within the last ").concat((_f = (_e = tour.conditions) === null || _e === void 0 ? void 0 : _e.seenTourWaitPeriod) === null || _f === void 0 ? void 0 : _f.days, " days."));
return false;
}
if (!this._isProductToursFeatureFlagEnabled({ flagKey: tour.internal_targeting_flag_key })) {
logger.info("Tour ".concat(tour.id, " failed feature flag check: ").concat(tour.internal_targeting_flag_key));
return false;
}
var linkedFlagVariant = (_g = tour.conditions) === null || _g === void 0 ? void 0 : _g.linkedFlagVariant;
if (!this._isProductToursFeatureFlagEnabled({ flagKey: tour.linked_flag_key, flagVariant: linkedFlagVariant })) {
logger.info("Tour ".concat(tour.id, " failed feature flag check: ").concat(tour.linked_flag_key, ", variant: ").concat(linkedFlagVariant));
return false;
}
return true;
};
ProductTourManager.prototype.showTour = function (tour, options) {
var _a, _b;
var _c;
var renderReason = (_c = options === null || options === void 0 ? void 0 : options.reason) !== null && _c !== void 0 ? _c : 'auto';
this.cancelPendingTour(tour.id);
this._activeTour = tour;
this._setStepIndex(0);
var rendered = this._renderCurrentStep();
if (rendered) {
this._captureEvent(posthog_product_tours_types_1.ProductTourEventName.SHOWN, (_a = {},
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_ID] = tour.id,
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_NAME] = tour.name,
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_ITERATION] = tour.current_iteration || 1,
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_RENDER_REASON] = renderReason,
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_TYPE] = tour.tour_type,
_a));
if (!this._isPreviewMode) {
storage_1.localStore._set("".concat(constants_1.TOUR_SHOWN_KEY_PREFIX).concat(tour.id), true);
storage_1.localStore._set("".concat(constants_1.LAST_SEEN_TOUR_DATE_KEY_PREFIX).concat(tour.tour_type), new Date().toISOString());
this._instance.capture('$set', {
$set: (_b = {}, _b["$product_tour_shown/".concat(tour.id)] = true, _b),
});
}
}
else {
this._cleanup();
}
return rendered;
};
ProductTourManager.prototype.showTourById = function (tourId, reason) {
var _this = this;
var _a;
logger.info("showTourById(".concat(tourId, ")"));
(_a = this._instance.productTours) === null || _a === void 0 ? void 0 : _a.getProductTours(function (tours) {
var tour = tours.find(function (t) { return t.id === tourId; });
if (tour) {
_this.showTour(tour, { reason: reason !== null && reason !== void 0 ? reason : 'api' });
}
else {
logger.warn('could not find tour', tourId);
}
});
};
ProductTourManager.prototype.previewTour = function (tour) {
logger.info("Previewing tour ".concat(tour.id));
this._cleanup();
this._isPreviewMode = true;
this._activeTour = tour;
this._currentStepIndex = 0;
this._renderCurrentStep();
};
ProductTourManager.prototype._completeTour = function () {
var _a, _b;
if (!this._activeTour) {
return;
}
this._captureEvent(posthog_product_tours_types_1.ProductTourEventName.COMPLETED, (_a = {},
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_ID] = this._activeTour.id,
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_STEPS_COUNT] = this._activeTour.steps.length,
_a));
if (!this._isPreviewMode) {
storage_1.localStore._set("".concat(constants_1.TOUR_COMPLETED_KEY_PREFIX).concat(this._activeTour.id), true);
this._instance.capture('$set', {
$set: (_b = {},
_b["$product_tour_completed/".concat(this._activeTour.id)] = true,
_b),
});
}
window.dispatchEvent(new CustomEvent('PHProductTourCompleted', { detail: { tourId: this._activeTour.id } }));
this._cleanup();
};
ProductTourManager.prototype._renderCurrentStep = function (retryCount) {
var _a, _b, _c;
var _this = this;
var _d;
if (retryCount === void 0) { retryCount = 0; }
if (!this._activeTour) {
return false;
}
var step = this._getCurrentStep();
if (!step) {
logger.warn("Step ".concat(this._currentStepIndex, " not found in tour ").concat(this._activeTour.id));
this._cleanup();
return false;
}
// Banner step - render full-width banner
if (step.type === 'banner') {
this._captureStepShown();
this._isResuming = false;
this._renderBanner();
return true;
}
// Survey step - render native survey step component
if (step.type === 'survey') {
if (step.survey) {
this._renderSurveyStep();
return true;
}
logger.warn('Unable to render survey step - survey data not found');
return false;
}
// Screen-positioned step (no element targeting) - render without a target element
if (!(0, product_tours_utils_1.hasElementTarget)(step)) {
this._captureStepShown();
this._isResuming = false;
this._renderTooltipWithPreact(null);
return true;
}
var result = (0, product_tours_utils_1.findStepElement)(step);
var inferenceProps = (_a = {},
_a[posthog_product_tours_types_1.ProductTourEventProperties.USE_MANUAL_SELECTOR] = (_d = step.useManualSelector) !== null && _d !== void 0 ? _d : false,
_a[posthog_product_tours_types_1.ProductTourEventProperties.INFERENCE_DATA_PRESENT] = !!step.inferenceData,
_a);
var previousStep = this._currentStepIndex > 0 ? this._activeTour.steps[this._currentStepIndex - 1] : null;
var shouldWaitForElement = (previousStep === null || previousStep === void 0 ? void 0 : previousStep.progressionTrigger) === 'click' || this._isResuming;
// 2s total timeout
var maxRetries = 20;
var retryTimeout = 100;
if (result.error === 'not_found' || result.error === 'not_visible') {
// if previous step was click-to-progress, or we are resuming a tour,
// give some time for the next element
if (shouldWaitForElement && retryCount < maxRetries) {
setTimeout(function () {
_this._renderCurrentStep(retryCount + 1);
}, retryTimeout);
return false;
}
var waitDurationMs = retryCount * retryTimeout;
this._captureStepSelectorFailed(result, __assign((_b = {}, _b[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_WAITED_FOR_ELEMENT] = shouldWaitForElement, _b[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_WAIT_DURATION_MS] = waitDurationMs, _b), inferenceProps));
if (this._currentStepIndex === 0 && !this._isResuming) {
logger.warn("Tour \"".concat(this._activeTour.name, "\" failed to show: element for first step not found (").concat(result.error, ")"));
return false;
}
logger.warn("Tour \"".concat(this._activeTour.name, "\" dismissed: element for step ").concat(this._currentStepIndex, " became unavailable (").concat(result.error, ")") +
(shouldWaitForElement ? " after waiting ".concat(waitDurationMs, "ms") : ''));
this.dismissTour('element_unavailable');
return false;
}
if (result.error === 'multiple_matches') {
this._captureStepSelectorFailed(result, inferenceProps);
// Continue with first match for multiple_matches case
}
if (!result.element) {
return false;
}
var element = result.element;
var metadata = (0, product_tours_utils_1.getElementMetadata)(element);
this._captureStepShown(__assign((_c = {}, _c[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_STEP_SELECTOR] = step.selector, _c[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_STEP_SELECTOR_FOUND] = true, _c[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_STEP_ELEMENT_TAG] = metadata.tag, _c[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_STEP_ELEMENT_ID] = metadata.id, _c[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_STEP_ELEMENT_CLASSES] = metadata.classes, _c[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_STEP_ELEMENT_TEXT] = metadata.text, _c), inferenceProps));
this._isResuming = false;
this._renderTooltipWithPreact(element);
return true;
};
ProductTourManager.prototype._renderTooltipWithPreact = function (element, onSurveySubmit, onDismissOverride) {
if (!this._activeTour) {
return;
}
var step = this._getCurrentStep();
if (!step) {
return;
}
var shadow = retrieveTourShadow(this._activeTour, this._instance).shadow;
(0, preact_1.render)((0, jsx_runtime_1.jsx)(ProductTourTooltip_1.ProductTourTooltip, { tour: this._activeTour, step: step, stepIndex: this._currentStepIndex, totalSteps: this._activeTour.steps.length, targetElement: element, onNext: this.nextStep, onPrevious: this.previousStep, onDismiss: onDismissOverride || this.dismissTour, onSurveySubmit: onSurveySubmit, onButtonClick: this._handleButtonClick }), shadow);
};
ProductTourManager.prototype._renderBanner = function () {
var _a;
var _this = this;
var _b;
if (!this._activeTour) {
return;
}
var step = this._getCurrentStep();
if (!step) {
return;
}
var result = retrieveBannerShadow(this._activeTour, this._instance, step.bannerConfig);
if (!result) {
this._captureEvent(posthog_product_tours_types_1.ProductTourEventName.BANNER_CONTAINER_SELECTOR_FAILED, (_a = {},
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_ID] = this._activeTour.id,
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_BANNER_SELECTOR] = (_b = step === null || step === void 0 ? void 0 : step.bannerConfig) === null || _b === void 0 ? void 0 : _b.selector,
_a));
this.dismissTour('container_unavailable');
return;
}
var shadow = result.shadow;
(0, preact_1.render)((0, jsx_runtime_1.jsx)(ProductTourBanner_1.ProductTourBanner, { step: step, onDismiss: function () { return _this.dismissTour('user_clicked_skip'); }, onActionClick: this._handleBannerActionClick, displayFrequency: this._activeTour.display_frequency }), shadow);
};
ProductTourManager.prototype._renderSurveyStep = function () {
var _a, _b;
var _this = this;
var _c, _d, _e;
if (!this._activeTour) {
return;
}
var tourId = this._activeTour.id;
var step = this._getCurrentStep();
if (!step) {
return;
}
var surveyId = step.linkedSurveyId;
var questionId = step.linkedSurveyQuestionId;
var questionText = ((_c = step.survey) === null || _c === void 0 ? void 0 : _c.questionText) || '';
this._captureStepShown((_a = {},
_a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_LINKED_SURVEY_ID] = surveyId,
_a));
this._captureEvent(posthog_surveys_types_1.SurveyEventName.SHOWN, (_b = {},
_b[posthog_surveys_types_1.SurveyEventProperties.SURVEY_ID] = surveyId,
_b[posthog_surveys_types_1.SurveyEventProperties.PRODUCT_TOUR_ID] = tourId,
_b.sessionRecordingUrl = (_e = (_d = this._instance).get_session_replay_url) === null || _e === void 0 ? void 0 : _e.call(_d),
_b));
var handleSubmit = function (response) {
var _a, _b;
var _c, _d;
var responseKey = questionId ? "$survey_response_".concat(questionId) : '$survey_response';
_this._captureEvent(posthog_surveys_types_1.SurveyEventName.SENT, __assign((_a = {}, _a[posthog_surveys_types_1.SurveyEventProperties.SURVEY_ID] = surveyId, _a[posthog_surveys_types_1.SurveyEventProperties.PRODUCT_TOUR_ID] = tourId, _a[posthog_surveys_types_1.SurveyEventProperties.SURVEY_QUESTIONS] = [
{
id: questionId,
question: questionText,
response: response,
},
], _a[posthog_surveys_types_1.SurveyEventProperties.SURVEY_COMPLETED] = true, _a.sessionRecordingUrl = (_d = (_c = _this._instance).get_session_replay_url) === null || _d === void 0 ? void 0 : _d.call(_c), _a), (!(0, core_1.isNull)(response) && (_b = {}, _b[responseKey] = response, _b))));
logger.info("Survey ".concat(surveyId, " completed"), !(0, core_1.isNull)(response) ? "with response: ".concat(response) : '(skipped)');
_this.nextStep();
};
var handleDismiss = function (reason) {
var _a;
var _b, _c;
_this._captureEvent(posthog_surveys_types_1.SurveyEventName.DISMISSED, (_a = {},
_a[posthog_surveys_types_1.SurveyEventProperties.SURVEY_ID] = surveyId,
_a[posthog_surveys_types_1.SurveyEventProperties.PRODUCT_TOUR_ID] = tourId,
_a[posthog_surveys_types_1.SurveyEventProperties.SURVEY_QUESTIONS] = [
{
id: questionId,
question: questionText,
response: null,
},
],
_a[posthog_surveys_types_1.SurveyEventProperties.SURVEY_PARTIALLY_COMPLETED] = false,
_a.sessionRecordingUrl = (_c = (_b = _this._instance).get_session_replay_url) === null || _c === void 0 ? void 0 : _c.call(_b),
_a));
logger.info("Survey ".concat(surveyId, " dismissed"));
_this.dismissTour(reason);
};
this._renderTooltipWithPreact(null, handleSubmit, handleDismiss);
logger.info("Rendered survey step for tour step ".concat(this._currentStepIndex));
};
ProductTourManager.prototype._isProductToursFeatureFlagEnabled = function (_a) {
var _b, _c;
var flagKey = _a.flagKey, flagVariant = _a.flagVariant;
if (!flagKey) {
return true;
}
var isFeatureEnabled = !!((_b = this._instance.featureFlags) === null || _b === void 0 ? void 0 : _b.isFeatureEnabled(flagKey, {
send_event: !flagKey.startsWith(PRODUCT_TOUR_TARGETING_FLAG_PREFIX),
}));
var flagVariantCheck = true;
if (flagVariant) {
var flagVariantValue = (_c = this._instance.featureFlags) === null || _c === void 0 ? void 0 : _c.getFeatureFlag(flagKey, { send_event: false });
flagVariantCheck = flagVariantValue === flagVariant || flagVariant === 'any';
}
return isFeatureEnabled && flagVariantCheck;
};
ProductTourManager.prototype._cleanup = function () {
if (this._activeTour) {
removeTourFromDom(this._activeTour.id);
}
this._activeTour = null;
this._currentStepIndex = 0;
this._isPreviewMode = false;
this._isResuming = false;
this._clearSessionState();
};
ProductTourManager.prototype._manageTriggerSelectorListener = function (tour, selector) {
var _this = this;
var currentElement = document.querySelector(selector);
var existingListenerData = this._triggerSelectorListeners.get(tour.id);
if (!currentElement) {
if (existingListenerData) {
this._removeTriggerSelectorListener(tour.id);
}
return;
}
if (existingListenerData) {
if (currentElement !== existingListenerData.element) {
logger.info("Trigger element changed for tour ".concat(tour.id, ". Re-attaching listener."));
this._removeTriggerSelectorListener(tour.id);
}
else {
return;
}
}
if (!currentElement.hasAttribute(TRIGGER_LISTENER_ATTRIBUTE)) {
var listener = function (event) {
var _a;
if (_this._activeTour) {
logger.info("Tour ".concat(tour.id, " trigger clicked but another tour is active"));
return;
}
var currentTour;
(_a = _this._instance.productTours) === null || _a === void 0 ? void 0 : _a.getProductTours(function (tours) {
currentTour = tours.find(function (t) { return t.id === tour.id; });
});
if (!currentTour) {
logger.warn("Tour ".concat(tour.id, " no longer exists. Removing stale listener."));
_this._removeTriggerSelectorListener(tour.id);
return;
}
if (!isTourInDateRange(currentTour)) {
logger.warn("Tour ".concat(tour.id, " trigger clicked, but tour is not launched - not showing tour."));
return;
}
logger.info("Tour ".concat(tour.id, " triggered by click on ").concat(selector));
if (_this.showTour(currentTour, { reason: 'trigger' })) {
event.preventDefault();
}
else {
logger.info("Tour ".concat(tour.id, " failed to show; not intercepting click."));
}
};
(0, general_utils_1.addEventListener)(currentElement, 'click', listener);
currentElement.setAttribute(TRIGGER_LISTENER_ATTRIBUTE, tour.id);
this._triggerSelectorListeners.set(tour.id, { element: currentElement, listener: listener, tour: tour });
logger.info("Attached trigger listener for tour ".concat(tour.id, " on ").concat(selector));
}
};
ProductTourManager.prototype._removeTriggerSelectorListener = function (tourId) {
var existing = this._triggerSelectorListeners.get(tourId);
if (existing) {
existing.element.removeEventListener('click', existing.listener);
existing.element.removeAttribute(TRIGGER_LISTENER_ATTRIBUTE);
this._triggerSelectorListeners.delete(tourId);
logger.info("Removed trigger listener for tour ".concat(tourId));
}
};
ProductTourManager.prototype._removeAllTriggerListeners = function () {
var _this = this;
this._triggerSelectorListeners.forEach(function (_, tourId) {
_this._removeTriggerSelectorListener(tourId);
});
};
ProductTourManager.prototype._captureEvent = function (eventName, properties) {
if (this._isPreviewMode) {
return;
}
this._instance.capture(eventName, properties);
};
ProductTourManager.prototype._captureStepShown = function (extraProps) {
var _a;
if (!this._activeTour) {
return;
}
var step = this._activeTour.steps[this._currentStepIndex];
this._captureEvent(posthog_product_tours_types_1.ProductTourEventName.STEP_SHOWN, __assign((_a = {}, _a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_ID] = this._activeTour.id, _a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_STEP_ID] = step.id, _a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_STEP_ORDER] = this._currentStepIndex, _a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_STEP_TYPE] = step.type, _a), extraProps));
};
ProductTourManager.prototype._captureStepSelectorFailed = function (result, extraProps) {
var _a;
if (!this._activeTour) {
return;
}
var step = this._activeTour.steps[this._currentStepIndex];
this._captureEvent(posthog_product_tours_types_1.ProductTourEventName.STEP_SELECTOR_FAILED, __assign((_a = {}, _a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_ID] = this._activeTour.id, _a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_STEP_ID] = step.id, _a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_STEP_ORDER] = this._currentStepIndex, _a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_STEP_SELECTOR] = step.selector, _a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_ERROR] = result.error, _a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_MATCHES_COUNT] = result.matchCount, _a[posthog_product_tours_types_1.ProductTourEventProperties.TOUR_FAILURE_PHASE] = 'runtime', _a), extraProps));
};
// Public API methods delegated from PostHogProductTours
ProductTourManager.prototype.getActiveProductTours = function (callback) {
var _this = this;
var _a;
(_a = this._instance.productTours) === null || _a === void 0 ? void 0 : _a.getProductTours(function (tours, context) {
if (!(context === null || context === void 0 ? void 0 : context.isLoaded)) {
callback([], context);
return;
}
var activeTours = tours.filter(function (tour) { return _this._isTourEligible(tour); });
callback(activeTours, context);
});
};
ProductTourManager.prototype.resetTour = function (tourId) {
storage_1.localStore._remove("".concat(constants_1.TOUR_SHOWN_KEY_PREFIX).concat(tourId));
storage_1.localStore._remove("".concat(constants_1.TOUR_COMPLETED_KEY_PREFIX).concat(tourId));
storage_1.localStore._remove("".concat(constants_1.TOUR_DISMISSED_KEY_PREFIX).concat(tourId));
};
ProductTourManager.prototype.resetAllTours = function () {
var storage = window === null || window === void 0 ? void 0 : window.localStorage;
if (!storage) {
return;
}
var keysToRemove = [];
for (var i = 0; i < storage.length; i++) {
var key = storage.key(i);
if ((key === null || key === void 0 ? void 0 : key.startsWith(constants_1.TOUR_SHOWN_KEY_PREFIX)) ||
(key === null || key === void 0 ? void 0 : key.startsWith(constants_1.TOUR_COMPLETED_KEY_PREFIX)) ||
(key === null || key === void 0 ? void 0 : key.startsWith(constants_1.TOUR_DISMISSED_KEY_PREFIX)) ||
(key === null || key === void 0 ? void 0 : key.startsWith(constants_1.LAST_SEEN_TOUR_DATE_KEY_PREFIX))) {
keysToRemove.push(key);
}
}
keysToRemove.forEach(function (key) { return storage_1.localStore._remove(key); });
};
ProductTourManager.prototype.cancelPendingTour = function (tourId) {
var timeout = this._pendingTourTimeouts.get(tourId);
if (timeout) {
clearTimeout(timeout);
this._pendingTourTimeouts.delete(tourId);
logger.info("Cancelled pending tour: ".concat(tourId));
}
};
ProductTourManager.prototype._cancelAllPendingTours = function () {
this._pendingTourTimeouts.forEach(function (timeout) { return clearTimeout(timeout); });
this._pendingTourTimeouts.clear();
};
ProductTourManager.prototype.isTourPending = function (tourId) {
return this._pendingTourTimeouts.has(tourId);
};
ProductTourManager.prototype.queueTourWithDelay = function (tourId, delaySeconds, reason) {
var _this = this;
logger.info("Queueing tour ".concat(tourId, " with ").concat(delaySeconds, "s delay"));
var timeoutId = setTimeout(function () {
_this._pendingTourTimeouts.delete(tourId);
logger.info("Delay elapsed for tour ".concat(tourId, ", showing now"));
_this.showTourById(tourId, reason);
}, delaySeconds * 1000);
this._pendingTourTimeouts.set(tourId, timeoutId);
};
return ProductTourManager;
}());
exports.ProductTourManager = ProductTourManager;
//# sourceMappingURL=product-tours.js.map