UNPKG

@contensis/forms

Version:
1,700 lines (1,678 loc) 110 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { ContensisForm: () => ContensisForm }); module.exports = __toCommonJS(src_exports); // src/components/Form.tsx var import_react37 = __toESM(require("react")); // src/state/captcha.ts var CAPTCHA_LOAD_CALLBACK = "on_captcha_load"; function getCaptchaUrl(siteKey) { return `https://www.google.com/recaptcha/api.js?onload=${CAPTCHA_LOAD_CALLBACK}&render=${siteKey}`; } function load(captcha) { if (captcha?.enabled && captcha?.siteKey) { ensureLoadCallback(); const captchaUrl = getCaptchaUrl(captcha.siteKey); const head = document.getElementsByTagName("head")[0]; const scripts = [...head.getElementsByTagName("script")]; const scriptSrcs = scripts.map((s) => s.src); const hasCaptcha = scriptSrcs.includes(captchaUrl); if (!hasCaptcha) { const captcha2 = document.createElement("script"); captcha2.src = captchaUrl; head.appendChild(captcha2); } } } async function submit(formId, captcha) { if (captcha?.enabled && captcha?.siteKey) { load(captcha); await window[CAPTCHA_LOAD_CALLBACK].loaded; await new Promise((resolve) => grecaptcha.ready(() => resolve(true))); return grecaptcha.execute(captcha.siteKey, { action: `${formId}_submit` }); } else { return Promise.resolve(""); } } function ensureLoadCallback() { if (!window[CAPTCHA_LOAD_CALLBACK]) { let loadedResolve; const loaded = new Promise((resolve) => { loadedResolve = resolve; }); const callback = function() { loadedResolve(null); }; Object.assign(callback, { loaded }); window[CAPTCHA_LOAD_CALLBACK] = callback; } } var Captcha = { load, submit }; // src/state/version.ts function isPublishedVersion(versionStatus) { return !versionStatus || versionStatus === "published"; } var Version = { isPublishedVersion }; // src/state/api.ts function getAllCookies() { return (document.cookie || "").split("; ").reduce( (cookies, cookieString) => { const parts = cookieString.split("="); const name = decode(parts[0]); if (name) { cookies[name] = decode(parts.slice(1).join("=")); } return cookies; }, {} ); } function decode(s) { try { return decodeURIComponent(s); } catch (e) { return s; } } function setCookie(key, value, expiry) { document.cookie = `${encodeURIComponent(key)}=${encodeURIComponent(value)}; expires=${expiry.toUTCString()}; path=/;`; } var CmsBearerTokenCookie = "ContensisSecurityBearerToken"; var CmsRefreshTokenCookie = "ContensisSecurityRefreshToken"; var RefreshTokenCookie = "RefreshToken"; var RefreshTokenExpiryTime = 15 * 24 * 3600 * 1e3; var SCOPE = "Security_Administrator ContentType_Read ContentType_Write ContentType_Delete Entry_Read Entry_Write Entry_Delete Project_Read Project_Write Project_Delete Workflow_Administrator"; var GRANT_TYPE = "contensis_classic_refresh_token"; function isLoggedIn() { const cookies = getAllCookies(); return !!cookies[CmsBearerTokenCookie]; } async function getBearerToken(options) { const cookies = getAllCookies(); if (cookies[CmsBearerTokenCookie]) { return cookies[CmsBearerTokenCookie]; } const refreshToken = cookies[CmsRefreshTokenCookie] || cookies[RefreshTokenCookie]; if (!refreshToken) { return null; } try { const currentDate = /* @__PURE__ */ new Date(); const response = await fetch(`${options.apiUrl}/authenticate/connect/token`, { headers: { "content-type": "application/x-www-form-urlencoded;charset=UTF-8" }, body: `scope=${encodeURIComponent(SCOPE)}&grant_type=${encodeURIComponent(GRANT_TYPE)}&refresh_token=${encodeURIComponent(refreshToken)}`, method: "POST", mode: "cors", credentials: "omit" }); if (!response.ok) { return null; } const data = await response.json(); const bearerToken = data.access_token; const bearerTokenExpiryDate = new Date(currentDate.getTime() + data.expires_in * 1e3); const newRefreshToken = data.refresh_token; const refreshTokenExpiryDate = new Date(currentDate.getTime() + RefreshTokenExpiryTime); setCookie(CmsBearerTokenCookie, bearerToken, bearerTokenExpiryDate); if (newRefreshToken) { setCookie(RefreshTokenCookie, newRefreshToken, refreshTokenExpiryDate); } return bearerToken; } catch (e) { return null; } } async function getDefaultHeaders(options) { const bearerToken = await getBearerToken(options); const headers = { "content-type": "application/json" }; if (bearerToken) { headers.authorization = `Bearer ${bearerToken}`; } return headers; } async function getForm({ apiUrl, projectId, formId, language, versionStatus }, signal) { const query = versionStatus === "latest" ? `?versionStatus=${versionStatus}` : ""; const headers = await getDefaultHeaders({ apiUrl }); const response = await fetch(`${apiUrl}/api/forms/projects/${projectId}/contentTypes/${formId}/languages/${language || "default"}${query}`, { headers, method: "GET", mode: "cors", signal }); if (response.ok) { const result = await response.json(); return result; } else { const error = await response.json(); return Promise.reject({ status: response.status, statusText: response.statusText, message: error?.message, error }); } } async function saveFormResponse({ apiUrl, projectId, formId, language, formVersionNo, versionStatus, formResponse, captcha }) { const headers = await getDefaultHeaders({ apiUrl }); const captchaResponse = Version.isPublishedVersion(versionStatus) && !isLoggedIn() ? await Captcha.submit(formId, captcha) : ""; let url = `${apiUrl}/api/forms/projects/${projectId}/contentTypes/${formId}/languages/${language || "default"}/entries`; url = Version.isPublishedVersion(versionStatus) && formVersionNo ? url : `${url}?contentTypePreviewVersion=${formVersionNo}`; const response = await fetch(url, { headers: { ...headers, ...!!captchaResponse ? { "g-recaptcha-response": captchaResponse } : {} }, method: "POST", body: JSON.stringify(formResponse), mode: "cors" }); if (response.ok) { const result = await response.json(); return result; } else { const error = await response.json(); return Promise.reject({ status: response.status, statusText: response.statusText, message: error?.message, error }); } } var Api = { isLoggedIn, getForm, saveFormResponse }; // src/state/dates/date-utils.ts function pad(n, length = 2) { const padding = Array.from({ length }).map(() => "0").join(""); return `${padding}${n}`.slice(-1 * length); } function padYear(year) { if (year >= 100) { return year; } year = 1900 + year; const currentYear = (/* @__PURE__ */ new Date()).getUTCFullYear(); return currentYear - year > 80 ? year + 100 : year; } function toNumber(n) { if (typeof n === "number") { return n; } if (typeof n === "string" && !!n) { const num = Number(n); if (!Number.isNaN(num)) { return num; } } return null; } function isWithinRange(n, min, max) { if (typeof n === "number") { return n >= min && n <= max; } return false; } var INVALID_DATE = "Invalid date"; var INVALID_TIME = "Invalid time"; function isDate(d) { return Object.prototype.toString.call(d) === "[object Date]"; } function isValidDate(dt) { return !Number.isNaN(Number(dt)); } function validateDateTimeParts(parts) { if (!parts?.year && !parts?.month && !parts?.day && !parts?.hour && !parts?.minute) { return { datetime: null }; } const { year, month, day, ...timeParts } = parts; const date = validateDateParts({ year, month, day }); const time = validateTimeParts(timeParts); if (date.invalid || time.invalid) { return { datetime: INVALID_DATE, invalid: [...date.invalid || [], ...time.invalid || []] }; } return { datetime: date.date.substring(0, 11) + time.time }; } function validateDateParts(parts) { if (!parts?.year && !parts?.month && !parts?.day) { return { date: null }; } const year = toNumber(parts.year); const month = toNumber(parts.month); const day = toNumber(parts.day); const invalid = []; if (!isWithinRange(year, 0, 3e3)) { invalid.push("year"); } if (!isWithinRange(month, 1, 12)) { invalid.push("month"); } if (!isWithinRange(day, 1, 31)) { invalid.push("day"); } if (!invalid.includes("year") && !invalid.includes("month") && !invalid.includes("day")) { const monthDays = daysInMonth(year, month); if (day > monthDays) { invalid.push("day"); } } if (invalid.length) { return { date: INVALID_DATE, invalid }; } return { date: `${padYear(year)}-${pad(month)}-${pad(day)}T00:00` }; } function validateTimeParts(parts) { if (!parts?.hour && !parts?.minute) { return { time: null }; } const hour = toNumber(parts.hour); const minute = toNumber(parts.minute); const { timeFormat, period } = parts; let hour24 = hour; const invalid = []; if (timeFormat === "12h") { if (!isWithinRange(hour, 1, 12)) { invalid.push("hour"); } else { if (hour === 12) { hour24 = period === "am" ? 0 : hour; } else { hour24 = period === "pm" ? hour + 12 : hour; } } } else { if (!isWithinRange(hour, 0, 23)) { invalid.push("hour"); } } if (!isWithinRange(minute, 0, 59)) { invalid.push("minute"); } if (invalid.length) { return { time: INVALID_TIME, invalid }; } return { time: `${pad(hour24)}:${pad(minute)}` }; } function daysInMonth(year, month) { const date = new Date(year, month, 1); date.setDate(0); return date.getDate(); } // src/state/dates/time-parsing.ts function parseTime(time) { if (!time) { return time; } const match = [match3Digit, match4Digit].reduce( (prev, fn) => { return prev || fn(time); }, null ); if (!match) { return INVALID_TIME; } let { hour, minute, period } = match; if (Number.isNaN(hour) || Number.isNaN(minute)) { return INVALID_TIME; } if (hour === 24) { if (minute === 0) { hour = 0; } else { return INVALID_TIME; } } if (hour === 0 && period === "pm") { return INVALID_TIME; } if (period === "am" && hour > 12) { return INVALID_TIME; } if (period === "am" && hour === 12) { hour = 0; } if (period === "pm" && hour < 12) { hour += 12; } if (hour > 24 || hour < 0) { return INVALID_TIME; } if (minute > 59 || minute < 0) { return INVALID_TIME; } return `${pad(hour)}:${pad(minute)}`; } function match3Digit(time) { const timeRegex = /^(\d{1,3})([ .])?(am|a.m|a.m.|pm|p.m|p.m.)?$/; const match = time.match(timeRegex); if (!match) { return null; } let t = Number(match[1]); if (Number.isNaN(t)) { return null; } let hour = 0; let minute = 0; const period = match[3]; const isAm = !!period?.startsWith("a"); const isPm = !!period?.startsWith("p"); if (t < 100) { hour = t; } else { hour = Math.floor(t / 100); minute = t % 100; } return { hour, minute, period: isAm ? "am" : isPm ? "pm" : null }; } function match4Digit(time) { const timeRegex = /^(\d{1,2})([.: ])?(\d{1,2})?([ .])?(am|a.m|am.|a.m.|pm|p.m|pm.|p.m.)?$/; const match = time.match(timeRegex); if (!match) { return null; } let hour = Number(match[1]); const minute = Number(match[3]); const period = match[5]; const isAm = !!period?.startsWith("a"); const isPm = !!period?.startsWith("p"); return { hour, minute, period: isAm ? "am" : isPm ? "pm" : null }; } // src/state/dates/locale.ts var DEFAULT_LOCALE = "default"; function isDatePartKey(key) { return key === "day" || key === "month" || key === "year"; } var localeInfo = /* @__PURE__ */ function() { let info = void 0; const createLocaleInfo = function() { const shortDateFormatter = new Intl.DateTimeFormat(DEFAULT_LOCALE, { dateStyle: "short" }); const shortTimeFormatter = new Intl.DateTimeFormat(DEFAULT_LOCALE, { timeStyle: "short" }); const shortDatePartsOrder = shortDateFormatter.formatToParts().map((p) => p.type).filter(isDatePartKey); const formatters = [dateFormatter("short"), dateFormatter("medium"), dateFormatter("long"), dateFormatter("full")]; return { formatters, shortDateMatchToParts(match) { return { [shortDatePartsOrder[0]]: match[0], [shortDatePartsOrder[1]]: match[1], [shortDatePartsOrder[2]]: match[2] }; }, shortDateTimeMatchToParts(match) { return { [shortDatePartsOrder[0]]: match[0], [shortDatePartsOrder[1]]: match[1], [shortDatePartsOrder[2]]: match[2], hour: match[3], minute: match[4] }; }, toShortDateString(input) { if (!input) { return ""; } const dt = isDate(input) ? input : new Date(input); return isValidDate(dt) ? shortDateFormatter.format(dt) : ""; }, toShortDateTimeString(input) { if (!input) { return ""; } const dt = isDate(input) ? input : new Date(input); return isValidDate(dt) ? `${shortDateFormatter.format(dt)} ${shortTimeFormatter.format(dt)}` : ""; } }; }; return function() { info = info || createLocaleInfo(); return info; }; }(); function dateFormatter(dateStyle) { const dtf = new Intl.DateTimeFormat(DEFAULT_LOCALE, { dateStyle }); const parts = dtf.formatToParts(); const monthNames = Array.from({ length: 12 }).map((_, i) => { const parts2 = dtf.formatToParts(new Date(2e3, i, 1)); const monthPart = parts2.find((p) => p.type === "month"); return monthPart?.value || ""; }); return { parts, monthNames }; } function parseDateFromFormatter(input, formatter) { const pattern = createDateParsePattern(formatter.parts); const result = {}; for (const p of pattern) { if (p.literal) { const i = input.indexOf(p.literal); if (i >= 0) { result[p.type] = input.substring(0, i); input = input.substring(i + p.literal.length); } } else { result[p.type] = input; } } if (result.day && result.month && result.year) { let month = Number(result.month); if (Number.isNaN(month)) { const index = formatter.monthNames.findIndex((m) => !!m && m?.toUpperCase() === result.month?.toUpperCase()); month = index >= 0 ? index + 1 : month; } const dt = validateDateParts({ year: result.year, month: `${month}`, day: result.day }); if (!dt.invalid) { return dt; } } } function createDateParsePattern(parts) { return parts.reduce((prev, part) => { if (part.type === "literal") { if (!!prev[prev.length - 1]) { prev[prev.length - 1].literal = part.value; } } else { prev.push({ type: part.type }); } return prev; }, []); } function parseDateTimeFromFormatter(input, formatter) { const pattern = createDateTimeParsePattern(formatter.parts); const result = {}; for (const p of pattern) { if (p.literal) { const i = input.indexOf(p.literal); if (i >= 0) { result[p.type] = input.substring(0, i); input = input.substring(i + p.literal.length); } } else { result[p.type] = input; } } let time = ""; if (input) { time = parseTime(input); if (!time) { return void 0; } } if (result.day && result.month && result.year) { let month = Number(result.month); if (Number.isNaN(month)) { const index = formatter.monthNames.findIndex((m) => !!m && m?.toUpperCase() === result.month?.toUpperCase()); month = index >= 0 ? index + 1 : month; } const hour = !!time ? time.split(":")[0] : void 0; const minute = !!time ? time.split(":")[1] : void 0; const dt = validateDateTimeParts({ year: result.year, month: `${month}`, day: result.day, hour, minute }); if (!dt.invalid) { return dt; } } } function createDateTimeParsePattern(parts) { return parts.reduce((prev, part) => { if (part.type === "literal") { if (!!prev[prev.length - 1]) { prev[prev.length - 1].literal = part.value; } } else { prev.push({ type: part.type, literal: " " }); } return prev; }, []); } // src/state/dates/date-parsing.ts var ISO_REG_EX = /^([0-9]{4})-([0-1][0-9])(?:-([0-3][0-9]))?(?:[T ]?([0-2][0-9])(?::([0-5][0-9]))?(?::([0-5][0-9]))?)?(?:\.([0-9]+))?(Z|(?:\+|-)[0-9]{4})?$/; var SHORT_DATE_REG_EX = /^(\d{1,2})(\/|-|\.)(\d{1,2})(\/|-|\.)(\d{2,4})$/; function parseDate(date) { if (!date) { return date; } let d = parseIso(date); if (d) { return d.date; } d = parseShortDate(date); if (d) { return d.date; } d = localeInfo().formatters.reduce((prev, formatter) => prev || parseDateFromFormatter(date, formatter), void 0); if (d) { return d.date; } return INVALID_DATE; } function parseIso(date) { let match = date.match(ISO_REG_EX); if (match) { const year = match[1]; const month = match[2]; const day = match[3]; return validateDateParts({ year, month, day }); } } function parseShortDate(date) { let match = date.match(SHORT_DATE_REG_EX); if (match) { const dateParts = localeInfo().shortDateMatchToParts([match[1], match[3], match[5]]); return validateDateParts(dateParts); } } // src/state/dates/date-time-parsing.ts var ISO_REG_EX2 = /^([0-9]{4})-([0-1][0-9])(?:-([0-3][0-9]))?(?:[T ]?([0-2][0-9])(?::([0-5][0-9]))?(?::([0-5][0-9]))?)?(?:\.([0-9]+))?(Z|(?:\+|-)[0-9]{4})?$/; var SHORT_DATE_TIME_REG_EX = /^(\d{1,2})(\/|-|\.)(\d{1,2})(\/|-|\.)(\d{2,4}) (\d{1,2}):(\d{1,2})$/; function parseDateTime(date) { if (!date) { return date; } let d = parseIso2(date); if (d) { return d.datetime; } d = parseShortDateTime(date); if (d) { return d.datetime; } d = localeInfo().formatters.reduce((prev, formatter) => prev || parseDateTimeFromFormatter(date, formatter), void 0); if (d) { return d.datetime; } return INVALID_DATE; } function parseIso2(date) { let match = date.match(ISO_REG_EX2); if (match) { const year = match[1]; const month = match[2]; const day = match[3]; const hour = match[4]; const minute = match[5]; return validateDateTimeParts({ year, month, day, hour, minute, period: "am", timeFormat: "24h" }); } } function parseShortDateTime(date) { let match = date.match(SHORT_DATE_TIME_REG_EX); if (match) { const dateParts = localeInfo().shortDateTimeMatchToParts([match[1], match[3], match[5], match[6], match[7]]); return validateDateTimeParts(dateParts); } } // src/state/dates/dates.ts function getNowDate() { return toLocalIsoDate(/* @__PURE__ */ new Date()); } function getNowDateTime() { return toLocalIsoDateTime(/* @__PURE__ */ new Date()); } function getNowTime() { return toLocalIsoTime(/* @__PURE__ */ new Date()); } function toLocalIsoDate(dt) { return `${dt.getFullYear()}-${pad2(dt.getMonth() + 1)}-${pad2(dt.getDate())}T00:00`; } function toLocalIsoDateTime(dt) { return `${dt.getFullYear()}-${pad2(dt.getMonth() + 1)}-${pad2(dt.getDate())}T${pad2(dt.getHours())}:${pad2(dt.getMinutes())}`; } function toDateParts(input) { const { year, month, day } = toDateTimeParts(input, "24h"); return { year, month, day }; } function timeToTimeParts(hour, minute, timeFormat) { let period = "am"; if (timeFormat === "12h") { if (hour === 0) { hour = 12; } else if (hour === 12) { period = "pm"; } else if (hour > 12) { hour = hour - 12; period = "pm"; } } return { hour: timeFormat === "24h" ? pad2(hour) : `${hour}`, minute: pad2(minute), period, timeFormat }; } function toDateTimeParts(input, timeFormat) { if (!input) { return { year: "", month: "", day: "", hour: "", minute: "", period: "am", timeFormat }; } const dt = isDate(input) ? input : new Date(input); if (isValidDate(dt)) { return { year: `${dt.getFullYear()}`, month: `${dt.getMonth() + 1}`, day: `${dt.getDate()}`, ...timeToTimeParts(dt.getHours(), dt.getMinutes(), timeFormat) }; } else { return { year: "", month: "", day: "", hour: "", minute: "", period: "am", timeFormat }; } } function toTimeParts(input, timeFormat) { const dateParts = toDateTimeParts(input, timeFormat); if (dateParts.hour && dateParts.minute) { return { hour: dateParts.hour, minute: dateParts.minute, period: dateParts.period, timeFormat: dateParts.timeFormat }; } if (typeof input === "string") { const parts = input.split(":"); if (parts.length === 2) { const hourNum = parseInt(parts[0], 10); const minuteNum = parseInt(parts[1], 10); if (!Number.isNaN(hourNum) && !Number.isNaN(minuteNum)) { return timeToTimeParts(hourNum, minuteNum, timeFormat); } } } return { hour: "", minute: "", period: "am", timeFormat }; } function toLocalIsoTime(dt) { return `${pad2(dt.getHours())}:${pad2(dt.getMinutes())}`; } function pad2(n, length = 2) { const padding = Array.from({ length }).map(() => "0").join(""); return `${padding}${n}`.slice(-1 * length); } // src/state/dates/index.ts var DateTime = { getNowDate, getNowDateTime, getNowTime, localeInfo, parseDate, parseDateTime, parseTime, toDateParts, toDateTimeParts, toTimeParts, toLocalIsoDateTime, validateDateParts, validateDateTimeParts, validateTimeParts }; var DateTimeSettings = { dateFormats: { dd_mm_yyyy: "dd-mm-yyyy", mm_dd_yyyy: "mm-dd-yyyy", yyyy_mm_dd: "yyyy-mm-dd" }, timeFormats: { h12: "12h", h24: "24h" }, defaultSeparators: { date: "/", time: ":" }, timePeriods: { am: "am", pm: "pm" } }; // src/state/errors.ts function handleError(error) { console.log(error); } function getErrorMessages(errors) { return !!errors ? Object.values(errors).map((e) => e.message) : null; } var Errors = { handleError, getErrorMessages }; // src/state/localisations.ts function format(s, ...args) { if (args.length === 0 || !s) { return ""; } return s.replace(/{([^{}]*)}/g, (_a, b) => { const bValue = parseInt(b, 10); if (!Number.isNaN(bValue) && bValue < args.length) { return args[bValue]; } return ""; }); } function plural(locale, value, fns) { const pluralRules = new Intl.PluralRules(locale); const rule = pluralRules.select(value); const fn = fns[rule]; return fn(); } function getPageTitle(defaultPageTitle, pageTitle, pageNo, pageCount, hasError, localizations) { let result = defaultPageTitle; if (pageCount > 1) { const pageProgress = format(localizations.messages.page, pageNo, pageCount); result = `${defaultPageTitle} - ${pageProgress}`; if (pageTitle) { result = `${result} - ${pageTitle}`; } } if (hasError) { result = `${localizations.error.pageTitle}: ${result}`; } return result; } // src/state/store.ts function memo(projector) { let lastArgs = null; let lastResult = null; return function(...args) { if (lastArgs && lastArgs.length === args.length && lastArgs.every((a, index) => Object.is(a, args[index]))) { return lastResult; } lastArgs = args; lastResult = projector.apply(null, args); return lastResult; }; } // src/state/validation.ts var createFieldValidator = memo((field, language, localizations) => { const dataTypeMessage = localizations.validation.dataType; const dateFormatMessages = { ...localizations.validation.dataFormat, reference: "" }; const validators = [ ["dataType", createDataTypeValidator(field.dataType), format(dataTypeMessage[field.dataType], field.name)], ["dataFormat", createDataFormatValidator(field.dataFormat), field.dataFormat ? format(dateFormatMessages[field.dataFormat], field.name) : ""], [ "required", createRequiredValidator(field.validations?.required), field.validations?.required?.message || format(localizations.validation.required, field.name) ], [ "allowedValue", createAllowedValueValidator(field.validations?.allowedValue), field.validations?.allowedValue?.message || format(localizations.validation.allowedValue, field.name) ], [ "size", createSizeValidator(field.validations?.min, field.validations?.max), getRangeErrorMessage( field.validations?.min, field.validations?.max, format(localizations.validation.min, field.name, field.validations?.min?.value), format(localizations.validation.max, field.name, field.validations?.max?.value), format(localizations.validation.minMax, field.name, field.validations?.min?.value, field.validations?.max?.value) ) ], [ "length", createLengthValidator(field.validations?.minLength, field.validations?.maxLength), getRangeErrorMessage( field.validations?.minLength, field.validations?.maxLength, format(localizations.validation.minLength, field.name, field.validations?.minLength?.value), format(localizations.validation.maxLength, field.name, field.validations?.maxLength?.value), format(localizations.validation.minMax, field.name, field.validations?.minLength?.value, field.validations?.maxLength?.value) ) ], [ "count", createCountValidator(field.validations?.minCount, field.validations?.maxCount), getRangeErrorMessage( field.validations?.minCount, field.validations?.maxCount, countMessage(language, field.validations?.minCount?.value, field, localizations.validation.minCount), countMessage(language, field.validations?.maxCount?.value, field, localizations.validation.maxCount), format(localizations.validation.minMaxCount, field.name, field.validations?.minCount?.value, field.validations?.maxCount?.value) ) ], ["regex", createRegExValidator(field.validations?.regex), field.validations?.regex?.message || format(localizations.validation.regEx, field.name)], [ "allowedValues", createAllowedValuesValidator(field.validations?.allowedValues), field.validations?.allowedValues?.message || format(localizations.validation.allowedValues, field.name) ], [ "pastDateTime", createPastDateTimeValidator(field.dataType, field.validations?.pastDateTime), field.validations?.pastDateTime?.message || format(localizations.validation.pastDateTime, field.name) ] ]; return function(value) { return validators.reduce( (prev, [key, validator, message]) => { const error = validator(value); if (error) { prev = prev || {}; prev = { ...prev, [key]: { ...error, message } }; } return prev; }, null ); }; }); function noopValidator() { return null; } function isNull(value) { return value == null; } function isEmpty(value) { return isNull(value) || value.length === 0; } function hasLength(value) { return typeof value === "string" || Array.isArray(value); } function fromValid(fn, getResult) { return (value) => { const isValid = fn(value); return isValid ? null : getResult(); }; } function createDataTypeValidator(dataType) { return fromValid( (value) => { if (isNull(value)) { return true; } switch (dataType) { case "boolean": { return typeof value === "boolean"; } case "dateTime": { if (typeof value !== "string") { return false; } const d = new Date(value); return !Number.isNaN(d.getTime()); } case "decimal": { return typeof value === "number"; } case "integer": { return typeof value === "number" && Number.isSafeInteger(value); } case "string": { return typeof value === "string"; } case "stringArray": { return Array.isArray(value); } } }, () => ({}) ); } function createDataFormatValidator(dataFormat) { return fromValid( (value) => { if (isEmpty(value) || !dataFormat) { return true; } switch (dataFormat) { case "email": { const emailRegex = new RegExp( '^(([^<>()\\[\\]\\.,;:\\s@\\"]+(\\.[^<>()\\[\\]\\.,;:\\s@\\"]+)*)|(\\".+\\"))@(([^<>()[\\]\\.,;:\\s@\\"]+\\.)+[^<>()[\\]\\.,;:\\s@\\"]{2,})$', "i" ); return typeof value === "string" && emailRegex.test(value); } case "phone": { return true; } case "reference": { return true; } case "time": { const timeRegex = new RegExp("^([01]?[0-9]|2[0-3])([:. ])([0-5]\\d)(\\2[0-5]\\d)?$"); return typeof value === "string" && timeRegex.test(value); } case "url": { const urlRegex = new RegExp( "^(?:(?:(?:https?|ftp):)?\\/\\/)(?:\\S+(?::\\S*)?@)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z0-9\\u00a1-\\uffff][a-z0-9\\u00a1-\\uffff-]{0,62})?[a-z0-9\\u00a1-\\uffff]\\.)+(?:[a-z\\u00a1-\\uffff]{2,}\\.?))(?::\\d{2,5})?(?:[/?#]\\S*)?$" ); return typeof value === "string" && urlRegex.test(value); } } }, () => ({}) ); } function createRequiredValidator(required) { return !!required ? fromValid( (value) => !isEmpty(value), () => ({}) ) : noopValidator; } function createSizeValidator(min, max) { return !!min || !!max ? fromValid( (value) => { let valid = true; if (typeof value === "number") { if (min) { valid = value >= min.value; } if (valid && max) { valid = value <= max.value; } } return valid; }, () => ({ min: min?.value, max: max?.value }) ) : noopValidator; } function createLengthValidator(minLength, maxLength) { return !!minLength || !!maxLength ? fromValid( (value) => { let valid = true; if (hasLength(value)) { if (minLength) { valid = value.length >= minLength.value; } if (valid && maxLength) { valid = value.length <= maxLength.value; } } return valid; }, () => ({ minLength: minLength?.value, maxLength: maxLength?.value }) ) : noopValidator; } function createCountValidator(minCount, maxCount) { return !!minCount || !!maxCount ? fromValid( (value) => { let valid = true; if (hasLength(value)) { if (minCount) { valid = value.length >= minCount.value; } if (valid && maxCount) { valid = value.length <= maxCount.value; } } return valid; }, () => ({ minCount: minCount?.value, maxCount: maxCount?.value }) ) : noopValidator; } function createRegExValidator(regex) { if (!regex?.pattern) { return noopValidator; } const pattern = regex.pattern; const isValidPattern = pattern && pattern.length > 3 && pattern.startsWith("/"); const flagsIndex = pattern.lastIndexOf("/"); const patternWithoutFlags = isValidPattern ? pattern.substring(1, flagsIndex) : pattern; const flags = !isValidPattern || flagsIndex === pattern.length - 1 || flagsIndex < 0 ? "" : pattern.substring(flagsIndex + 1); return fromValid( (value) => { if (isEmpty(value)) { return true; } const regex2 = new RegExp(patternWithoutFlags, flags); return typeof value === "string" && regex2.test(value); }, () => ({ pattern }) ); } function createAllowedValueValidator(allowedValue) { const allowed = allowedValue?.value; if (isNull(allowed)) { return noopValidator; } return fromValid( (value) => { return value === allowed; }, () => ({ allowed }) ); } function createAllowedValuesValidator(allowedValues) { const allowed = allowedValues?.labeledValues ? allowedValues.labeledValues.map((value) => value.value) : allowedValues?.values; if (!allowed?.length) { return noopValidator; } return fromValid( (value) => { if (isEmpty(value)) { return true; } return Array.isArray(value) ? value.every((v) => allowed.includes(v)) : allowed.includes(value); }, () => ({ allowed }) ); } function createPastDateTimeValidator(dataType, pastDateTime) { return !!pastDateTime ? fromValid( (value) => { if (isNull(value)) { return true; } if (dataType === "dateTime") { const now = /* @__PURE__ */ new Date(); const dt = /* @__PURE__ */ new Date(`${value}`); return dt.getTime() <= now.getTime(); } return true; }, () => ({}) ) : noopValidator; } function validate(value, language, field, localizations) { if (field?.editor?.properties?.hidden) { return null; } const validator = createFieldValidator(field, language, localizations); return validator(value); } function countMessage(language, value, field, localizations) { value = value || 0; return plural(language, value, { zero: () => format(localizations.zero, field.name, value), one: () => format(localizations.one, field.name, value), two: () => format(localizations.two, field.name, value), few: () => format(localizations.few, field.name, value), many: () => format(localizations.many, field.name, value), other: () => format(localizations.other, field.name, value) }); } function getRangeErrorMessage(min, max, defaultMinMessage, defaultMaxMessage, defaultRangeMessage) { if (min?.message) { return min.message; } if (max?.message) { return max.message; } if (min && max) { return defaultRangeMessage; } if (min) { return defaultMinMessage; } if (max) { return defaultMaxMessage; } return ""; } var Validation = { validate }; // src/state/fields.ts var DEFAULT_DATA_TYPE_EDITOR_TYPES = { boolean: "checkbox", dateTime: "date", decimal: "decimal", integer: "integer", string: "text", stringArray: "radio" }; var DEFAULT_DATA_FORMAT_EDITOR_TYPES = { email: "email", phone: "tel", reference: "reference", time: "time", url: "url" }; var DEFAULT_EDITOR_ID_EDITOR_TYPES = { date: "date", dateparts: "dateparts", datetime: "datetime", datetimeparts: "datetimeparts", decimal: "decimal", integer: "integer", list: "radio", "list-dropdown": "select", multiline: "multiline", text: "text", time: "time", timeparts: "timeparts" }; function getEditorType(field) { let editorType = field.editor?.id ? DEFAULT_EDITOR_ID_EDITOR_TYPES[field.editor.id] : null; if (!editorType && field.dataFormat) { editorType = DEFAULT_DATA_FORMAT_EDITOR_TYPES[field.dataFormat]; } if (!editorType && (field.validations?.allowedValues?.values || field.validations?.allowedValues?.labeledValues)) { editorType = field.dataType === "stringArray" ? "multiselect" : "radio"; } editorType = editorType || DEFAULT_DATA_TYPE_EDITOR_TYPES[field.dataType]; return editorType || "text"; } var EMPTY_FIELD_VALUES = { boolean: false, dateTime: null, decimal: null, integer: null, string: "", stringArray: null }; function getEmptyFieldValue(field) { return EMPTY_FIELD_VALUES[field.dataType]; } function getOptions(field, htmlId, localizations) { const pairs = field?.validations?.allowedValues?.labeledValues ? field?.validations?.allowedValues?.labeledValues?.map((value) => value) : field?.validations?.allowedValues?.values?.map((value) => ({ value, label: value })); let options = pairs?.filter((pair) => !!pair.value).map((pair, index) => { return { key: `${index}`, htmlId: `${htmlId}-option-${index}`, value: pair.value || "", label: pair.label }; }); if (getEditorType(field) === "select") { options = [ { key: "", htmlId: `${htmlId}-option--1`, value: "", label: field?.editor?.properties?.placeholderText || localizations.labels.selectPlaceholder }, ...options || [] ]; } return options; } function getDefaultValue(field) { const defaultValue = typeof field?.default !== "undefined" && field?.default !== null ? field.default : getEmptyFieldValue(field); if (field.dataType === "dateTime" && defaultValue === "now()") { const editorType = getEditorType(field); return editorType === "datetime" || editorType === "datetimeparts" ? DateTime.getNowDateTime() : DateTime.getNowDate(); } else if (field.dataType === "string" && field.dataFormat === "time" && defaultValue === "now()") { return DateTime.getNowTime(); } return defaultValue; } function getInputValue(field, value) { const editor = getEditorType(field); if (field.dataType === "dateTime") { if (editor === "datetime") { return DateTime.localeInfo().toShortDateTimeString(value); } if (editor === "date") { return DateTime.localeInfo().toShortDateString(value); } if (editor === "datetimeparts") { return DateTime.toDateTimeParts(value, field.editor?.properties?.timeFormat || "24h"); } if (editor === "dateparts") { return DateTime.toDateParts(value); } } if (field.dataType === "string" && field.dataFormat === "time") { if (editor === "timeparts") { return DateTime.toTimeParts(value, field.editor?.properties?.timeFormat || "24h"); } } return value; } function reduceFields(form, fn) { return form?.fields ? form.fields.reduce((prev, field, index) => ({ ...prev, [field.id]: fn(field, index) }), {}) : {}; } function validate2(field, value, language, localizations) { return Validation.validate(value, language, field, localizations); } function isNullish(o) { return o === null || typeof o === "undefined"; } function getInitialValue(field, language, query, progressValue, localizations) { let value = null; if (typeof progressValue !== "undefined") { const errors = validate2(field, progressValue, language, localizations); if (!errors?.dataType && !errors?.allowedValues) { value = progressValue; } } if (isNullish(value)) { let queryValue = null; const firstQuery = query?.[0]; switch (field.dataType) { case "boolean": { if (firstQuery === "true" || firstQuery === "checked" || firstQuery === "on") { queryValue = true; } else if (firstQuery === "false" || firstQuery === "unchecked" || firstQuery === "off") { queryValue = false; } break; } case "dateTime": { if (firstQuery) { if (field.dataType === "dateTime") { return getEditorType(field) === "datetime" ? DateTime.parseDateTime(firstQuery) : DateTime.parseDate(firstQuery); } else if (field.dataType === "string" && field.dataFormat === "time") { return DateTime.parseTime(firstQuery); } } break; } case "decimal": { queryValue = !!firstQuery ? parseFloat(firstQuery) : null; break; } case "integer": { queryValue = !!firstQuery ? parseInt(firstQuery, 10) : null; break; } case "string": { queryValue = firstQuery; break; } case "stringArray": { if (query) { if (field.validations?.allowedValues) { const allowed = field.validations.allowedValues.labeledValues ? field.validations.allowedValues.labeledValues.map((value2) => value2.value) : field.validations.allowedValues.values; if (allowed) { queryValue = query.filter((item) => allowed.includes(item)); } } else { queryValue = query; } } break; } } if (queryValue !== null) { const errors = validate2(field, queryValue, language, localizations); if (!errors?.dataType && !errors?.allowedValues) { value = queryValue; } } } if (isNullish(value)) { value = getDefaultValue(field); } return value; } var Fields = { getEditorType, getOptions, getInitialValue, getInputValue, reduceFields, validate: validate2 }; // src/state/progress.ts var AUTO_SAVE_PROGRESS_EXPIRY_DAYS = 30; function getProgressExpiry() { const d = /* @__PURE__ */ new Date(); d.setDate(d.getDate() + AUTO_SAVE_PROGRESS_EXPIRY_DAYS); return DateTime.toLocalIsoDateTime(d); } function autoSave(form, value) { if (form?.id && form?.properties?.autoSaveProgress) { localStorage.setItem(`contensis-form-${form?.id}-expiry`, getProgressExpiry()); localStorage.setItem(`contensis-form-${form?.id}-value`, !!value ? JSON.stringify(value) : ""); } } function reset(form) { if (form?.id) { localStorage.removeItem(`contensis-form-${form?.id}-expiry`); localStorage.removeItem(`contensis-form-${form?.id}-value`); } } function load2(form) { if (!!form) { const expiry = localStorage.getItem(`contensis-form-${form.id}-expiry`); const jsonValue = localStorage.getItem(`contensis-form-${form.id}-value`); const d = DateTime.getNowDateTime(); if (expiry && jsonValue && d < expiry) { try { const value = JSON.parse(jsonValue); return { value }; } catch { } } } return null; } function loadQuery() { if (window?.location?.search) { const params = new URLSearchParams(window.location.search); return [...params.keys()].reduce( (prev, key) => ({ ...prev, [key]: params.getAll(key) }), {} ); } return {}; } var Progress = { autoSave, reset, load: load2, loadQuery }; // src/state/form.ts function getInputValue2(form, value) { return Fields.reduceFields(form, (field) => Fields.getInputValue(field, value?.[field.id])); } function validate3(form, value, localizations) { return Fields.reduceFields(form, (field) => Validation.validate(value?.[field.id], form.language, field, localizations)); } function getPages(form) { if (!form) { return []; } if (form?.properties?.mode === "survey") { return form.fields.map((field, index) => { const { id, name, description } = field; return { pageNo: index + 1, id, title: name, description, fields: [field] }; }); } else if (!form.groups) { return [ { pageNo: 1, id: "page1", title: "", description: null, fields: form.fields } ]; } else { return form.groups.map((group, index) => { const { id, name, description } = group; return { pageNo: index + 1, id, title: name, description, fields: (form?.fields || []).filter((field) => field.groupId === id) }; }); } } function getInitialValue2(form, localizations) { const query = Progress.loadQuery(); const progress = Progress.load(form); return Fields.reduceFields(form, (field) => Fields.getInitialValue(field, form.language, query?.[field.id], progress?.value?.[field.id], localizations)); } function pageHasErrors(page, errors) { return !!page && !!page.fields.some((field) => !!errors?.[field.id]); } var Form = { getInputValue: getInputValue2, getPages, getInitialValue: getInitialValue2, pageHasErrors, validate: validate3 }; // src/state/rules.ts function isConfirmationRuleReturnUri(r) { return !!r?.link?.sys?.uri; } function isConfirmationRuleReturnContent(r) { return !!r?.content; } var Rules = { isConfirmationRuleReturnContent, isConfirmationRuleReturnUri }; // src/components/FormConfirmation.tsx var import_react2 = __toESM(require("react")); // src/components/FormRenderContext.tsx var import_react = __toESM(require("react")); var DEFAULT_LANGUAGE = "en-GB"; var DEFAULT_HEADING_LEVEL = 3; var DEFAULT_LOCALIZATIONS = { buttons: { next: "Next", // next previous: "Previous", // previous submit: "Submit" //submit }, load: { error: "Error loading form", // na loading: "Loading..." // na }, error: { label: "Error", // errorLabel pageTitle: "Error", // errorPageTitle summaryTitle: "There is a problem" // errorSummaryTitle }, messages: { confirmation: "Form submitted successfully", // na page: "Page {0} of {1}" // pageHeader }, labels: { selectPlaceholder: "Please select", // na required: "required" // requiredLabel }, dates: { day: "Day", // dateInputDayLabel month: "Month", // dateInputMonthLabel year: "Year", // dateInputYearLabel hour: "Hour", // dateInputHourLabel minute: "Minute", // dateInputMinuteLabel period: "Period" // dateInputPeriodLabel }, validation: { dataType: { boolean: "{0} is not a boolean", // validationDataTypeBoolean dateTime: "{0} is not a date", // validationDataTypeDateTime decimal: "{0} is not a number", // validationDataTypeDecimal integer: "{0} is not an integer", // // validationDataTypeInteger string: "{0} is not a string", // // validationDataTypeString stringArray: "{0} is not an array" // validationDataTypeStringArray }, dataFormat: { email: "{0} is not a valid email", //validationDataFormatEmail phone: "{0} is not a valid telephone number", // validationDataFormatPhone time: "{0} is not a valid time", // validationDataFormatTime url: "{0} is not a valid URL" // validationDataFormatUrl }, minCount: { zero: "{0} requires a minimum of {1} items", // validationMinCountZero one: "{0} requires a minimum of {1} item", // validationMinCountOne two: "{0} requires a minimum of {1} items", // validationMinCountTwo few: "{0} requires a minimum of {1} items", // validationMinCountFew many: "{0} requires a minimum of {1} items", // validationMinCountMany other: "{0} requires a minimum of {1} items" // validationMinCountOther }, maxCount: { zero: "{0} requires a maximum of {1} items", // validationMaxCountZero one: "{0} requires a maximum of {1} item", // validationMaxCountOne two: "{0} requires a maximum of {1} items", // validationMaxCountTwo few: "{0} requires a maximum of {1} items", // validationMaxCountFew many: "{0} requires a maximum of {1} items", // validationMaxCountMany other: "{0} requires a maximum of {1} items" // validationMaxCountOther }, required: "{0} is required", // validationRequired allowedValue: "{0} is required", // validationAllowedValue allowedValues: "{0} has an invalid value", // validationAllowedValues regEx: "{0} has an invalid format", // validationRegEx pastDateTime: "{0} must be in the past", // validationPastDateTime min: "{0} must be minimum of {1}", // validationMin max: "{0} must be a maximum of {1}", // validationMax minMax: "{0} must be between {1} and {2}", // validationMinMax minLength: "{0} must have a minimum length of {1}", // validationMinLength maxLength: "{0} must have a maximum length of {1}", // validationMaxLength minMaxLength: "{0} must have a length between {1} and {2}", // validationMinMaxLength minMaxCount: "{0} requires between {1} and {2} items" // validationMinMaxCount }, characterCount: { remaining: { zero: "You have {0} characters remaining", // characterCountRemainingZero one: "You have {0} character remaining", // characterCountRemainingOne two: "You have {0} characters remaining", // characterCountRemainingTwo few: "You have {0} characters remaining", // characterCountRemainingFew many: "You have