@aemforms/af-custom-functions
Version:
Provides a collection of custom functions designed to aid creation of adaptive form
284 lines (265 loc) • 10.5 kB
JavaScript
/**
* Validates if the given URL is correct.
* @param {string} url - The URL to validate.
* @returns {boolean} - True if the URL is valid, false otherwise.
*/
function validateURL(url) {
try {
const validatedUrl = new URL(url, window.location.href);
return (validatedUrl.protocol === 'http:' || validatedUrl.protocol === 'https:');
}
catch (err) {
return false;
}
}
/**
* Converts a JSON string to an object.
* @param {string} str - The JSON string to convert to an object.
* @returns {object} - The parsed JSON object. Returns an empty object if an exception occurs.
* @memberof module:FormView~customFunctions
*/
function toObject(str) {
if (typeof str === 'string') {
try {
return JSON.parse(str);
} catch (e) {
return {};
}
}
return str;
}
/**
* Navigates to the specified URL.
* @param {string} destinationURL - The URL to navigate to. If not specified, a new blank window will be opened.
* @param {string} destinationType - The type of destination. Supports the following values: "_newwindow", "_blank", "_parent", "_self", "_top", or the name of the window.
* @returns {Window} - The newly opened window.
*/
function navigateTo(destinationURL, destinationType) {
let param = null,
windowParam = window,
arg = null;
switch (destinationType){
case "_newwindow":
param = "_blank";
arg = "width=1000,height=800";
break;
}
if (!param) {
if (destinationType) {
param = destinationType;
} else {
param = "_blank";
}
}
if (validateURL(destinationURL)){
windowParam.open(destinationURL, param, arg);
}
}
/**
* Default error handler for the invoke service API.
* @param {object} response - The response body of the invoke service API.
* @param {object} headers - The response headers of the invoke service API.
* @param {scope} globals - An object containing read-only form instance, read-only target field instance and methods for form modifications.
* @returns {void}
*/
function defaultErrorHandler(response, headers, globals) {
if(response && response.validationErrors) {
response.validationErrors?.forEach(function (violation) {
if (violation.details) {
if (violation.fieldName) {
globals.functions.markFieldAsInvalid(violation.fieldName, violation.details.join("\n"), {useQualifiedName: true});
} else if (violation.dataRef) {
globals.functions.markFieldAsInvalid(violation.dataRef, violation.details.join("\n"), {useDataRef: true});
}
}
});
}
}
/**
* Handles the success response after a form submission.
*
* @param {scope} globals - An object containing read-only form instance, read-only target field instance and methods for form modifications.
* @returns {void}
*/
function defaultSubmitSuccessHandler(globals) {
const event = globals.event;
const submitSuccessResponse = event?.payload?.body;
const form = globals.form;
if (submitSuccessResponse) {
if (submitSuccessResponse.redirectUrl) {
window.location.href = encodeURI(submitSuccessResponse.redirectUrl);
} else if (submitSuccessResponse.thankYouMessage) {
let formContainerElement = document.getElementById(`${form.$id}`);
let thankYouMessage = document.createElement("div");
thankYouMessage.setAttribute("class", "tyMessage");
thankYouMessage.setAttribute("tabindex", "-1");
thankYouMessage.setAttribute("role","alertdialog");
thankYouMessage.innerHTML = submitSuccessResponse.thankYouMessage;
formContainerElement.replaceWith(thankYouMessage);
thankYouMessage.focus();
}
}
}
/**
* Handles the error response after a form submission.
*
* @param {string} defaultSubmitErrorMessage - The default error message.
* @param {scope} globals - An object containing read-only form instance, read-only target field instance and methods for form modifications.
* @returns {void}
*/
function defaultSubmitErrorHandler(defaultSubmitErrorMessage, globals) {
// view layer should send localized error message here
window.alert(defaultSubmitErrorMessage);
}
/**
* Fetches the captcha token for the form.
*
* This function uses the Google reCAPTCHA Enterprise/turnstile service to fetch the captcha token.
*
* @async
* @param {object} globals - An object containing read-only form instance, read-only target field instance and methods for form modifications.
* @returns {string} - The captcha token.
*/
async function fetchCaptchaToken(globals) {
return new Promise((resolve, reject) => {
// successCallback and errorCallback can be reused for different captcha implementations
const successCallback = function(token) {
resolve(token);
};
const errorCallback = function(error) {
reject(error);
};
try{
const captcha = globals.form.$captcha;
if (captcha.$captchaProvider === "turnstile") {
const turnstileContainer = document.getElementsByClassName("cmp-adaptiveform-turnstile__widget")[0];
const turnstileParameters = {
'sitekey': captcha.$captchaSiteKey,
'callback': successCallback,
'error-callback': errorCallback
}
if(turnstile != undefined) {
const widgetId = turnstile.render(turnstileContainer, turnstileParameters);
if (widgetId) {
turnstile.execute(widgetId);
} else {
reject({error: "Failed to render turnstile captcha"});
}
} else {
reject({error: "Turnstile captcha not loaded"});
}
} else {
const siteKey = captcha?.$properties['fd:captcha']?.config?.siteKey;
const captchaElementName = captcha.$name.replaceAll('-', '_');
let captchaPath = captcha?.$properties['fd:path'];
const index = captchaPath.indexOf('/jcr:content');
let formName = '';
if (index > 0) {
captchaPath = captchaPath.substring(0, index);
formName = captchaPath.substring(captchaPath.lastIndexOf("/") + 1).replaceAll('-', '_');
}
let actionName = `submit_${formName}_${captchaElementName}`;
grecaptcha.enterprise.ready(() => {
grecaptcha.enterprise.execute(siteKey, {action: actionName})
.then((token) => resolve(token))
.catch((error) => reject(error));
});
}
} catch (error) {
reject(error);
}
});
}
/**
* Converts a date to the number of days since the Unix epoch (1970-01-01).
*
* If the input date is a number, it is assumed to represent the number of days since the epoch,
* including both integer and decimal parts. In this case, only the integer part is returned as the number of days.
*
* @param {string|Date|number} date - The date to convert.
* Can be:
* - An ISO string (yyyy-mm-dd)
* - A Date object
* - A number representing the days since the epoch, where the integer part is the number of days and the decimal part is the fraction of the day
*
* @returns {number} - The number of days since the Unix epoch
*/
function dateToDaysSinceEpoch(date) {
let dateObj;
if (typeof date === 'string') {
dateObj = new Date(date);
} else if (typeof date === 'number') {
return Math.floor(date);
} else if (date instanceof Date) {
dateObj = date;
} else {
throw new Error('Invalid date input');
}
// Validate that date is valid after parsing
if (isNaN(dateObj.getTime())) {
throw new Error('Invalid date input');
}
return Math.floor(dateObj.getTime() / (1000 * 60 * 60 * 24));
}
/**
* Prefixes the URL with the context path.
* @param {string} url - The URL to externalize.
* @returns {string} - The externalized URL.
*/
function externalize(url) {
// Check if Granite.HTTP.externalize is available, otherwise return the original URL
if (window?.Granite?.HTTP && typeof window.Granite.HTTP.externalize === "function") {
return window.Granite.HTTP.externalize(url);
} else {
return url;
}
}
/**
* Downloads the Document of Record (DoR) for the form.
*
* @param {string=} fileName - The name of the file to be downloaded. Defaults to "Downloaded_DoR.pdf" if not specified.
* @param {scope} globals - An object containing read-only form instance, read-only target field instance, and methods for form modifications.
* @returns {void}
*/
function downloadDoR(fileName, globals) {
const lang = globals.form.$lang;
const query = new URLSearchParams({ locale: lang }).toString();
let dorApiUrl = externalize('/adobe/forms/af/dor/' + globals.form.$id + '?' + query);
const formData = new FormData();
var jsonString = JSON.stringify(globals.functions.exportData());
formData.append('data', jsonString);
const id = globals.form.$id;
var xhr = new XMLHttpRequest();
xhr.open('POST', dorApiUrl, true);
xhr.responseType = "arraybuffer";
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
const blob = new Blob([xhr.response], { type: 'application/pdf' });
const blobUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = blobUrl;
a.download = fileName || "Downloaded_DoR.pdf";
document.body.appendChild(a);
a.click();
URL.revokeObjectURL(blobUrl);
document.body.removeChild(a);
} else {
console.error("Error while generating pdf");
}
}
};
xhr.send(formData);
}
export {
validateURL,
navigateTo,
toObject,
defaultErrorHandler,
defaultSubmitSuccessHandler,
defaultSubmitErrorHandler,
fetchCaptchaToken,
dateToDaysSinceEpoch,
externalize,
downloadDoR
};