Blame Raw
· · · 131 lines (4.6 KB)
0 contributors
1import { DOMPurify, Handlebars } from '../lib.js';
2import { applyLocale } from './i18n.js';
3
4/**
5 * @type {Map<string, function>}
6 * @description Cache for Handlebars templates.
7 */
8const TEMPLATE_CACHE = new Map();
9
10/**
11 * Loads a URL content using XMLHttpRequest synchronously.
12 * @param {string} url URL to load synchronously
13 * @returns {string} Response text
14 */
15function getUrlSync(url) {
16 console.debug('Loading URL synchronously', url);
17 const request = new XMLHttpRequest();
18 request.open('GET', url, false); // `false` makes the request synchronous
19 request.send();
20
21 if (request.status >= 200 && request.status < 300) {
22 return request.responseText;
23 }
24
25 throw new Error(`Error loading ${url}: ${request.status} ${request.statusText}`);
26}
27
28/**
29 * Loads a URL content using XMLHttpRequest asynchronously.
30 * @param {string} url URL to load asynchronously
31 * @returns {Promise<string>} Response text
32 */
33function getUrlAsync(url) {
34 return new Promise((resolve, reject) => {
35 const request = new XMLHttpRequest();
36 request.open('GET', url, true);
37 request.onload = () => {
38 if (request.status >= 200 && request.status < 300) {
39 resolve(request.responseText);
40 } else {
41 reject(new Error(`Error loading ${url}: ${request.status} ${request.statusText}`));
42 }
43 };
44 request.onerror = () => {
45 reject(new Error(`Error loading ${url}: ${request.status} ${request.statusText}`));
46 };
47 request.send();
48 });
49}
50
51/**
52 * Renders a Handlebars template asynchronously.
53 * @param {string} templateId ID of the template to render
54 * @param {Record<string, any>} templateData The data to pass to the template
55 * @param {boolean} sanitize Should the template be sanitized with DOMPurify
56 * @param {boolean} localize Should the template be localized
57 * @param {boolean} fullPath Should the template ID be treated as a full path or a relative path
58 * @returns {Promise<string>} Rendered template
59 */
60export async function renderTemplateAsync(templateId, templateData = {}, sanitize = true, localize = true, fullPath = false) {
61 async function fetchTemplateAsync(pathToTemplate) {
62 let template = TEMPLATE_CACHE.get(pathToTemplate);
63 if (!template) {
64 const templateContent = await getUrlAsync(pathToTemplate);
65 template = Handlebars.compile(templateContent);
66 TEMPLATE_CACHE.set(pathToTemplate, template);
67 }
68 return template;
69 }
70
71 try {
72 const pathToTemplate = fullPath ? templateId : `/scripts/templates/${templateId}.html`;
73 const template = await fetchTemplateAsync(pathToTemplate);
74 let result = template(templateData);
75
76 if (sanitize) {
77 result = DOMPurify.sanitize(result);
78 }
79
80 if (localize) {
81 result = applyLocale(result);
82 }
83
84 return result;
85 } catch (err) {
86 console.error('Error rendering template', templateId, templateData, err);
87 toastr.error('Check the DevTools console for more information.', 'Error rendering template');
88 }
89}
90
91/**
92 * Renders a Handlebars template synchronously.
93 * @param {string} templateId ID of the template to render
94 * @param {Record<string, any>} templateData The data to pass to the template
95 * @param {boolean} sanitize Should the template be sanitized with DOMPurify
96 * @param {boolean} localize Should the template be localized
97 * @param {boolean} fullPath Should the template ID be treated as a full path or a relative path
98 * @returns {string} Rendered template
99 *
100 * @deprecated Use renderTemplateAsync instead.
101 */
102export function renderTemplate(templateId, templateData = {}, sanitize = true, localize = true, fullPath = false) {
103 function fetchTemplateSync(pathToTemplate) {
104 let template = TEMPLATE_CACHE.get(pathToTemplate);
105 if (!template) {
106 const templateContent = getUrlSync(pathToTemplate);
107 template = Handlebars.compile(templateContent);
108 TEMPLATE_CACHE.set(pathToTemplate, template);
109 }
110 return template;
111 }
112
113 try {
114 const pathToTemplate = fullPath ? templateId : `/scripts/templates/${templateId}.html`;
115 const template = fetchTemplateSync(pathToTemplate);
116 let result = template(templateData);
117
118 if (sanitize) {
119 result = DOMPurify.sanitize(result);
120 }
121
122 if (localize) {
123 result = applyLocale(result);
124 }
125
126 return result;
127 } catch (err) {
128 console.error('Error rendering template', templateId, templateData, err);
129 toastr.error('Check the DevTools console for more information.', 'Error rendering template');
130 }
131}