xmlserializer
Version:
Serializes a document to XML/XHTML
130 lines (111 loc) • 4.11 kB
JavaScript
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define([], factory);
} else if (typeof module === 'object' && module.exports) {
module.exports = factory();
} else {
root.xmlserializer = factory();
}
}(this, function () {
var removeInvalidCharacters = function (content) {
// See http://www.w3.org/TR/xml/#NT-Char for valid XML 1.0 characters
return content.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, '');
};
var serializeAttributeValue = function (value) {
return value
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
};
var serializeTextContent = function (content) {
return content
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>');
};
var serializeAttribute = function (attr) {
var value = attr.value;
return ' ' + attr.name + '="' + serializeAttributeValue(value) + '"';
};
var getTagName = function (node) {
var tagName = node.tagName;
// Aid in serializing of original HTML documents
if (node.namespaceURI === 'http://www.w3.org/1999/xhtml') {
tagName = tagName.toLowerCase();
}
return tagName;
};
var serializeNamespace = function (node, isRootNode) {
var nodeHasXmlnsAttr = Array.prototype.map.call(node.attributes || node.attrs, function (attr) {
return attr.name;
})
.indexOf('xmlns') >= 0;
// Serialize the namespace as an xmlns attribute whenever the element
// doesn't already have one and the inherited namespace does not match
// the element's namespace.
if (!nodeHasXmlnsAttr &&
(isRootNode ||
node.namespaceURI !== node.parentNode.namespaceURI)) {
return ' xmlns="' + node.namespaceURI + '"';
} else {
return '';
}
};
var serializeChildren = function (node) {
return Array.prototype.map.call(node.childNodes, function (childNode) {
return nodeTreeToXHTML(childNode);
}).join('');
};
var serializeTag = function (node, isRootNode) {
var output = '<' + getTagName(node);
output += serializeNamespace(node, isRootNode);
Array.prototype.forEach.call(node.attributes || node.attrs, function (attr) {
output += serializeAttribute(attr);
});
if (node.childNodes.length > 0) {
output += '>';
output += serializeChildren(node);
output += '</' + getTagName(node) + '>';
} else {
output += '/>';
}
return output;
};
var serializeText = function (node) {
var text = node.nodeValue || node.value || '';
return serializeTextContent(text);
};
var serializeComment = function (node) {
return '<!--' +
node.data
.replace(/-/g, '-') +
'-->';
};
var serializeCDATA = function (node) {
return '<![CDATA[' + node.nodeValue + ']]>';
};
var nodeTreeToXHTML = function (node, options) {
var isRootNode = options && options.rootNode;
if (node.nodeName === '#document' ||
node.nodeName === '#document-fragment') {
return serializeChildren(node);
} else {
if (node.tagName) {
return serializeTag(node, isRootNode);
} else if (node.nodeName === '#text') {
return serializeText(node);
} else if (node.nodeName === '#comment') {
return serializeComment(node);
} else if (node.nodeName === '#cdata-section') {
return serializeCDATA(node);
}
}
};
return {
serializeToString: function (node) {
return removeInvalidCharacters(nodeTreeToXHTML(node, {rootNode: true}));
}
};
}));