kitchensink
Version:
Dispatch's awesome components and style guide
131 lines (103 loc) • 3.36 kB
JavaScript
module.exports = serializeNode
var voidElements = /area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr/i;
function serializeNode(node) {
switch (node.nodeType) {
case 3:
return escapeText(node.data)
case 8:
return "<!--" + node.data + "-->"
default:
return serializeElement(node)
}
}
function serializeElement(elem) {
var strings = []
var tagname = elem.tagName
if (elem.namespaceURI === "http://www.w3.org/1999/xhtml") {
tagname = tagname.toLowerCase()
}
strings.push("<" + tagname + properties(elem) + datasetify(elem))
if (voidElements.test(tagname)) {
strings.push(" />")
} else {
strings.push(">")
if (elem.childNodes.length) {
strings.push.apply(strings, elem.childNodes.map(serializeNode))
} else if (elem.textContent || elem.innerText) {
strings.push(escapeText(elem.textContent || elem.innerText))
} else if (elem.innerHTML) {
strings.push(elem.innerHTML)
}
strings.push("</" + tagname + ">")
}
return strings.join("")
}
function isProperty(elem, key) {
var type = typeof elem[key]
if (key === "style" && Object.keys(elem.style).length > 0) {
return true
}
return elem.hasOwnProperty(key) &&
(type === "string" || type === "boolean" || type === "number") &&
key !== "nodeName" && key !== "className" && key !== "tagName" &&
key !== "textContent" && key !== "innerText" && key !== "namespaceURI" && key !== "innerHTML"
}
function stylify(styles) {
var attr = ""
Object.keys(styles).forEach(function (key) {
var value = styles[key]
key = key.replace(/[A-Z]/g, function(c) {
return "-" + c.toLowerCase();
})
attr += key + ":" + value + ";"
})
return attr
}
function datasetify(elem) {
var ds = elem.dataset
var props = []
for (var key in ds) {
props.push({ name: "data-" + key, value: ds[key] })
}
return props.length ? stringify(props) : ""
}
function stringify(list) {
var attributes = []
list.forEach(function (tuple) {
var name = tuple.name
var value = tuple.value
if (name === "style") {
value = stylify(value)
}
attributes.push(name + "=" + "\"" + escapeAttributeValue(value) + "\"")
})
return attributes.length ? " " + attributes.join(" ") : ""
}
function properties(elem) {
var props = []
for (var key in elem) {
if (isProperty(elem, key)) {
props.push({ name: key, value: elem[key] })
}
}
for (var ns in elem._attributes) {
for (var attribute in elem._attributes[ns]) {
var prop = elem._attributes[ns][attribute]
var name = (prop.prefix ? prop.prefix + ":" : "") + attribute
props.push({ name: name, value: prop.value })
}
}
if (elem.className) {
props.push({ name: "class", value: elem.className })
}
return props.length ? stringify(props) : ""
}
function escapeText(str) {
return str
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
}
function escapeAttributeValue(str) {
return escapeText(str).replace(/"/g, """)
}