serialize.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. module.exports = serializeNode
  2. var voidElements = ["area","base","br","col","embed","hr","img","input","keygen","link","menuitem","meta","param","source","track","wbr"];
  3. function serializeNode(node) {
  4. switch (node.nodeType) {
  5. case 3:
  6. return escapeText(node.data)
  7. case 8:
  8. return "<!--" + node.data + "-->"
  9. default:
  10. return serializeElement(node)
  11. }
  12. }
  13. function serializeElement(elem) {
  14. var strings = []
  15. var tagname = elem.tagName
  16. if (elem.namespaceURI === "http://www.w3.org/1999/xhtml") {
  17. tagname = tagname.toLowerCase()
  18. }
  19. strings.push("<" + tagname + properties(elem) + datasetify(elem))
  20. if (voidElements.indexOf(tagname) > -1) {
  21. strings.push(" />")
  22. } else {
  23. strings.push(">")
  24. if (elem.childNodes.length) {
  25. strings.push.apply(strings, elem.childNodes.map(serializeNode))
  26. } else if (elem.textContent || elem.innerText) {
  27. strings.push(escapeText(elem.textContent || elem.innerText))
  28. } else if (elem.innerHTML) {
  29. strings.push(elem.innerHTML)
  30. }
  31. strings.push("</" + tagname + ">")
  32. }
  33. return strings.join("")
  34. }
  35. function isProperty(elem, key) {
  36. var type = typeof elem[key]
  37. if (key === "style" && Object.keys(elem.style).length > 0) {
  38. return true
  39. }
  40. return elem.hasOwnProperty(key) &&
  41. (type === "string" || type === "boolean" || type === "number") &&
  42. key !== "nodeName" && key !== "className" && key !== "tagName" &&
  43. key !== "textContent" && key !== "innerText" && key !== "namespaceURI" && key !== "innerHTML"
  44. }
  45. function stylify(styles) {
  46. if (typeof styles === 'string') return styles
  47. var attr = ""
  48. Object.keys(styles).forEach(function (key) {
  49. var value = styles[key]
  50. key = key.replace(/[A-Z]/g, function(c) {
  51. return "-" + c.toLowerCase();
  52. })
  53. attr += key + ":" + value + ";"
  54. })
  55. return attr
  56. }
  57. function datasetify(elem) {
  58. var ds = elem.dataset
  59. var props = []
  60. for (var key in ds) {
  61. props.push({ name: "data-" + key, value: ds[key] })
  62. }
  63. return props.length ? stringify(props) : ""
  64. }
  65. function stringify(list) {
  66. var attributes = []
  67. list.forEach(function (tuple) {
  68. var name = tuple.name
  69. var value = tuple.value
  70. if (name === "style") {
  71. value = stylify(value)
  72. }
  73. attributes.push(name + "=" + "\"" + escapeAttributeValue(value) + "\"")
  74. })
  75. return attributes.length ? " " + attributes.join(" ") : ""
  76. }
  77. function properties(elem) {
  78. var props = []
  79. for (var key in elem) {
  80. if (isProperty(elem, key)) {
  81. props.push({ name: key, value: elem[key] })
  82. }
  83. }
  84. for (var ns in elem._attributes) {
  85. for (var attribute in elem._attributes[ns]) {
  86. var prop = elem._attributes[ns][attribute]
  87. var name = (prop.prefix ? prop.prefix + ":" : "") + attribute
  88. props.push({ name: name, value: prop.value })
  89. }
  90. }
  91. if (elem.className) {
  92. props.push({ name: "class", value: elem.className })
  93. }
  94. return props.length ? stringify(props) : ""
  95. }
  96. function escapeText(s) {
  97. var str = '';
  98. if (typeof(s) === 'string') {
  99. str = s;
  100. } else if (s) {
  101. str = s.toString();
  102. }
  103. return str
  104. .replace(/&/g, "&amp;")
  105. .replace(/</g, "&lt;")
  106. .replace(/>/g, "&gt;")
  107. }
  108. function escapeAttributeValue(str) {
  109. return escapeText(str).replace(/"/g, "&quot;")
  110. }