NEWS: Welcome to my new homepage! <3

Initial commit - figure - Unnamed repository; edit this file 'description' to name the repository.

figure

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit fe2376423c14f0e563699d40777fb09143bcdb1f
Author: typable <contact@typable.dev>
Date:   Thu, 15 Sep 2022 16:27:06 +0200

Initial commit

Diffstat:
Alib.js | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 97 insertions(+), 0 deletions(-)

diff --git a/lib.js b/lib.js @@ -0,0 +1,97 @@ +const parser = new DOMParser(); +let counter = 1000; + +function tlx(parts, ...props) { + const [html, refs] = ltr(parts, props); + const dom = parser.parseFromString(html, 'text/html'); + if(dom.body.childNodes.length !== 1) { + throw 'invalid DOM structure!'; + } + const node = dom.body.childNodes[0]; + const elements = render(node, refs); + if(elements.length !== 1) { + throw 'invalid VDOM structure!'; + } + return elements[0]; +} + +function render(node, refs) { + if(node.nodeType === Node.TEXT_NODE) { + return apply(node.textContent, refs); + } + const tag = node.tagName.toLowerCase(); + const attributes = {}; + for(const attribute of node.attributes) { + const key = attribute.name; + const value = attribute.textContent; + let isDynamic = false; + for(const ref in refs) { + if(value === ref) { + if((match = /^@(\w+)$/.exec(key)) !== null) { + const event = match[1]; + attributes[`on${event.substr(0, 1).toUpperCase()}${event.substr(1)}`] = refs[ref]; + } + else { + attributes[key] = refs[ref]; + } + isDynamic = true; + break; + } + } + if(!isDynamic) { + if(key === 'style') { + const styles = {}; + for(const item of value.split(';')) { + if(item.trim().length === 0) { + break; + } + let [key, value] = item.split(':'); + key = key.trim(); + if((match = /-(\w)/.exec(key)) !== null) { + const char = match[1]; + key = key.replace(`-${char}`, char.toUpperCase()); + } + value = value.trim(); + styles[key] = apply(value, refs).join(''); + } + attributes[key] = styles; + } + else { + attributes[key] = apply(value, refs).join(''); + } + } + } + const children = []; + (node.childNodes ?? []).forEach((child) => children.push(...render(child, refs))); + return [React.createElement(tag, attributes, ...children)]; +} + +function apply(value, refs) { + let values = []; + const expr = /\$tlx-\d+/g; + let match = null; + let last = 0; + while((match = expr.exec(value)) !== null) { + const index = match.index; + values.push(value.substring(last, index)); + values.push(refs[match[0]]); + last = index + match[0].length; + } + values.push(value.substring(last)); + return values; +} + +function ltr(parts, props) { + let string = ''; + const refs = {}; + for(let i = 0; i < parts.length; i++) { + string += parts[i]; + if(props[i] !== undefined) { + const id = `$tlx-${counter}`; + refs[id] = props[i]; + string += id ?? ''; + counter++; + } + } + return [string.trim(), refs]; +}