Skip to content

Instantly share code, notes, and snippets.

@scravy
Created July 29, 2021 16:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save scravy/dfe8a73a372d6821476eee1621cc5acb to your computer and use it in GitHub Desktop.
Save scravy/dfe8a73a372d6821476eee1621cc5acb to your computer and use it in GitHub Desktop.
class Context {
parent = null;
vars = {};
constructor(parent = null) {
this.parent = parent;
}
get(key) {
if (key in this.vars) {
return this.vars[key];
}
if (this.parent !== null) {
return this.parent.get(key);
}
return null;
}
}
class Renderer {
funcs = {
'upper': (ctx, val) => val.toUpperCase(),
'lower': (ctx, val) => val.toLowerCase(),
};
applyTemplate(value, data, context) {
const results = value.matchAll(/{(([a-zA-Z0-9_-]+)(\|[a-zA-Z0-9_-]+)*)}/g);
const pieces = []
let ix = 0;
for (const result of results) {
pieces.push(result.input.substring(ix, result.index));
ix = result.index + result[0].length;
if (result[2] in data) {
value = data[result[2]];
const ts = result[1].split('|');
for (let i = 1; i < ts.length; i += 1) {
if (ts[i] in this.funcs) {
value = this.funcs[ts[i]](context, value);
}
}
pieces.push(value);
} else {
pieces.push(result[0]);
}
}
pieces.push(value.substring(ix));
return pieces.join('');
}
irender(node, data, context) {
switch (node.nodeType) {
case Node.ELEMENT_NODE:
const localContext = new Context(context);
for (const attr of node.attributes) {
if (/^data-var-/.test(attr.name)) {
const key = attr.name.substring(9);
localContext.vars[key] = attr.value;
}
}
for (const child of node.childNodes) {
this.irender(child, data, localContext);
}
break;
case Node.TEXT_NODE:
node.nodeValue = this.applyTemplate(node.nodeValue, data, context);
break;
default:
break;
}
}
render(template_node, data) {
const parentNode = template_node.parentNode;
const newNode = template_node.cloneNode(true);
for (const node of parentNode.querySelectorAll(".to-be-removed")) {
parentNode.removeChild(node);
}
newNode.classList.add('to-be-removed');
newNode.classList.remove('template');
const context = new Context();
this.irender(newNode, data, context);
parentNode.insertBefore(newNode, template_node);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment