|
module.exports = serializeNode |
|
|
|
var voidElements = ["area","base","br","col","embed","hr","img","input","keygen","link","menuitem","meta","param","source","track","wbr"]; |
|
|
|
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.indexOf(tagname) > -1) { |
|
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) { |
|
if (typeof styles === 'string') return 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(s) { |
|
var str = ''; |
|
|
|
if (typeof(s) === 'string') { |
|
str = s; |
|
} else if (s) { |
|
str = s.toString(); |
|
} |
|
|
|
return str |
|
.replace(/&/g, "&") |
|
.replace(/</g, "<") |
|
.replace(/>/g, ">") |
|
} |
|
|
|
function escapeAttributeValue(str) { |
|
return escapeText(str).replace(/"/g, """) |
|
} |
|
|