posthog-js
Version:
Posthog-js allows you to automatically capture usage and send events to PostHog.
213 lines • 9.99 kB
JavaScript
;
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 __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractPrefillParamsFromUrl = extractPrefillParamsFromUrl;
exports.convertPrefillToResponses = convertPrefillToResponses;
exports.calculatePrefillStartIndex = calculatePrefillStartIndex;
var posthog_surveys_types_1 = require("../posthog-surveys-types");
var surveys_extension_utils_1 = require("../extensions/surveys/surveys-extension-utils");
var logger_1 = require("@posthog/browser-common/utils/logger");
var core_1 = require("@posthog/core");
var survey_branching_1 = require("./survey-branching");
/**
* Extract prefill parameters from URL search string
* Format: ?q0=1&q1=8&q2=0&q2=2&auto_submit=true
* NOTE: Manual parsing for IE11/op_mini compatibility (no URLSearchParams)
*/
function extractPrefillParamsFromUrl(searchString) {
var e_1, _a;
var params = {};
var autoSubmit = false;
// Remove leading ? if present
var cleanSearch = searchString.replace(/^\?/, '');
if (!cleanSearch) {
return { params: params, autoSubmit: autoSubmit };
}
// Split by & to get key-value pairs
var pairs = cleanSearch.split('&');
try {
for (var pairs_1 = __values(pairs), pairs_1_1 = pairs_1.next(); !pairs_1_1.done; pairs_1_1 = pairs_1.next()) {
var pair = pairs_1_1.value;
var _b = __read(pair.split('='), 2), key = _b[0], value = _b[1];
if (!key || (0, core_1.isUndefined)(value)) {
continue;
}
var decodedKey = decodeURIComponent(key);
var decodedValue = decodeURIComponent(value);
// Check for auto_submit parameter
if (decodedKey === 'auto_submit' && decodedValue === 'true') {
autoSubmit = true;
continue;
}
// Check for question parameters (q0, q1, etc.)
var match = decodedKey.match(/^q(\d+)$/);
if (match) {
var questionIndex = parseInt(match[1], 10);
if (!params[questionIndex]) {
params[questionIndex] = [];
}
params[questionIndex].push(decodedValue);
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (pairs_1_1 && !pairs_1_1.done && (_a = pairs_1.return)) _a.call(pairs_1);
}
finally { if (e_1) throw e_1.error; }
}
return { params: params, autoSubmit: autoSubmit };
}
/**
* Convert URL prefill values to SDK response format
*/
function convertPrefillToResponses(survey, prefillParams) {
var responses = {};
survey.questions.forEach(function (question, index) {
if (!prefillParams[index] || !question.id) {
return;
}
var values = prefillParams[index];
var responseKey = (0, surveys_extension_utils_1.getSurveyResponseKey)(question.id);
try {
switch (question.type) {
case posthog_surveys_types_1.SurveyQuestionType.SingleChoice: {
if (!question.choices || question.choices.length === 0) {
logger_1.logger.warn("[Survey Prefill] Question ".concat(index, " has no choices"));
return;
}
var choiceIndex = parseInt(values[0], 10);
if (isNaN(choiceIndex) || choiceIndex < 0 || choiceIndex >= question.choices.length) {
logger_1.logger.warn("[Survey Prefill] Invalid choice index for q".concat(index, ": ").concat(values[0]));
return;
}
responses[responseKey] = question.choices[choiceIndex];
break;
}
case posthog_surveys_types_1.SurveyQuestionType.MultipleChoice: {
if (!question.choices || question.choices.length === 0) {
logger_1.logger.warn("[Survey Prefill] Question ".concat(index, " has no choices"));
return;
}
var choiceIndices = values
.map(function (v) { return parseInt(v, 10); })
.filter(function (i) { return !isNaN(i) && i >= 0 && i < question.choices.length; });
if (choiceIndices.length === 0) {
logger_1.logger.warn("[Survey Prefill] No valid choices for q".concat(index));
return;
}
// Remove duplicates and map to choice values
var uniqueChoices = __spreadArray([], __read(new Set(choiceIndices.map(function (i) { return question.choices[i]; }))), false);
if (uniqueChoices.length < choiceIndices.length) {
logger_1.logger.warn("[Survey Prefill] Removed duplicate choices for q".concat(index));
}
responses[responseKey] = uniqueChoices;
break;
}
case posthog_surveys_types_1.SurveyQuestionType.Rating: {
var rating = parseInt(values[0], 10);
var scale = question.scale || 10;
if (isNaN(rating) || rating < 0 || rating > scale) {
logger_1.logger.warn("[Survey Prefill] Invalid rating for q".concat(index, ": ").concat(values[0], " (scale: 0-").concat(scale, ")"));
return;
}
responses[responseKey] = rating;
break;
}
default:
logger_1.logger.info("[Survey Prefill] Question type ".concat(question.type, " does not support prefill"));
}
}
catch (error) {
logger_1.logger.error("[Survey Prefill] Error converting q".concat(index, ":"), error);
}
});
return responses;
}
/**
* Calculate which question index to start at based on prefilled questions.
* Advances past consecutive prefilled questions (starting from index 0)
* that have skipSubmitButton enabled, respecting any branching logic configured
* on those questions.
*
* @param survey - The full survey object (needed for branching logic)
* @param prefilledIndices - Array of question indices that have been prefilled
* @param responses - Map of response keys to response values
* @returns Object with startQuestionIndex and map of questions which have been skipped
*/
function calculatePrefillStartIndex(survey, prefilledIndices, responses) {
var currentIndex = 0;
var skippedResponses = {};
// Auto-advanced questions, persisted as visitedIndices so a manual submit keeps their answers.
var skippedIndices = [];
var MAX_ITERATIONS = survey.questions.length + 1;
var iterations = 0;
while (currentIndex < survey.questions.length && iterations < MAX_ITERATIONS) {
iterations++;
// Stop if current question is not prefilled
if (!prefilledIndices.includes(currentIndex)) {
break;
}
var question = survey.questions[currentIndex];
// Only advance if the prefilled question has skipSubmitButton
if (!question || !('skipSubmitButton' in question) || !question.skipSubmitButton) {
// Show question if skipSubmitButton is false, even if prefilled
break;
}
// Record the skipped response
if (question.id) {
var responseKey = (0, surveys_extension_utils_1.getSurveyResponseKey)(question.id);
if (!(0, core_1.isUndefined)(responses[responseKey])) {
skippedResponses[responseKey] = responses[responseKey];
}
}
skippedIndices.push(currentIndex);
// Use branching logic to determine the next question
var response = question.id ? responses[(0, surveys_extension_utils_1.getSurveyResponseKey)(question.id)] : null;
var nextStep = (0, survey_branching_1.getNextSurveyStep)(survey, currentIndex, response);
if (nextStep === posthog_surveys_types_1.SurveyQuestionBranchingType.End) {
// Survey is complete - return questions.length to indicate completion
return { startQuestionIndex: survey.questions.length, skippedResponses: skippedResponses, skippedIndices: skippedIndices };
}
// Move to the next question (respecting branching)
currentIndex = nextStep;
}
return { startQuestionIndex: currentIndex, skippedResponses: skippedResponses, skippedIndices: skippedIndices };
}
//# sourceMappingURL=survey-url-prefill.js.map