UNPKG

@eeue56/coed

Version:
1,176 lines (1,175 loc) 37.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.class_ = class_; exports.style_ = style_; exports.none = none; exports.attribute = attribute; exports.booleanAttribute = booleanAttribute; exports.on = on; exports.onInput = onInput; exports.text = text; exports.node = node; exports.voidNode = voidNode; exports.nodeNS = nodeNS; exports.voidNodeNS = voidNodeNS; exports.render = render; exports.flatRender = flatRender; exports.fromString = fromString; exports.hydrate = hydrate; exports.hydrateNode = hydrateNode; exports.buildTree = buildTree; exports.triggerEvent = triggerEvent; exports.map = map; exports.program = program; exports.a = a; exports.abbr = abbr; exports.address = address; exports.area = area; exports.article = article; exports.aside = aside; exports.audio = audio; exports.b = b; exports.base = base; exports.bdi = bdi; exports.bdo = bdo; exports.blockquote = blockquote; exports.body = body; exports.br = br; exports.button = button; exports.canvas = canvas; exports.caption = caption; exports.cite = cite; exports.code = code; exports.col = col; exports.colgroup = colgroup; exports.data = data; exports.datalist = datalist; exports.dd = dd; exports.del = del; exports.details = details; exports.dfn = dfn; exports.dialog = dialog; exports.div = div; exports.dl = dl; exports.dt = dt; exports.em = em; exports.embed = embed; exports.fieldset = fieldset; exports.figure = figure; exports.footer = footer; exports.form = form; exports.h1 = h1; exports.h2 = h2; exports.h3 = h3; exports.h4 = h4; exports.h5 = h5; exports.h6 = h6; exports.head = head; exports.header = header; exports.hgroup = hgroup; exports.hr = hr; exports.html = html; exports.i = i; exports.iframe = iframe; exports.img = img; exports.input = input; exports.ins = ins; exports.kbd = kbd; exports.keygen = keygen; exports.label = label; exports.legend = legend; exports.li = li; exports.link = link; exports.main = main; exports.map_ = map_; exports.mark = mark; exports.menu = menu; exports.menuitem = menuitem; exports.meta = meta; exports.meter = meter; exports.nav = nav; exports.noscript = noscript; exports.object = object; exports.ol = ol; exports.optgroup = optgroup; exports.option = option; exports.output = output; exports.p = p; exports.param = param; exports.pre = pre; exports.progress = progress; exports.q = q; exports.rb = rb; exports.rp = rp; exports.rt = rt; exports.rtc = rtc; exports.ruby = ruby; exports.s = s; exports.samp = samp; exports.script = script; exports.section = section; exports.select = select; exports.small = small; exports.source = source; exports.span = span; exports.strong = strong; exports.style = style; exports.sub = sub; exports.summary = summary; exports.sup = sup; exports.table = table; exports.tbody = tbody; exports.td = td; exports.template = template; exports.textarea = textarea; exports.tfoot = tfoot; exports.th = th; exports.thead = thead; exports.time = time; exports.title = title; exports.tr = tr; exports.track = track; exports.u = u; exports.ul = ul; exports.var_ = var_; exports.video = video; exports.wbr = wbr; const ts_core_1 = require("@eeue56/ts-core"); /** Creates a class attribute - classes are combined by the html creator, so you can use it like: ``` html.div([ ], [ class_("one"), class_("two") ], [ ]) ``` */ function class_(str) { return { kind: "string", key: "class", value: str, }; } /** Creates a style attribute - styles are combined by the html creator, so you can use it like: ``` html.div([ ], [ style_("color", "red"), style_("background-color", "blue") ], [ ]) ``` */ function style_(key, value) { return { kind: "style", key: key, value: value, }; } /** An empty attribute - filtered by the html creator on creation. This is useful if you have a tenary operator, e.g: ``` html.div([ ], [ somethingTruthy ? none() : class_("something") ], [ ]) ``` */ function none() { return { kind: "none", }; } /** Create an attribute with a given key and value. This is set via `setAttribute` at runtime. */ function attribute(key, value) { if (key === "style") return style_(value.split(":")[0], value.split(":")[1]); return { kind: "string", key: key, value: value, }; } /** Create an attribute with a given key and value. This is set via `setAttribute` at runtime. */ function booleanAttribute(key, value) { return { kind: "boolean", key: key, value: value, }; } /** Creates an event handler for passing to a html node */ function on(name, tagger, stopPropagation = true, preventDefault = true) { return { name: name, tagger: (event) => { if (stopPropagation) { event.stopPropagation(); } if (preventDefault) { event.preventDefault(); } return tagger(event); }, }; } /** Special-cased input handler */ function onInput(tagger) { return { name: "input", tagger: (event) => { event.stopPropagation(); event.preventDefault(); if (event.target && "value" in event.target) { return tagger(event.target.value); } return tagger(""); }, }; } /** Creates a text node */ function text(str) { return { kind: "text", text: str, }; } /** Creates a html node with a given tag name, any events, any attributes and any children. */ function node(tag, events, attributes, children) { return { kind: "regular", tag: tag, events: events, attributes: combineAttributes(attributes), children: children, _eventListeners: [], }; } /** Creates a void html node with a given tag name, any events, any attributes. */ function voidNode(tag, events, attributes) { return { kind: "void", tag: tag, events: events, attributes: combineAttributes(attributes), _eventListeners: [], }; } /** Creates a void html node with a given tag name, any events, any attributes. */ function nodeNS(tag, namespace, events, attributes, children) { const namespaceXmlns = { kind: "string", key: "xmlns", value: namespace, }; attributes.push(namespaceXmlns); return { kind: "ns-regular", tag: tag, namespace, events: events, attributes: combineAttributes(attributes), children: children, _eventListeners: [], }; } /** Creates a void html node with a given tag name, any events, any attributes. */ function voidNodeNS(tag, namespace, events, attributes) { const namespaceXmlns = { kind: "string", key: "xmlns", value: namespace, }; attributes.push(namespaceXmlns); return { kind: "ns-void", tag: tag, namespace, events: events, attributes: combineAttributes(attributes), _eventListeners: [], }; } function combineAttributes(attributes) { const knownStringAttributes = {}; const knownStyleAttributes = []; const otherAttributes = []; // group attribute values attributes.forEach((attribute) => { switch (attribute.kind) { case "string": if (!knownStringAttributes[attribute.key]) { knownStringAttributes[attribute.key] = []; } knownStringAttributes[attribute.key].push(attribute); break; case "style": knownStyleAttributes.push(attribute); break; default: otherAttributes.push(attribute); } }); const combinedAttributes = otherAttributes.filter((attribute) => attribute.kind !== "none"); // actually combine attributes together Object.keys(knownStringAttributes).map((key) => { combinedAttributes.push(knownStringAttributes[key].reduce((acc, currentValue) => { if (key === "class") { acc.value += " " + currentValue.value; } return acc; })); }); if (knownStyleAttributes.length > 0) { // actually combine attributes together combinedAttributes.push(knownStyleAttributes.reduce((acc, currentValue) => { if (typeof acc.value === "undefined") acc.value = ""; acc.value += currentValue.key + ":" + currentValue.value + ";"; return acc; }, attribute("style", ""))); } return combinedAttributes; } function renderAttribute(attribute) { switch (attribute.kind) { case "string": if (attribute.value.indexOf('"') > 0) { return `${attribute.key}='${attribute.value}'`; } return `${attribute.key}="${attribute.value}"`; case "number": return `${attribute.key}=${attribute.value}`; case "style": return ""; case "boolean": return attribute.value ? `${attribute.key}="${attribute.key}"` : ""; case "none": return ""; } } /** * Renders a HtmlNode tree as a string. */ function render(node, depth = 0) { const whitespace = " ".repeat(depth * 4); switch (node.kind) { case "text": return whitespace + node.text; case "void": case "regular": case "ns-void": case "ns-regular": { const renderedAttributes = node.attributes .map(renderAttribute) .join(" "); const attributes = (renderedAttributes.length > 0 ? " " : "") + renderedAttributes; switch (node.kind) { case "void": case "ns-void": return whitespace + `<${node.tag}${attributes}>`; case "regular": case "ns-regular": { if (node.children.length > 0) { return (whitespace + `<${node.tag}${attributes}> ${node.children.map((child) => render(child, depth + 1)).join("\n")} ${whitespace}</${node.tag}>`); } return (whitespace + `<${node.tag}${attributes}></${node.tag}>`); } } } case "html-string": { return node.content; } } } /** * Render a node without whitespace */ function flatRender(node) { switch (node.kind) { case "text": return node.text; case "void": case "regular": case "ns-void": case "ns-regular": { const attributes = (node.attributes.length > 0 ? " " : "") + node.attributes.map(renderAttribute).join(" "); switch (node.kind) { case "void": case "ns-void": return `<${node.tag}${attributes}>`; case "regular": case "ns-regular": { if (node.children.length > 0) { return `<${node.tag}${attributes}>${node.children .map((child) => flatRender(child)) .join("")}</${node.tag}>`; } return `<${node.tag}${attributes}></${node.tag}>`; } } } case "html-string": { return node.content; } } } /** * Create a HtmlStringNode from a html string - and create a DOM element using it * @param string a string of html */ function fromString(string) { return { kind: "html-string", content: string }; } /** Hydrates a root from a given program. Program must have root set as the string "hydration" **/ function hydrate(program, root) { program.program.root = root; const node = program.program.view(program.program.initialModel); if (node.kind === "text") return; if (root.children.length === 0) { console.error("This root has no children. Did you correctly server-side render content?"); console.error(`Your html should look like <div id="root">{your content}</div>`); console.error("The root node should have exactly one child, which is your generated html."); } hydrateNode(node, program.send, root.children[0]); } /** Attaches event listeners to nodes */ function hydrateNode(node, listener, root) { switch (node.kind) { case "text": { return; } case "void": case "regular": { node.events.forEach((event) => { const listenerFunction = (data) => { listener(event.tagger(data)); }; root.addEventListener(event.name, listenerFunction, { once: true, }); node._eventListeners.push({ event: event, listener: listenerFunction, }); }); } } if (node.kind === "regular") { let i = 0; for (const child of node.children) { if (child.kind === "text") continue; const newRoot = root.children[i]; hydrateNode(child, listener, newRoot); i++; } } } /** * Builds a HTMLElement tree from a HtmlNode tree, with event triggers being sent to the runner via the listener * This function should not be needed by most usage. */ function buildTree(listener, node) { switch (node.kind) { case "text": return document.createTextNode(node.text); case "void": case "regular": case "ns-void": case "ns-regular": { let element; if (node.kind === "ns-regular" || node.kind === "ns-void") { element = document.createElementNS(node.namespace, node.tag); } else { element = document.createElement(node.tag); } node.attributes.forEach((attribute) => { setAttributeOnElement(element, attribute); }); node.events.forEach((event) => { const listenerFunction = (data) => { listener(event.tagger(data)); }; element.addEventListener(event.name, listenerFunction, { once: true, }); node._eventListeners.push({ event: event, listener: listenerFunction, }); }); if (node.kind === "regular" || node.kind === "ns-regular") { const children = node.children.map((child) => buildTree(listener, child)); children.forEach((child) => { element.appendChild(child); }); } return element; } case "html-string": { const parser = new DOMParser(); const parsed = parser.parseFromString(node.content, "text/html"); if (!parsed.body.firstElementChild) return document.createTextNode("Failed to parse"); return parsed.body.firstElementChild; } } } /** * Triggers the event by name, passing it the payload provided. * This function is useful for testing but not much else */ function triggerEvent(eventName, payload, node) { payload = Object.assign({ stopPropagation: () => undefined, preventDefault: () => undefined }, payload); switch (node.kind) { case "text": { return ts_core_1.Maybe.Nothing(); } case "void": case "regular": case "ns-void": case "ns-regular": { const events = node.events.filter((event) => event.name === eventName); if (events.length > 0) { return ts_core_1.Maybe.Just(events[0].tagger(payload)); } else { return ts_core_1.Maybe.Nothing(); } } case "html-string": { return ts_core_1.Maybe.Nothing(); } } } /** Converts a `HtmlNode` of type `A` to a `HtmlNode` of type `B`, including children. */ function map(tagger, tree) { switch (tree.kind) { case "text": { return tree; } case "void": case "ns-void": { return voidNode(tree.tag, tree.events.map((event) => { return on(event.name, (data) => tagger(event.tagger(data))); }), tree.attributes); } case "regular": case "ns-regular": { return node(tree.tag, tree.events.map((event) => { return on(event.name, (data) => tagger(event.tagger(data))); }), tree.attributes, tree.children.map((child) => { return map(tagger, child); })); } case "html-string": { return tree; } } } function isProperty(tag, key) { switch (tag) { case "INPUT": return (key === "checked" || key === "indeterminate" || key === "value" || key === "readonly" || key === "disabled"); case "OPTION": return key === "selected" || key === "disabled"; case "TEXTAREA": return key === "value" || key === "readonly" || key === "disabled"; case "SELECT": return key === "value" || key === "disabled"; case "BUTTON": case "OPTGROUP": return key === "disabled"; } return false; } function setAttributeOnElement(element, attribute) { switch (attribute.kind) { case "string": case "number": { if (isProperty(element.tagName, attribute.key)) { element[attribute.key] = attribute.value; return true; } else { element.setAttribute(attribute.key, attribute.value); return true; } } case "style": { element.removeAttribute("style"); const styles = attribute.value.split(";"); for (var i = 0; i < styles.length; i++) { const styleName = styles[i].split(":")[0]; const styleValue = styles[i].split(":")[1]; element.style[styleName] = styleValue; } return true; } case "boolean": { if (attribute.value) { if (isProperty(element.tagName, attribute.key)) { element[attribute.key] = attribute.value; return true; } element.setAttribute(attribute.key, attribute.key); } else { if (element.getAttribute(attribute.key) === attribute.key) { element.removeAttribute(attribute.key); } } return true; } case "none": return true; } } function patchFacts(previousTree, nextTree, elements) { switch (nextTree.kind) { case "void": case "regular": case "ns-void": case "ns-regular": { // remove previous attributes that no longer exist on the next dom version if (previousTree.kind === nextTree.kind) { const nextAttributes = []; for (const attr of nextTree.attributes) { if (attr.kind != "none") { nextAttributes.push(attr.key); } } for (const attribute of previousTree.attributes) { if (attribute.kind !== "none" && nextAttributes.indexOf(attribute.key) === -1) { elements.removeAttribute(attribute.key); } } } nextTree.attributes.forEach((attribute) => { setAttributeOnElement(elements, attribute); }); return true; } case "text": { return true; } case "html-string": { return true; } } } function patchEvents(listener, previousTree, nextTree, elements) { switch (nextTree.kind) { case "void": case "regular": case "ns-void": case "ns-regular": previousTree._eventListeners.forEach((eventListeners) => { elements.removeEventListener(eventListeners.event.name, eventListeners.listener); }); nextTree.events.forEach((event) => { const listenerFunction = (data) => { listener(event.tagger(data)); }; elements.addEventListener(event.name, listenerFunction, { once: true, }); nextTree._eventListeners.push({ event: event, listener: listenerFunction, }); }); return true; case "text": case "html-string": return true; } } function patch(listener, currentTree, nextTree, elements) { var _a, _b; if (currentTree.kind != nextTree.kind) { elements.replaceWith(buildTree(listener, nextTree)); return nextTree; } switch (currentTree.kind) { case "text": { nextTree = nextTree; elements = elements; if (currentTree.text == nextTree.text) { return currentTree; } else { elements.replaceWith(document.createTextNode(nextTree.text)); return nextTree; } } case "void": case "ns-void": { currentTree = currentTree; nextTree = nextTree; if (currentTree.tag != nextTree.tag) { elements.replaceWith(buildTree(listener, nextTree)); return nextTree; } else { patchFacts(currentTree, nextTree, elements); patchEvents(listener, currentTree, nextTree, elements); } return nextTree; } case "regular": case "ns-regular": { currentTree = currentTree; nextTree = nextTree; const currentTreeId = (_a = currentTree.attributes.filter((x) => x.kind === "string" && x.key === "id")[0]) === null || _a === void 0 ? void 0 : _a.value; const nextTreeId = (_b = nextTree.attributes.filter((x) => x.kind === "string" && x.key === "id")[0]) === null || _b === void 0 ? void 0 : _b.value; if (currentTree.tag !== nextTree.tag || currentTreeId !== nextTreeId) { elements.replaceWith(buildTree(listener, nextTree)); return nextTree; } else { patchFacts(currentTree, nextTree, elements); patchEvents(listener, currentTree, nextTree, elements); const htmlElements = elements; for (var i = 0; i < nextTree.children.length; i++) { const currentChild = currentTree.children[i]; const nextChild = nextTree.children[i]; const node = htmlElements.childNodes[i]; if (typeof node === "undefined") { htmlElements.appendChild(buildTree(listener, nextChild)); continue; } switch (node.nodeType) { case Node.ELEMENT_NODE: const element = node; patch(listener, currentChild, nextChild, element); break; case Node.TEXT_NODE: const text = node; patch(listener, currentChild, nextChild, text); break; } } for (var i = htmlElements.childNodes.length - 1; i > nextTree.children.length - 1; i--) { const node = htmlElements.childNodes[i]; htmlElements.removeChild(node); } } return nextTree; } case "html-string": { currentTree = currentTree; nextTree = nextTree; if (currentTree.content === nextTree.content) { return currentTree; } elements.replaceWith(buildTree(listener, nextTree)); return nextTree; } } } /** * Takes in a program, sets it up and runs it as a main loop */ function program(program) { let model = program.initialModel; let previousView = program.view(program.initialModel); let currentTree = null; const listener = async (msg) => { if (currentTree === null) { currentTree = buildTree(listener, previousView); if (program.root !== "hydration") { while (program.root.firstChild) { program.root.removeChild(program.root.firstChild); } program.root.appendChild(currentTree); } } model = await program.update(msg, model, listener); const nextView = program.view(model); patch(listener, previousView, nextView, currentTree); previousView = nextView; if (program.postRender) { program.postRender(model); } }; if (program.root !== "hydration") { currentTree = buildTree(listener, previousView); program.root.appendChild(currentTree); } return { program: program, send: listener, }; } // tags function a(events, attributes, children) { return node("a", events, attributes, children); } function abbr(events, attributes, children) { return node("abbr", events, attributes, children); } function address(events, attributes, children) { return node("address", events, attributes, children); } function area(events, attributes) { return voidNode("area", events, attributes); } function article(events, attributes, children) { return node("article", events, attributes, children); } function aside(events, attributes, children) { return node("aside", events, attributes, children); } function audio(events, attributes, children) { return node("audio", events, attributes, children); } function b(events, attributes, children) { return node("b", events, attributes, children); } function base(events, attributes) { return voidNode("base", events, attributes); } function bdi(events, attributes, children) { return node("bdi", events, attributes, children); } function bdo(events, attributes, children) { return node("bdo", events, attributes, children); } function blockquote(events, attributes, children) { return node("blockquote", events, attributes, children); } function body(events, attributes, children) { return node("body", events, attributes, children); } function br(events, attributes) { return voidNode("br", events, attributes); } function button(events, attributes, children) { return node("button", events, attributes, children); } function canvas(events, attributes, children) { return node("canvas", events, attributes, children); } function caption(events, attributes, children) { return node("caption", events, attributes, children); } function cite(events, attributes, children) { return node("cite", events, attributes, children); } function code(events, attributes, children) { return node("code", events, attributes, children); } function col(events, attributes) { return voidNode("col", events, attributes); } function colgroup(events, attributes, children) { return node("colgroup", events, attributes, children); } function data(events, attributes, children) { return node("data", events, attributes, children); } function datalist(events, attributes, children) { return node("datalist", events, attributes, children); } function dd(events, attributes, children) { return node("dd", events, attributes, children); } function del(events, attributes, children) { return node("del", events, attributes, children); } function details(events, attributes, children) { return node("details", events, attributes, children); } function dfn(events, attributes, children) { return node("dfn", events, attributes, children); } function dialog(events, attributes, children) { return node("dialog", events, attributes, children); } function div(events, attributes, children) { return node("div", events, attributes, children); } function dl(events, attributes, children) { return node("dl", events, attributes, children); } function dt(events, attributes, children) { return node("dt", events, attributes, children); } function em(events, attributes, children) { return node("em", events, attributes, children); } function embed(events, attributes) { return voidNode("embed", events, attributes); } function fieldset(events, attributes, children) { return node("fieldset", events, attributes, children); } function figure(events, attributes, children) { return node("figure", events, attributes, children); } function footer(events, attributes, children) { return node("footer", events, attributes, children); } function form(events, attributes, children) { return node("form", events, attributes, children); } function h1(events, attributes, children) { return node("h1", events, attributes, children); } function h2(events, attributes, children) { return node("h2", events, attributes, children); } function h3(events, attributes, children) { return node("h3", events, attributes, children); } function h4(events, attributes, children) { return node("h4", events, attributes, children); } function h5(events, attributes, children) { return node("h5", events, attributes, children); } function h6(events, attributes, children) { return node("h6", events, attributes, children); } function head(events, attributes, children) { return node("head", events, attributes, children); } function header(events, attributes, children) { return node("header", events, attributes, children); } function hgroup(events, attributes, children) { return node("hgroup", events, attributes, children); } function hr(events, attributes) { return voidNode("hr", events, attributes); } function html(events, attributes, children) { return node("html", events, attributes, children); } function i(events, attributes, children) { return node("i", events, attributes, children); } function iframe(events, attributes, children) { return node("iframe", events, attributes, children); } function img(events, attributes) { return voidNode("img", events, attributes); } function input(events, attributes) { return voidNode("input", events, attributes); } function ins(events, attributes, children) { return node("ins", events, attributes, children); } function kbd(events, attributes, children) { return node("kbd", events, attributes, children); } function keygen(events, attributes, children) { return node("keygen", events, attributes, children); } function label(events, attributes, children) { return node("label", events, attributes, children); } function legend(events, attributes, children) { return node("legend", events, attributes, children); } function li(events, attributes, children) { return node("li", events, attributes, children); } function link(events, attributes) { return voidNode("link", events, attributes); } function main(events, attributes, children) { return node("main", events, attributes, children); } function map_(events, attributes, children) { return node("map", events, attributes, children); } function mark(events, attributes, children) { return node("mark", events, attributes, children); } function menu(events, attributes, children) { return node("menu", events, attributes, children); } function menuitem(events, attributes, children) { return node("menuitem", events, attributes, children); } function meta(events, attributes) { return voidNode("meta", events, attributes); } function meter(events, attributes, children) { return node("meter", events, attributes, children); } function nav(events, attributes, children) { return node("nav", events, attributes, children); } function noscript(events, attributes, children) { return node("noscript", events, attributes, children); } function object(events, attributes, children) { return node("object", events, attributes, children); } function ol(events, attributes, children) { return node("ol", events, attributes, children); } function optgroup(events, attributes, children) { return node("optgroup", events, attributes, children); } function option(events, attributes, children) { return node("option", events, attributes, children); } function output(events, attributes, children) { return node("output", events, attributes, children); } function p(events, attributes, children) { return node("p", events, attributes, children); } function param(events, attributes) { return voidNode("param", events, attributes); } function pre(events, attributes, children) { return node("pre", events, attributes, children); } function progress(events, attributes, children) { return node("progress", events, attributes, children); } function q(events, attributes, children) { return node("q", events, attributes, children); } function rb(events, attributes, children) { return node("rb", events, attributes, children); } function rp(events, attributes, children) { return node("rp", events, attributes, children); } function rt(events, attributes, children) { return node("rt", events, attributes, children); } function rtc(events, attributes, children) { return node("rtc", events, attributes, children); } function ruby(events, attributes, children) { return node("ruby", events, attributes, children); } function s(events, attributes, children) { return node("s", events, attributes, children); } function samp(events, attributes, children) { return node("samp", events, attributes, children); } function script(events, attributes, children) { return node("script", events, attributes, children); } function section(events, attributes, children) { return node("section", events, attributes, children); } function select(events, attributes, children) { return node("select", events, attributes, children); } function small(events, attributes, children) { return node("small", events, attributes, children); } function source(events, attributes) { return voidNode("source", events, attributes); } function span(events, attributes, children) { return node("span", events, attributes, children); } function strong(events, attributes, children) { return node("strong", events, attributes, children); } function style(events, attributes, children) { return node("style", events, attributes, children); } function sub(events, attributes, children) { return node("sub", events, attributes, children); } function summary(events, attributes, children) { return node("summary", events, attributes, children); } function sup(events, attributes, children) { return node("sup", events, attributes, children); } function table(events, attributes, children) { return node("table", events, attributes, children); } function tbody(events, attributes, children) { return node("tbody", events, attributes, children); } function td(events, attributes, children) { return node("td", events, attributes, children); } function template(events, attributes, children) { return node("template", events, attributes, children); } function textarea(events, attributes, children) { return node("textarea", events, attributes, children); } function tfoot(events, attributes, children) { return node("tfoot", events, attributes, children); } function th(events, attributes, children) { return node("th", events, attributes, children); } function thead(events, attributes, children) { return node("thead", events, attributes, children); } function time(events, attributes, children) { return node("time", events, attributes, children); } function title(events, attributes, children) { return node("title", events, attributes, children); } function tr(events, attributes, children) { return node("tr", events, attributes, children); } function track(events, attributes) { return voidNode("track", events, attributes); } function u(events, attributes, children) { return node("u", events, attributes, children); } function ul(events, attributes, children) { return node("ul", events, attributes, children); } function var_(events, attributes, children) { return node("var", events, attributes, children); } function video(events, attributes, children) { return node("video", events, attributes, children); } function wbr(events, attributes) { return voidNode("wbr", events, attributes); }