UNPKG

posthog-js

Version:

Posthog-js allows you to automatically capture usage and send events to PostHog.

721 lines • 35.6 kB
"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."); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.clearInProgressSurveyState = exports.isSurveyInProgress = exports.getInProgressSurveyState = exports.setInProgressSurveyState = exports.renderChildrenAsTextOrHtml = exports.useSurveyContext = exports.SurveyContext = exports.hasWaitPeriodPassed = exports.getSurveySeen = exports.canActivateRepeatedly = exports.getDisplayOrderQuestions = exports.getQuestionOrder = exports.getDisplayOrderChoices = exports.shuffle = exports.sendSurveyAbandonedEvent = exports.dismissedSurveyEvent = exports.sendSurveyEvent = exports.retrieveSurveyShadow = exports.addSurveyCSSVariablesToElement = exports.defaultSurveyAppearance = exports.getSurveyResponseKey = void 0; exports.getFontFamily = getFontFamily; exports.hex2rgb = hex2rgb; exports.hexToRgba = hexToRgba; exports.getContrastingTextColor = getContrastingTextColor; exports.getSurveyStylesheet = getSurveyStylesheet; exports.doesSurveyUrlMatch = doesSurveyUrlMatch; exports.doesSurveyDeviceTypesMatch = doesSurveyDeviceTypesMatch; exports.doesSurveyMatchSelector = doesSurveyMatchSelector; exports.getSurveyContainerClass = getSurveyContainerClass; exports.getPopoverPosition = getPopoverPosition; var preact_1 = require("preact"); var posthog_surveys_types_1 = require("../../posthog-surveys-types"); var globals_1 = require("@posthog/browser-common/utils/globals"); var survey_utils_1 = require("../../utils/survey-utils"); var core_1 = require("@posthog/core"); var surveys_1 = require("@posthog/core/surveys"); Object.defineProperty(exports, "getSurveyResponseKey", { enumerable: true, get: function () { return surveys_1.getSurveyResponseKey; } }); 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 storage_1 = require("../../storage"); var constants_1 = require("../../constants"); var stylesheet_loader_1 = require("@posthog/browser-common/utils/stylesheet-loader"); // We cast the types here which is dangerous but protected by the top level generateSurveys call var window = globals_1.window; var document = globals_1.document; var survey_css_1 = __importDefault(require("./survey.css")); var hooks_1 = require("preact/hooks"); var matcher_utils_1 = require("@posthog/browser-common/utils/matcher-utils"); function getFontFamily(fontFamily) { if (fontFamily === 'inherit') { return 'inherit'; } var defaultFontStack = 'BlinkMacSystemFont, "Inter", "Segoe UI", "Roboto", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"'; return fontFamily ? "".concat(fontFamily, ", ").concat(defaultFontStack) : "-apple-system, ".concat(defaultFontStack); } var BLACK_TEXT_COLOR = '#020617'; // Maps out to text-slate-950 from tailwind colors. Intended for text use outside interactive elements like buttons // Keep in sync with defaultSurveyAppearance on the main app exports.defaultSurveyAppearance = { fontFamily: 'inherit', backgroundColor: '#eeeded', submitButtonColor: 'black', submitButtonTextColor: 'white', ratingButtonColor: 'white', ratingButtonActiveColor: 'black', borderColor: '#c9c6c6', placeholder: 'Start typing...', whiteLabel: false, displayThankYouMessage: true, thankYouMessageHeader: 'Thank you for your feedback!', position: posthog_surveys_types_1.SurveyPosition.Right, widgetType: posthog_surveys_types_1.SurveyWidgetType.Tab, widgetLabel: 'Feedback', widgetColor: 'black', zIndex: String(constants_1.Z_INDEX_SURVEYS), disabledButtonOpacity: '0.6', maxWidth: '300px', textSubtleColor: '#939393', boxPadding: '20px 24px', boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)', borderRadius: '10px', shuffleQuestions: false, surveyPopupDelaySeconds: undefined, // Not customizable atm outlineColor: 'rgba(59, 130, 246, 0.8)', inputBackground: 'white', inputTextColor: BLACK_TEXT_COLOR, scrollbarThumbColor: 'var(--ph-survey-border-color)', scrollbarTrackColor: 'var(--ph-survey-background-color)', }; var BOTTOM_BORDER_SURVEY_POSITIONS = [ posthog_surveys_types_1.SurveyPosition.Center, posthog_surveys_types_1.SurveyPosition.Left, posthog_surveys_types_1.SurveyPosition.Right, ]; var addSurveyCSSVariablesToElement = function (element, type, appearance) { var effectiveAppearance = __assign(__assign({}, exports.defaultSurveyAppearance), appearance); var hostStyle = element.style; var surveyHasBottomBorder = !BOTTOM_BORDER_SURVEY_POSITIONS.includes(effectiveAppearance.position) || (type === posthog_surveys_types_1.SurveyType.Widget && (appearance === null || appearance === void 0 ? void 0 : appearance.widgetType) === posthog_surveys_types_1.SurveyWidgetType.Tab); hostStyle.setProperty('--ph-survey-font-family', getFontFamily(effectiveAppearance.fontFamily)); hostStyle.setProperty('--ph-survey-box-padding', effectiveAppearance.boxPadding); hostStyle.setProperty('--ph-survey-max-width', effectiveAppearance.maxWidth); hostStyle.setProperty('--ph-survey-z-index', effectiveAppearance.zIndex); hostStyle.setProperty('--ph-survey-border-color', effectiveAppearance.borderColor); // Non-bottom surveys or tab surveys have the border bottom if (surveyHasBottomBorder) { hostStyle.setProperty('--ph-survey-border-radius', effectiveAppearance.borderRadius); hostStyle.setProperty('--ph-survey-border-bottom', '1.5px solid var(--ph-survey-border-color)'); } else { hostStyle.setProperty('--ph-survey-border-bottom', 'none'); hostStyle.setProperty('--ph-survey-border-radius', "".concat(effectiveAppearance.borderRadius, " ").concat(effectiveAppearance.borderRadius, " 0 0")); } hostStyle.setProperty('--ph-survey-background-color', effectiveAppearance.backgroundColor); hostStyle.setProperty('--ph-survey-box-shadow', effectiveAppearance.boxShadow); hostStyle.setProperty('--ph-survey-disabled-button-opacity', effectiveAppearance.disabledButtonOpacity); hostStyle.setProperty('--ph-survey-submit-button-color', effectiveAppearance.submitButtonColor); hostStyle.setProperty('--ph-survey-submit-button-text-color', (appearance === null || appearance === void 0 ? void 0 : appearance.submitButtonTextColor) || getContrastingTextColor(effectiveAppearance.submitButtonColor)); hostStyle.setProperty('--ph-survey-rating-bg-color', effectiveAppearance.ratingButtonColor); hostStyle.setProperty('--ph-survey-rating-active-bg-color', effectiveAppearance.ratingButtonActiveColor); // Active rating text is always auto-calculated for contrast with active background hostStyle.setProperty('--ph-survey-rating-active-text-color', getContrastingTextColor(effectiveAppearance.ratingButtonActiveColor)); // Primary text color: use override if provided, otherwise auto-calculate from background hostStyle.setProperty('--ph-survey-text-primary-color', (appearance === null || appearance === void 0 ? void 0 : appearance.textColor) || getContrastingTextColor(effectiveAppearance.backgroundColor)); hostStyle.setProperty('--ph-survey-text-subtle-color', effectiveAppearance.textSubtleColor); hostStyle.setProperty('--ph-widget-color', effectiveAppearance.widgetColor); hostStyle.setProperty('--ph-widget-text-color', getContrastingTextColor(effectiveAppearance.widgetColor)); hostStyle.setProperty('--ph-widget-z-index', effectiveAppearance.zIndex); // Use user-provided inputBackground (or deprecated inputBackgroundColor for backwards compat) // Fallback to internal default, with slight adjustment for white backgrounds var userInputBg = (appearance === null || appearance === void 0 ? void 0 : appearance.inputBackground) || (appearance === null || appearance === void 0 ? void 0 : appearance.inputBackgroundColor); var inputBgColor = userInputBg || effectiveAppearance.inputBackground; if (!userInputBg && effectiveAppearance.backgroundColor === 'white') { inputBgColor = '#f8f8f8'; } hostStyle.setProperty('--ph-survey-input-background', inputBgColor); // Input text color applies to both text inputs and inactive rating buttons var inputTextColor = (appearance === null || appearance === void 0 ? void 0 : appearance.inputTextColor) || getContrastingTextColor(inputBgColor); hostStyle.setProperty('--ph-survey-input-text-color', inputTextColor); hostStyle.setProperty('--ph-survey-rating-text-color', inputTextColor); hostStyle.setProperty('--ph-survey-scrollbar-thumb-color', effectiveAppearance.scrollbarThumbColor); hostStyle.setProperty('--ph-survey-scrollbar-track-color', effectiveAppearance.scrollbarTrackColor); hostStyle.setProperty('--ph-survey-outline-color', effectiveAppearance.outlineColor); }; exports.addSurveyCSSVariablesToElement = addSurveyCSSVariablesToElement; function nameToHex(name) { return { aliceblue: '#f0f8ff', antiquewhite: '#faebd7', aqua: '#00ffff', aquamarine: '#7fffd4', azure: '#f0ffff', beige: '#f5f5dc', bisque: '#ffe4c4', black: '#000000', blanchedalmond: '#ffebcd', blue: '#0000ff', blueviolet: '#8a2be2', brown: '#a52a2a', burlywood: '#deb887', cadetblue: '#5f9ea0', chartreuse: '#7fff00', chocolate: '#d2691e', coral: '#ff7f50', cornflowerblue: '#6495ed', cornsilk: '#fff8dc', crimson: '#dc143c', cyan: '#00ffff', darkblue: '#00008b', darkcyan: '#008b8b', darkgoldenrod: '#b8860b', darkgray: '#a9a9a9', darkgreen: '#006400', darkkhaki: '#bdb76b', darkmagenta: '#8b008b', darkolivegreen: '#556b2f', darkorange: '#ff8c00', darkorchid: '#9932cc', darkred: '#8b0000', darksalmon: '#e9967a', darkseagreen: '#8fbc8f', darkslateblue: '#483d8b', darkslategray: '#2f4f4f', darkturquoise: '#00ced1', darkviolet: '#9400d3', deeppink: '#ff1493', deepskyblue: '#00bfff', dimgray: '#696969', dodgerblue: '#1e90ff', firebrick: '#b22222', floralwhite: '#fffaf0', forestgreen: '#228b22', fuchsia: '#ff00ff', gainsboro: '#dcdcdc', ghostwhite: '#f8f8ff', gold: '#ffd700', goldenrod: '#daa520', gray: '#808080', green: '#008000', greenyellow: '#adff2f', honeydew: '#f0fff0', hotpink: '#ff69b4', 'indianred ': '#cd5c5c', indigo: '#4b0082', ivory: '#fffff0', khaki: '#f0e68c', lavender: '#e6e6fa', lavenderblush: '#fff0f5', lawngreen: '#7cfc00', lemonchiffon: '#fffacd', lightblue: '#add8e6', lightcoral: '#f08080', lightcyan: '#e0ffff', lightgoldenrodyellow: '#fafad2', lightgrey: '#d3d3d3', lightgreen: '#90ee90', lightpink: '#ffb6c1', lightsalmon: '#ffa07a', lightseagreen: '#20b2aa', lightskyblue: '#87cefa', lightslategray: '#778899', lightsteelblue: '#b0c4de', lightyellow: '#ffffe0', lime: '#00ff00', limegreen: '#32cd32', linen: '#faf0e6', magenta: '#ff00ff', maroon: '#800000', mediumaquamarine: '#66cdaa', mediumblue: '#0000cd', mediumorchid: '#ba55d3', mediumpurple: '#9370d8', mediumseagreen: '#3cb371', mediumslateblue: '#7b68ee', mediumspringgreen: '#00fa9a', mediumturquoise: '#48d1cc', mediumvioletred: '#c71585', midnightblue: '#191970', mintcream: '#f5fffa', mistyrose: '#ffe4e1', moccasin: '#ffe4b5', navajowhite: '#ffdead', navy: '#000080', oldlace: '#fdf5e6', olive: '#808000', olivedrab: '#6b8e23', orange: '#ffa500', orangered: '#ff4500', orchid: '#da70d6', palegoldenrod: '#eee8aa', palegreen: '#98fb98', paleturquoise: '#afeeee', palevioletred: '#d87093', papayawhip: '#ffefd5', peachpuff: '#ffdab9', peru: '#cd853f', pink: '#ffc0cb', plum: '#dda0dd', powderblue: '#b0e0e6', purple: '#800080', red: '#ff0000', rosybrown: '#bc8f8f', royalblue: '#4169e1', saddlebrown: '#8b4513', salmon: '#fa8072', sandybrown: '#f4a460', seagreen: '#2e8b57', seashell: '#fff5ee', sienna: '#a0522d', silver: '#c0c0c0', skyblue: '#87ceeb', slateblue: '#6a5acd', slategray: '#708090', snow: '#fffafa', springgreen: '#00ff7f', steelblue: '#4682b4', tan: '#d2b48c', teal: '#008080', thistle: '#d8bfd8', tomato: '#ff6347', turquoise: '#40e0d0', violet: '#ee82ee', wheat: '#f5deb3', white: '#ffffff', whitesmoke: '#f5f5f5', yellow: '#ffff00', yellowgreen: '#9acd32', }[name.toLowerCase()]; } function hex2rgb(c) { if (c.startsWith('#')) { var hexColor = c.replace(/^#/, ''); // Handle 3-character shorthand (e.g., #111 -> #111111, #abc -> #aabbcc) if (/^[0-9A-Fa-f]{3}$/.test(hexColor)) { hexColor = hexColor[0] + hexColor[0] + hexColor[1] + hexColor[1] + hexColor[2] + hexColor[2]; } if (!/^[0-9A-Fa-f]{6}$/.test(hexColor)) { return 'rgb(255, 255, 255)'; } var r = parseInt(hexColor.slice(0, 2), 16); var g = parseInt(hexColor.slice(2, 4), 16); var b = parseInt(hexColor.slice(4, 6), 16); return "rgb(".concat(r, ",").concat(g, ",").concat(b, ")"); } return 'rgb(255, 255, 255)'; } function hexToRgba(hex, opacity) { var rgb = hex2rgb(hex); var match = rgb.match(/^rgb\((\d+),(\d+),(\d+)\)$/); if (!match) { return hex; } return "rgba(".concat(match[1], ", ").concat(match[2], ", ").concat(match[3], ", ").concat(opacity, ")"); } function getContrastingTextColor(color) { if (color === void 0) { color = exports.defaultSurveyAppearance.backgroundColor; } var rgb; if (color[0] === '#') { rgb = hex2rgb(color); } if (color.startsWith('rgb')) { rgb = color; } // otherwise it's a color name var nameColorToHex = nameToHex(color); if (nameColorToHex) { rgb = hex2rgb(nameColorToHex); } if (!rgb) { return BLACK_TEXT_COLOR; } var colorMatch = rgb.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/); if (colorMatch) { var r = parseInt(colorMatch[1]); var g = parseInt(colorMatch[2]); var b = parseInt(colorMatch[3]); var hsp = Math.sqrt(0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b)); return hsp > 127.5 ? BLACK_TEXT_COLOR : 'white'; } return BLACK_TEXT_COLOR; } function getSurveyStylesheet(posthog) { var stylesheet = (0, stylesheet_loader_1.prepareStylesheet)(document, typeof survey_css_1.default === 'string' ? survey_css_1.default : '', posthog); stylesheet === null || stylesheet === void 0 ? void 0 : stylesheet.setAttribute('data-ph-survey-style', 'true'); return stylesheet; } var retrieveSurveyShadow = function (survey, posthog, element) { var widgetClassName = getSurveyContainerClass(survey); var existingDiv = document.querySelector(".".concat(widgetClassName)); if (existingDiv && existingDiv.shadowRoot) { return { shadow: existingDiv.shadowRoot, isNewlyCreated: false, }; } // If it doesn't exist, create it var div = document.createElement('div'); (0, exports.addSurveyCSSVariablesToElement)(div, survey.type, survey.appearance); div.className = widgetClassName; var shadow = div.attachShadow({ mode: 'open' }); var stylesheet = getSurveyStylesheet(posthog); if (stylesheet) { var existingStylesheet = shadow.querySelector('style'); if (existingStylesheet) { shadow.removeChild(existingStylesheet); } shadow.appendChild(stylesheet); } ; (element ? element : document.body).appendChild(div); return { shadow: shadow, isNewlyCreated: true, }; }; exports.retrieveSurveyShadow = retrieveSurveyShadow; var sendSurveyEvent = function (_a) { var _b, _c, _d; var _e; var responses = _a.responses, survey = _a.survey, surveySubmissionId = _a.surveySubmissionId, posthog = _a.posthog, isSurveyCompleted = _a.isSurveyCompleted, properties = _a.properties, surveyLanguage = _a.surveyLanguage, questionSnapshots = _a.questionSnapshots; if (!posthog) { survey_utils_1.SURVEY_LOGGER.error('[survey sent] event not captured, PostHog instance not found.'); return; } (0, survey_utils_1.setSurveySeenOnLocalStorage)(survey); posthog.capture(posthog_surveys_types_1.SurveyEventName.SENT, __assign(__assign(__assign(__assign(__assign((_b = {}, _b[posthog_surveys_types_1.SurveyEventProperties.SURVEY_NAME] = survey.name, _b[posthog_surveys_types_1.SurveyEventProperties.SURVEY_ID] = survey.id, _b[posthog_surveys_types_1.SurveyEventProperties.SURVEY_ITERATION] = survey.current_iteration, _b[posthog_surveys_types_1.SurveyEventProperties.SURVEY_ITERATION_START_DATE] = survey.current_iteration_start_date, _b[posthog_surveys_types_1.SurveyEventProperties.SURVEY_SUBMISSION_ID] = surveySubmissionId, _b[posthog_surveys_types_1.SurveyEventProperties.SURVEY_COMPLETED] = isSurveyCompleted, _b), (surveyLanguage && (_c = {}, _c[posthog_surveys_types_1.SurveyEventProperties.SURVEY_LANGUAGE] = surveyLanguage, _c))), { sessionRecordingUrl: (_e = posthog.get_session_replay_url) === null || _e === void 0 ? void 0 : _e.call(posthog) }), (0, surveys_1.buildSurveyResponseProperties)(responses, survey, questionSnapshots)), properties), { $set: (_d = {}, _d[(0, survey_utils_1.getSurveyInteractionProperty)(survey, 'responded')] = true, _d) })); if (isSurveyCompleted) { // Only dispatch PHSurveySent if the survey is completed, as that removes the survey from focus window.dispatchEvent(new CustomEvent('PHSurveySent', { detail: { surveyId: survey.id } })); (0, exports.clearInProgressSurveyState)(survey); // Recompute the internal targeting flag promptly. The response we just recorded makes this // person ineligible server-side, but the cached flag still says "eligible", so reloading now // stops a quick revisit from re-showing the survey and recording a duplicate response. posthog.reloadFeatureFlags(); } }; exports.sendSurveyEvent = sendSurveyEvent; var _buildSurveyEventProperties = function (survey, inProgressSurvey, posthog) { var _a, _b, _c; var _d; return (__assign(__assign(__assign((_a = {}, _a[posthog_surveys_types_1.SurveyEventProperties.SURVEY_NAME] = survey.name, _a[posthog_surveys_types_1.SurveyEventProperties.SURVEY_ID] = survey.id, _a[posthog_surveys_types_1.SurveyEventProperties.SURVEY_ITERATION] = survey.current_iteration, _a[posthog_surveys_types_1.SurveyEventProperties.SURVEY_ITERATION_START_DATE] = survey.current_iteration_start_date, _a[posthog_surveys_types_1.SurveyEventProperties.SURVEY_PARTIALLY_COMPLETED] = (0, surveys_1.surveyHasResponses)(inProgressSurvey === null || inProgressSurvey === void 0 ? void 0 : inProgressSurvey.responses), _a), ((inProgressSurvey === null || inProgressSurvey === void 0 ? void 0 : inProgressSurvey.surveyLanguage) && (_b = {}, _b[posthog_surveys_types_1.SurveyEventProperties.SURVEY_LANGUAGE] = inProgressSurvey.surveyLanguage, _b))), (_c = { sessionRecordingUrl: (_d = posthog.get_session_replay_url) === null || _d === void 0 ? void 0 : _d.call(posthog) }, _c[posthog_surveys_types_1.SurveyEventProperties.SURVEY_SUBMISSION_ID] = inProgressSurvey === null || inProgressSurvey === void 0 ? void 0 : inProgressSurvey.surveySubmissionId, _c)), (0, surveys_1.buildSurveyResponseProperties)(inProgressSurvey === null || inProgressSurvey === void 0 ? void 0 : inProgressSurvey.responses, survey, inProgressSurvey === null || inProgressSurvey === void 0 ? void 0 : inProgressSurvey.questionSnapshots))); }; var dismissedSurveyEvent = function (survey, posthog, readOnly, surveyLanguage) { var _a, _b; if (!posthog) { survey_utils_1.SURVEY_LOGGER.error('[survey dismissed] event not captured, PostHog instance not found.'); return; } if (readOnly) { return; } var inProgressSurvey = (0, exports.getInProgressSurveyState)(survey); // Prefer the language snapshotted when the user last answered (answer-time language), // which is legitimately `null` when no translation matched at answer time — that must not // fall through to the current display language. Only fall back to the current display // language when no in-progress state exists at all (i.e. the survey was dismissed without // answering any question), so check for the record's presence, not its value. var effectiveLanguage = inProgressSurvey ? inProgressSurvey.surveyLanguage : surveyLanguage; posthog.capture(posthog_surveys_types_1.SurveyEventName.DISMISSED, __assign(__assign(__assign({}, _buildSurveyEventProperties(survey, inProgressSurvey, posthog)), (effectiveLanguage && (_a = {}, _a[posthog_surveys_types_1.SurveyEventProperties.SURVEY_LANGUAGE] = effectiveLanguage, _a))), { $set: (_b = {}, _b[(0, survey_utils_1.getSurveyInteractionProperty)(survey, 'dismissed')] = true, _b) })); (0, exports.clearInProgressSurveyState)(survey); (0, survey_utils_1.setSurveySeenOnLocalStorage)(survey); window.dispatchEvent(new CustomEvent('PHSurveyClosed', { detail: { surveyId: survey.id } })); }; exports.dismissedSurveyEvent = dismissedSurveyEvent; var sendSurveyAbandonedEvent = function (survey, posthog) { if (!posthog) { survey_utils_1.SURVEY_LOGGER.error('[survey abandoned] event not captured, PostHog instance not found.'); return; } var abandonedKey = (0, survey_utils_1.getSurveyAbandonedKey)(survey); try { if (localStorage.getItem(abandonedKey) === 'true') { return; } } catch (_a) { // localStorage not available return; } var inProgressSurvey = (0, exports.getInProgressSurveyState)(survey); if (!inProgressSurvey) { return; } try { localStorage.setItem(abandonedKey, 'true'); } catch (_b) { // localStorage not available } posthog.capture(posthog_surveys_types_1.SurveyEventName.ABANDONED, _buildSurveyEventProperties(survey, inProgressSurvey, posthog), { transport: 'sendBeacon', }); }; exports.sendSurveyAbandonedEvent = sendSurveyAbandonedEvent; // Use the Fisher-yates algorithm to shuffle this array // https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle var shuffle = function (array) { return array .map(function (a) { return ({ sort: Math.floor(Math.random() * 10), value: a }); }) .sort(function (a, b) { return a.sort - b.sort; }) .map(function (a) { return a.value; }); }; exports.shuffle = shuffle; var reverseIfUnshuffled = function (unshuffled, shuffled) { if (unshuffled.length === shuffled.length && unshuffled.every(function (val, index) { return val === shuffled[index]; })) { return shuffled.reverse(); } return shuffled; }; var getDisplayOrderChoices = function (question) { if (!question.shuffleOptions) { return question.choices; } var displayOrderChoices = question.choices; var openEndedChoice = ''; if (question.hasOpenChoice) { // if the question has an open-ended choice, its always the last element in the choices array. openEndedChoice = displayOrderChoices.pop(); } var shuffledOptions = reverseIfUnshuffled(displayOrderChoices, (0, exports.shuffle)(displayOrderChoices)); if (question.hasOpenChoice) { question.choices.push(openEndedChoice); shuffledOptions.push(openEndedChoice); } return shuffledOptions; }; exports.getDisplayOrderChoices = getDisplayOrderChoices; var hasBranching = function (survey) { return survey.questions.some(function (question) { var _a; return !!((_a = question.branching) === null || _a === void 0 ? void 0 : _a.type); }); }; /** * Restores the question order a persisted record's indices point into, so a resumed survey reads * them against the same order that produced them. Returns null when the record predates the stored * order, or when it no longer describes this survey's questions. */ var restorePersistedOrder = function (survey, questionOrder) { var e_1, _a; if (!questionOrder || questionOrder.length !== survey.questions.length) { return null; } var questionsById = new Map(survey.questions.map(function (question) { return [question.id, question]; })); var restored = []; try { for (var questionOrder_1 = __values(questionOrder), questionOrder_1_1 = questionOrder_1.next(); !questionOrder_1_1.done; questionOrder_1_1 = questionOrder_1.next()) { var id = questionOrder_1_1.value; var question = questionsById.get(id); if (!question) { return null; } restored.push(question); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (questionOrder_1_1 && !questionOrder_1_1.done && (_a = questionOrder_1.return)) _a.call(questionOrder_1); } finally { if (e_1) throw e_1.error; } } return restored; }; /** Question ids in the given order, or undefined when any question lacks an id to record. */ var getQuestionOrder = function (questions) { var ids = questions.map(function (question) { return question.id; }); return ids.every(function (id) { return !!id; }) ? ids : undefined; }; exports.getQuestionOrder = getQuestionOrder; /** * Branching rules hold the position of their target question in survey.questions, so a survey that * uses branching is always read in the configured order. The API rejects surveys that set both * shuffleQuestions and branching, so that covers rows predating the validation. * * A survey the respondent has already started keeps the order its persisted indices point into. * Without a recorded order those indices came from a build that did not store one, and the order * that produced them is unknowable, so the configured order is the only safe reading. */ var getDisplayOrderQuestions = function (survey, inProgressState) { var _a; if (hasBranching(survey)) { return survey.questions; } if (inProgressState) { return (_a = restorePersistedOrder(survey, inProgressState.questionOrder)) !== null && _a !== void 0 ? _a : survey.questions; } if (!survey.appearance || !survey.appearance.shuffleQuestions || survey.enable_partial_responses) { return survey.questions; } return reverseIfUnshuffled(survey.questions, (0, exports.shuffle)(survey.questions)); }; exports.getDisplayOrderQuestions = getDisplayOrderQuestions; var canActivateRepeatedly = function (survey) { return (0, surveys_1.canSurveyActivateRepeatedly)(survey) || (0, exports.isSurveyInProgress)(survey); }; exports.canActivateRepeatedly = canActivateRepeatedly; /** * getSurveySeen checks local storage for the surveySeen Key a * and overrides this value if the survey can be repeatedly activated by its events. * @param survey */ var getSurveySeen = function (survey) { var surveySeen = storage_1.localStore._get((0, survey_utils_1.getSurveySeenKey)(survey)); if (surveySeen) { // if a survey has already been seen, // we will override it with the event repeated activation value. return !(0, exports.canActivateRepeatedly)(survey); } return false; }; exports.getSurveySeen = getSurveySeen; var LAST_SEEN_SURVEY_DATE_KEY = 'lastSeenSurveyDate'; var hasWaitPeriodPassed = function (waitPeriodInDays) { var lastSeenSurveyDate = storage_1.localStore._get(LAST_SEEN_SURVEY_DATE_KEY); return (0, matcher_utils_1.hasPeriodPassed)(waitPeriodInDays, lastSeenSurveyDate); }; exports.hasWaitPeriodPassed = hasWaitPeriodPassed; exports.SurveyContext = (0, preact_1.createContext)({ isPreviewMode: false, previewPageIndex: 0, onPopupSurveyDismissed: function () { }, isPopup: true, onPreviewSubmit: function () { }, onPreviewBack: function () { }, surveySubmissionId: '', properties: undefined, surveyLanguage: null, }); var useSurveyContext = function () { return (0, hooks_1.useContext)(exports.SurveyContext); }; exports.useSurveyContext = useSurveyContext; var renderChildrenAsTextOrHtml = function (_a) { var component = _a.component, children = _a.children, renderAsHtml = _a.renderAsHtml, style = _a.style; return renderAsHtml ? (0, preact_1.cloneElement)(component, { dangerouslySetInnerHTML: { __html: children }, style: style, }) : (0, preact_1.cloneElement)(component, { children: children, style: style, }); }; exports.renderChildrenAsTextOrHtml = renderChildrenAsTextOrHtml; function defaultMatchType(matchType) { return matchType !== null && matchType !== void 0 ? matchType : 'icontains'; } // use urlMatchType to validate url condition, fallback to contains for backwards compatibility function doesSurveyUrlMatch(survey, posthog) { var _a, _b; if (!((_a = survey.conditions) === null || _a === void 0 ? void 0 : _a.url)) { return true; } // honors the `get_current_url` config hook so apps that rewrite their URL can target surveys correctly var href = (0, url_targeting_utils_1.getTargetingUrl)(posthog); if (!href) { // if we dont know the url, assume it is not a match return false; } var targets = [survey.conditions.url]; var matchType = defaultMatchType((_b = survey.conditions) === null || _b === void 0 ? void 0 : _b.urlMatchType); return property_utils_1.propertyComparisons[matchType](targets, [href]); } function doesSurveyDeviceTypesMatch(survey) { var _a, _b; return (0, matcher_utils_1.doesDeviceTypeMatch)((_a = survey.conditions) === null || _a === void 0 ? void 0 : _a.deviceTypes, (_b = survey.conditions) === null || _b === void 0 ? void 0 : _b.deviceTypesMatchType); } function doesSurveyMatchSelector(survey) { var _a; if (!((_a = survey.conditions) === null || _a === void 0 ? void 0 : _a.selector)) { return true; } return !!(document === null || document === void 0 ? void 0 : document.querySelector(survey.conditions.selector)); } var getInProgressSurveyStateKey = function (survey) { return (0, survey_utils_1.getSurveyStorageKey)(survey_utils_1.SURVEY_IN_PROGRESS_PREFIX, survey); }; var setInProgressSurveyState = function (survey, state) { try { localStorage.setItem(getInProgressSurveyStateKey(survey), JSON.stringify(state)); } catch (e) { survey_utils_1.SURVEY_LOGGER.error('Error setting in-progress survey state in localStorage', e); } }; exports.setInProgressSurveyState = setInProgressSurveyState; var getInProgressSurveyState = function (survey) { try { var stateString = localStorage.getItem(getInProgressSurveyStateKey(survey)); if (stateString) { return JSON.parse(stateString); } } catch (e) { survey_utils_1.SURVEY_LOGGER.error('Error getting in-progress survey state from localStorage', e); } return null; }; exports.getInProgressSurveyState = getInProgressSurveyState; var isSurveyInProgress = function (survey) { var state = (0, exports.getInProgressSurveyState)(survey); return !(0, core_1.isNullish)(state === null || state === void 0 ? void 0 : state.surveySubmissionId); }; exports.isSurveyInProgress = isSurveyInProgress; var clearInProgressSurveyState = function (survey) { try { localStorage.removeItem(getInProgressSurveyStateKey(survey)); } catch (e) { survey_utils_1.SURVEY_LOGGER.error('Error clearing in-progress survey state from localStorage', e); } }; exports.clearInProgressSurveyState = clearInProgressSurveyState; function getSurveyContainerClass(survey, asSelector) { if (asSelector === void 0) { asSelector = false; } var className = "PostHogSurvey-".concat(survey.id); return asSelector ? ".".concat(className) : className; } /** * Get CSS position styles for a popover/modal based on SurveyPosition * Used by both surveys and product tours for consistent positioning */ function getPopoverPosition(type, position, surveyWidgetType) { if (position === void 0) { position = posthog_surveys_types_1.SurveyPosition.Right; } if (type === posthog_surveys_types_1.SurveyType.ExternalSurvey) { return {}; } switch (position) { case posthog_surveys_types_1.SurveyPosition.TopLeft: return { top: '0', left: '0', transform: 'translate(30px, 30px)' }; case posthog_surveys_types_1.SurveyPosition.TopRight: return { top: '0', right: '0', transform: 'translate(-30px, 30px)' }; case posthog_surveys_types_1.SurveyPosition.TopCenter: return { top: '0', left: '50%', transform: 'translate(-50%, 30px)' }; case posthog_surveys_types_1.SurveyPosition.MiddleLeft: return { top: '50%', left: '0', transform: 'translate(30px, -50%)' }; case posthog_surveys_types_1.SurveyPosition.MiddleRight: return { top: '50%', right: '0', transform: 'translate(-30px, -50%)' }; case posthog_surveys_types_1.SurveyPosition.MiddleCenter: return { top: '50%', left: '50%', transform: 'translate(-50%, -50%)' }; case posthog_surveys_types_1.SurveyPosition.Left: return { left: '30px' }; case posthog_surveys_types_1.SurveyPosition.Center: return { left: '50%', transform: 'translateX(-50%)' }; default: case posthog_surveys_types_1.SurveyPosition.Right: return { right: type === posthog_surveys_types_1.SurveyType.Widget && surveyWidgetType === posthog_surveys_types_1.SurveyWidgetType.Tab ? '60px' : '30px' }; } } //# sourceMappingURL=surveys-extension-utils.js.map