Created
January 22, 2020 10:09
-
-
Save IanSSenne/34c27d2259e570f260dfbbe1109f2dbf to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(() => { | |
const addStyleSnippet = (() => { | |
const styles = document.createElement("style"); | |
document.head.appendChild(styles); | |
return function(snippet) { | |
styles.innerHTML += snippet; | |
}; | |
})(); | |
const UTILSTORE = { templates: {}, toRender: [] }; | |
class NodeValuePointer$attribute { | |
constructor(attribute) { | |
this.attribute = attribute; | |
this.default = this.attribute.nodeValue; | |
} | |
update(store) { | |
let nextContent = this.default; | |
for (let key in store) { | |
nextContent = nextContent.replace( | |
new RegExp('\\[\\["' + key + '"\\]\\]', "g"), | |
store[key] === undefined | |
? "MISSING VALUE FOR KEY " + store[key] | |
: store[key] | |
); | |
} | |
this.attribute.nodeValue = nextContent; | |
} | |
} | |
class NodeValuePointer$text { | |
constructor(textNode) { | |
this.textNode = textNode; | |
this.default = this.textNode.textContent; | |
} | |
update(store) { | |
let nextContent = this.default; | |
for (let key in store) { | |
nextContent = nextContent.replace( | |
new RegExp('\\[\\["' + key + '"\\]\\]', "g"), | |
store[key] === undefined | |
? "MISSING VALUE FOR KEY " + store[key] | |
: store[key] | |
); | |
} | |
this.textNode.textContent = nextContent; | |
} | |
} | |
class NodeTemplate { | |
constructor(name, node) { | |
UTILSTORE.templates[name] = this; | |
this.name = name; | |
this.node = node; | |
this.children = []; | |
this.knownData = []; | |
for (let child of this.node.childNodes) { | |
if ( | |
(this.node instanceof Text && this.node.textContent.trim() != "") || | |
this.node instanceof Element | |
) | |
this.children.push(new NodeTemplate(null, child)); | |
} | |
if (this.node instanceof Text) { | |
if (this.node.textContent.match(/\[\[".+"\]\]/)) | |
this.knownData.push(new NodeValuePointer$text(this.node)); | |
} else { | |
for (let attribute of this.node.attributes) { | |
if (attribute.nodeValue.match(/\[\[".+"\]\]/)) | |
this.knownData.push(new NodeValuePointer$attribute(attribute)); | |
} | |
} | |
} | |
generate(data) { | |
for (let pointer of this.knownData) { | |
pointer.update(data); | |
} | |
for (let child of this.children) { | |
child.generate(data); | |
} | |
if (this.node.nodeName === "SLOT") { | |
this.node.innerHTML = ""; | |
for (let i = 0; i < data.children.length; i++) | |
this.node.appendChild(data.children[i].cloneNode(true)); | |
} | |
if (this.name) { | |
let res = this.node.cloneNode(true); | |
res.attributes.setNamedItem(document.createAttribute("data-visible")); | |
res.attributes["data-type"].nodeValue = "rendered-template"; | |
res.attributes.removeNamedItem("data-template"); | |
return res; | |
} | |
} | |
} | |
class NodeTree { | |
constructor(node) { | |
this.node = node; | |
this.children = []; | |
for (let child of this.node.childNodes) { | |
this.children.push(new NodeTree(child)); | |
} | |
} | |
generateTemplate(name) { | |
return new NodeTemplate(name, this.node); | |
} | |
} | |
function getNodeTree(node) { | |
return new NodeTree(node); | |
} | |
function isUtilElementCheck(Element) { | |
if (Element.nodeName != "UTIL") { | |
console.warn(Element); | |
throw new Error("unable to call function on non util element"); | |
} | |
} | |
function validateUtilFunction(Element, type) { | |
if (Element.getAttribute("data-type") != type) { | |
console.warn(Element); | |
throw new Error("unable to call function with incorrect data-type"); | |
} | |
} | |
HTMLUnknownElement.prototype.registerTemplate = function() { | |
isUtilElementCheck(this); | |
validateUtilFunction(this, "template"); | |
const name = this.getAttribute("data-template"); | |
if (UTILSTORE.templates[name]) { | |
console.warn( | |
'failed to register template "' + | |
name + | |
'", do you have multiple declarations for it.' | |
); | |
return UTILSTORE.templates[name]; | |
} else { | |
// document.head.appendChild(this); | |
this.remove(); | |
console.log('registered template "' + name + '" from element', this); | |
return getNodeTree(this).generateTemplate(name); | |
} | |
}; | |
HTMLUnknownElement.prototype.render = function(data) { | |
isUtilElementCheck(this); | |
validateUtilFunction(this, "insert"); | |
const name = this.getAttribute("data-target"); | |
if (data === undefined) { | |
data = JSON.parse(this.getAttribute("data-input")); | |
} | |
data.children = this.children; | |
const rendered = UTILSTORE.templates[name].generate(data); | |
this.replaceWith(rendered); | |
return rendered; | |
}; | |
addStyleSnippet("util:not([data-visible]){display:none;}"); | |
function renderTemplates() { | |
if (undefined === UTILSTORE.toRender[0]) return; | |
while (UTILSTORE.toRender[0]) { | |
const el = UTILSTORE.toRender.shift(); | |
el.render(); | |
} | |
PUtil.renderAllTemplates(); | |
} | |
Object.defineProperty(globalThis, "PUtil", { | |
value: { | |
["registerTemplates"]: function() { | |
Array.from( | |
document.querySelectorAll('util[data-type="template"]') | |
).forEach(element => element.registerTemplate()); | |
}, | |
["renderTemplate"]: function(name, data) { | |
const template = UTILSTORE.templates[name]; | |
return template.generate(data); | |
}, | |
["renderAllTemplates"]: function() { | |
UTILSTORE.toRender.push.call( | |
UTILSTORE.toRender, | |
Array.from( | |
document.querySelectorAll('util[data-type="insert"]') | |
).filter(el => !UTILSTORE.toRender.includes(el)) | |
); | |
UTILSTORE.toRender = UTILSTORE.toRender.flat(); | |
renderTemplates(); | |
}, | |
["createElement"]: function(str) { | |
return new DOMParser().parseFromString(str, "text/html").body | |
.children[0]; | |
} | |
}, | |
writable: false | |
}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment