#26 - Loading animation + success message fix
This commit was merged in pull request #31.
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
import { RsvTemplateRegistry } from './RsvTemplateRegistry.js';
|
||||
|
||||
function esc_html(str) {
|
||||
return String(str)
|
||||
.replace(/&/g, '&')
|
||||
.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></$1>');
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user