import { RsvTemplateRegistry } from './RsvTemplateRegistry.js'; function esc_html(str) { return String(str) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } export class RsvTemplateEngine { constructor(registry = null) { this.registry = registry ?? new RsvTemplateRegistry(); } render(source, data = {}) { const interpolated = this.interpolate(source, data); return this.expand_elements(interpolated, data); } interpolate(source, data) { return source.replace(/{{\s*([^}]+?)\s*}}/g, (match, path) => { const value = this.resolve(path.trim(), data); if (value === null || value === undefined || typeof value === 'object') { return ''; // null and non-scalar (arrays, objects) render empty } return esc_html(String(value)); }); } /** * * @param {*} data Submitted & computed data for the form * @param {*} element The element to build the symbol table for * @returns {Object} The symbol table for the element */ build_symbol_table(data, element) { const attrs = {}; for (const attr of element.attributes) { attrs[attr.name] = attr.value; } return { ...data, ...attrs }; } wrap(element) { const elWrap = document.createElement('div'); elWrap.innerHTML = element; return elWrap; } expand_elements(html, data) { html = html.replace(/<([\w-]+)([^>]*?)\/>/g, '<$1$2>'); let doc = document.createElement('div'); doc.innerHTML = html; [...doc.querySelectorAll('*')] .filter(el => this.registry.has(el.tagName.toLowerCase())) .forEach(el => { const renderer = this.registry.get(el.tagName.toLowerCase()); el.replaceWith(this.wrap(renderer(this.build_symbol_table(data, el)))); }); const wrapper = document.createElement('div'); wrapper.appendChild(doc); return wrapper.innerHTML; } resolve(path, data) { const tokens = this.tokens(path); let current = data; for (const token of tokens) { if (typeof current !== 'object' || current === null || !(token in current)) { return null; } current = current[token]; } return current; } tokens(path) { const raw = path.split(/[\.\[\]]+/).filter(Boolean); const result = []; for (const token of raw) { if (token === '$') continue; result.push(token.replace(/^['"]|['"]$/g, '')); } return result; } }