import { applyLocale } from './i18n.js'; /** * @type {Map} * @description Cache for Handlebars templates. */ const TEMPLATE_CACHE = new Map(); /** * Loads a URL content using XMLHttpRequest synchronously. * @param {string} url URL to load synchronously * @returns {string} Response text */ function getUrlSync(url) { console.debug('Loading URL synchronously', url); const request = new XMLHttpRequest(); request.open('GET', url, false); // `false` makes the request synchronous request.send(); if (request.status >= 200 && request.status < 300) { return request.responseText; } throw new Error(`Error loading ${url}: ${request.status} ${request.statusText}`); } /** * Loads a URL content using XMLHttpRequest asynchronously. * @param {string} url URL to load asynchronously * @returns {Promise} Response text */ function getUrlAsync(url) { return new Promise((resolve, reject) => { const request = new XMLHttpRequest(); request.open('GET', url, true); request.onload = () => { if (request.status >= 200 && request.status < 300) { resolve(request.responseText); } else { reject(new Error(`Error loading ${url}: ${request.status} ${request.statusText}`)); } }; request.onerror = () => { reject(new Error(`Error loading ${url}: ${request.status} ${request.statusText}`)); }; request.send(); }); } /** * Renders a Handlebars template asynchronously. * @param {string} templateId ID of the template to render * @param {Record} templateData The data to pass to the template * @param {boolean} sanitize Should the template be sanitized with DOMPurify * @param {boolean} localize Should the template be localized * @param {boolean} fullPath Should the template ID be treated as a full path or a relative path * @returns {Promise} Rendered template */ export async function renderTemplateAsync(templateId, templateData = {}, sanitize = true, localize = true, fullPath = false) { async function fetchTemplateAsync(pathToTemplate) { let template = TEMPLATE_CACHE.get(pathToTemplate); if (!template) { const templateContent = await getUrlAsync(pathToTemplate); template = Handlebars.compile(templateContent); TEMPLATE_CACHE.set(pathToTemplate, template); } return template; } try { const pathToTemplate = fullPath ? templateId : `/scripts/templates/${templateId}.html`; const template = await fetchTemplateAsync(pathToTemplate); let result = template(templateData); if (sanitize) { result = DOMPurify.sanitize(result); } if (localize) { result = applyLocale(result); } return result; } catch (err) { console.error('Error rendering template', templateId, templateData, err); toastr.error('Check the DevTools console for more information.', 'Error rendering template'); } } /** * Renders a Handlebars template synchronously. * @param {string} templateId ID of the template to render * @param {Record} templateData The data to pass to the template * @param {boolean} sanitize Should the template be sanitized with DOMPurify * @param {boolean} localize Should the template be localized * @param {boolean} fullPath Should the template ID be treated as a full path or a relative path * @returns {string} Rendered template * * @deprecated Use renderTemplateAsync instead. */ export function renderTemplate(templateId, templateData = {}, sanitize = true, localize = true, fullPath = false) { function fetchTemplateSync(pathToTemplate) { let template = TEMPLATE_CACHE.get(pathToTemplate); if (!template) { const templateContent = getUrlSync(pathToTemplate); template = Handlebars.compile(templateContent); TEMPLATE_CACHE.set(pathToTemplate, template); } return template; } try { const pathToTemplate = fullPath ? templateId : `/scripts/templates/${templateId}.html`; const template = fetchTemplateSync(pathToTemplate); let result = template(templateData); if (sanitize) { result = DOMPurify.sanitize(result); } if (localize) { result = applyLocale(result); } return result; } catch (err) { console.error('Error rendering template', templateId, templateData, err); toastr.error('Check the DevTools console for more information.', 'Error rendering template'); } }