Skip to content

Instantly share code, notes, and snippets.

@yesasha
Last active May 9, 2018 18:54
Show Gist options
  • Select an option

  • Save yesasha/3283a14f4a065aa2d3425b22f8cb8b41 to your computer and use it in GitHub Desktop.

Select an option

Save yesasha/3283a14f4a065aa2d3425b22f8cb8b41 to your computer and use it in GitHub Desktop.
Convert node to plain object and back. Development version.
(function (w, defaultNamespaceURI) {
"use strict";
var namespaceURI = defaultNamespaceURI;
function toObj (node) {
if (node == null) {
return null;
}
var obj = {};
var nodeType = determineNodeType(node);
var tagName = node.tagName;
var nodeName = node.nodeName;
var nodeValue = node.nodeValue;
switch (nodeType) {
case Node.ELEMENT_NODE:
if ('tagName' in node) {
tagName = ('' + tagName).toLowerCase();
} else if ('nodeName' in node) {
tagName = ('' + nodeName).toLowerCase();
} else {
throw new TypeError('TagName or nodeName property is required.');
}
// obj.nodeType = Node.ELEMENT_NODE;
obj.tagName = tagName;
// obj.nodeName = tagName;
if ('namespaceURI' in node && node.namespaceURI !== defaultNamespaceURI) {
obj.namespaceURI = node.namespaceURI;
}
var attributes = node.attributes || [];
for (var i = 0; i < attributes.length; i++) {
var attr = attributes[i];
if (attr) {
if ('name' in attr && 'value' in attr) {
obj.attributes || (obj.attributes = []);
obj.attributes.push({name: attr.name, value: attr.value});
} else {
throw new TypeError('Name and value are required in attribute.');
}
}
}
break;
case Node.TEXT_NODE:
if (!('nodeValue' in node)) {
throw new TypeError('NodeValue property is required.');
}
// obj.nodeType = Node.TEXT_NODE;
obj.nodeName = '#text';
obj.nodeValue = nodeValue;
return obj;
case Node.PROCESSING_INSTRUCTION_NODE:
if (!('nodeName' in node) || !('nodeValue' in node)) {
throw new TypeError('NodeName and nodeValue properties are required.');
}
// obj.nodeType = Node.PROCESSING_INSTRUCTION_NODE;
obj.nodeName = nodeName;
obj.nodeValue = nodeValue;
return obj;
case Node.COMMENT_NODE:
if (!('nodeValue' in node)) {
throw new TypeError('NodeValue property is required.');
}
// obj.nodeType = Node.COMMENT_NODE;
obj.nodeName = '#comment';
obj.nodeValue = nodeValue;
return obj;
case Node.DOCUMENT_NODE:
// obj.nodeType = Node.DOCUMENT_NODE;
obj.nodeName = '#document';
break;
case Node.DOCUMENT_FRAGMENT_NODE:
// obj.nodeType = Node.DOCUMENT_FRAGMENT_NODE;
obj.nodeName = '#document-fragment';
break;
case Node.DOCUMENT_TYPE_NODE:
// obj.nodeType = Node.DOCUMENT_TYPE_NODE;
if ('name' in node) {
obj.name = node.name;
} else if ('nodeName' in node) {
obj.name = nodeName;
} else {
throw new TypeError('Name or nodeName property is required.');
}
var publicId = node.publicId;
var systemId = node.systemId;
if ('publicId' in node && publicId !== '') {
obj.publicId = publicId;
}
if ('systemId' in node && systemId !== '') {
obj.systemId = systemId;
}
return obj;
default:
return null;
}
var childNodes = node.childNodes || [];
for (var i = 0; i < childNodes.length; i++) {
var childNode = childNodes[i];
childNode = toObj(childNode); // May also throw
if (childNode) {
obj.childNodes || (obj.childNodes = []);
obj.childNodes.push(childNode);
}
}
return obj;
}
function toObjAll (node) {
if (node == null) {
return null;
}
var obj = {};
var nodeType = determineNodeType(node);
var tagName = node.tagName;
var nodeName = node.nodeName;
var nodeValue = node.nodeValue;
switch (nodeType) {
case Node.ELEMENT_NODE:
if ('tagName' in node) {
tagName = ('' + tagName).toLowerCase();
} else if ('nodeName' in node) {
tagName = ('' + nodeName).toLowerCase();
} else {
throw new TypeError('TagName or nodeName property is required.');
}
obj.nodeType = Node.ELEMENT_NODE;
obj.tagName = tagName;
obj.nodeName = tagName;
if ('namespaceURI' in node && node.namespaceURI !== defaultNamespaceURI) {
obj.namespaceURI = node.namespaceURI;
}
var attributes = node.attributes || [];
for (var i = 0; i < attributes.length; i++) {
var attr = attributes[i];
if (attr) {
if ('name' in attr && 'value' in attr) {
obj.attributes || (obj.attributes = []);
obj.attributes.push({name: attr.name, value: attr.value});
} else {
throw new TypeError('Name and value are required in attribute.');
}
}
}
break;
case Node.TEXT_NODE:
if (!('nodeValue' in node)) {
throw new TypeError('NodeValue property is required.');
}
obj.nodeType = Node.TEXT_NODE;
obj.nodeName = '#text';
obj.nodeValue = nodeValue;
return obj;
case Node.PROCESSING_INSTRUCTION_NODE:
if (!('nodeName' in node) || !('nodeValue' in node)) {
throw new TypeError('NodeName and nodeValue properties are required.');
}
obj.nodeType = Node.PROCESSING_INSTRUCTION_NODE;
obj.nodeName = nodeName;
obj.nodeValue = nodeValue;
return obj;
case Node.COMMENT_NODE:
if (!('nodeValue' in node)) {
throw new TypeError('NodeValue property is required.');
}
obj.nodeType = Node.COMMENT_NODE;
obj.nodeName = '#comment';
obj.nodeValue = nodeValue;
return obj;
case Node.DOCUMENT_NODE:
obj.nodeType = Node.DOCUMENT_NODE;
obj.nodeName = '#document';
break;
case Node.DOCUMENT_FRAGMENT_NODE:
obj.nodeType = Node.DOCUMENT_FRAGMENT_NODE;
obj.nodeName = '#document-fragment';
break;
case Node.DOCUMENT_TYPE_NODE:
obj.nodeType = Node.DOCUMENT_TYPE_NODE;
if ('name' in node) {
obj.name = node.name;
} else if ('nodeName' in node) {
obj.name = nodeName;
} else {
throw new TypeError('Name or nodeName property is required.');
}
var publicId = node.publicId;
var systemId = node.systemId;
if ('publicId' in node && publicId !== '') {
obj.publicId = publicId;
}
if ('systemId' in node && systemId !== '') {
obj.systemId = systemId;
}
return obj;
default:
return null;
}
var childNodes = node.childNodes || [];
for (var i = 0; i < childNodes.length; i++) {
var childNode = childNodes[i];
childNode = toObjAll(childNode); // May also throw
if (childNode) {
obj.childNodes || (obj.childNodes = []);
obj.childNodes.push(childNode);
}
}
return obj;
}
function toDOM (obj) {
if (obj == null) {
return null;
}
var node;
var name;
var nodeType = determineNodeType(obj);
var nodeName = obj.nodeName;
var nodeValue = obj.nodeValue;
var publicId;
var systemId;
switch (nodeType) {
case Node.ELEMENT_NODE:
if ('tagName' in obj) {
nodeName = obj.tagName;
} else if ('nodeName' in obj) {
nodeName = obj.nodeName;
} else {
throw new TypeError('TagName or nodeName property is required to create an ELEMENT_NODE.');
}
if ('namespaceURI' in obj) {
namespaceURI = obj.namespaceURI;
} else {
namespaceURI = defaultNamespaceURI;
}
node = document.createElementNS(namespaceURI, nodeName);
setAttributes(node, obj.attributes);
break;
case Node.TEXT_NODE:
if ('nodeValue' in obj) {
return document.createTextNode(nodeValue);
}
throw new TypeError('nodeValue property is required to create a TEXT_NODE.');
case Node.PROCESSING_INSTRUCTION_NODE:
if ('nodeName' in obj && 'nodeValue' in obj) {
return document.createProcessingInstruction(nodeName, nodeValue);
}
throw new TypeError('NodeName and nodeValue properties are required to create a PROCESSING_INSTRUCTION_NODE.');
case Node.COMMENT_NODE:
if ('nodeValue' in obj) {
return document.createComment(nodeValue);
}
throw new TypeError('NodeValue property is required to create a COMMENT_NODE.');
case Node.DOCUMENT_NODE:
var doctype = null;
var otherChildNodes = [];
var documentElement;
nodeName = null;
// Find DOCUMENT_TYPE_NODE and ELEMENT_NODE
var childNodes = obj.childNodes || [];
for (var i = 0; i < childNodes.length; i++) {
var childNode = childNodes[i];
nodeType = determineNodeType(childNode);
if (nodeType === Node.DOCUMENT_TYPE_NODE) {
doctype = toDOM(childNode);
} else if (nodeType === Node.ELEMENT_NODE) {
documentElement = childNode;
if ('namespaceURI' in childNode) {
namespaceURI = childNode.namespaceURI;
} else {
namespaceURI = defaultNamespaceURI;
}
if ('tagName' in childNode) {
nodeName = childNode.tagName;
} else if ('nodeName' in childNode) {
nodeName = childNode.nodeName;
} else {
throw new TypeError('TagName or nodeName property is required in ELEMENT_NODE.');
}
} else {
otherChildNodes.push(childNode);
}
}
node = document.implementation.createDocument(namespaceURI, nodeName, doctype);
if (node.documentElement) {
setAttributes(node.documentElement, documentElement.attributes);
appendChildNodes(node.documentElement, documentElement.childNodes);
}
appendChildNodes(node, otherChildNodes);
return node;
case Node.DOCUMENT_TYPE_NODE:
if ('name' in obj) {
name = obj.name;
} else if ('nodeName' in obj) {
name = obj.nodeName;
} else {
throw new TypeError('Name or nodeName property is required to create a DOCUMENT_TYPE_NODE.');
}
publicId = obj.publicId;
systemId = obj.systemId;
if (!('publicId' in obj)) {
publicId = '';
}
if (!('systemId' in obj)) {
systemId = '';
}
return document.implementation.createDocumentType(name, publicId, systemId);
case Node.DOCUMENT_FRAGMENT_NODE:
node = document.createDocumentFragment();
break;
default:
return null;
}
appendChildNodes(node, obj.childNodes);
return node;
}
function toDOMPreserveNodes (obj) {
if (obj == null) {
return null;
}
var type = Object.prototype.toString.call(obj).slice(8, -1);
if (
type.match(/HTML.+Element/) !== null ||
type === 'Text' ||
type === 'ProcessingInstruction' ||
type === 'Comment' ||
type === 'HTMLDocument' ||
type === 'DocumentType' ||
type === 'DocumentFragment'
) {
return obj;
}
var node;
var name;
var nodeType = determineNodeType(obj);
var nodeName = obj.nodeName;
var nodeValue = obj.nodeValue;
var publicId;
var systemId;
switch (nodeType) {
case Node.ELEMENT_NODE:
if ('tagName' in obj) {
nodeName = obj.tagName;
} else if ('nodeName' in obj) {
nodeName = obj.nodeName;
} else {
throw new TypeError('TagName or nodeName property is required to create an ELEMENT_NODE.');
}
if ('namespaceURI' in obj) {
namespaceURI = obj.namespaceURI;
} else {
namespaceURI = defaultNamespaceURI;
}
node = document.createElementNS(namespaceURI, nodeName);
setAttributes(node, obj.attributes);
break;
case Node.TEXT_NODE:
if ('nodeValue' in obj) {
return document.createTextNode(nodeValue);
}
throw new TypeError('nodeValue property is required to create a TEXT_NODE.');
case Node.PROCESSING_INSTRUCTION_NODE:
if ('nodeName' in obj && 'nodeValue' in obj) {
return document.createProcessingInstruction(nodeName, nodeValue);
}
throw new TypeError('NodeName and nodeValue properties are required to create a PROCESSING_INSTRUCTION_NODE.');
case Node.COMMENT_NODE:
if ('nodeValue' in obj) {
return document.createComment(nodeValue);
}
throw new TypeError('NodeValue property is required to create a COMMENT_NODE.');
case Node.DOCUMENT_NODE:
var doctype = null;
var otherChildNodes = [];
var documentElement;
nodeName = null;
// Find DOCUMENT_TYPE_NODE and ELEMENT_NODE
var childNodes = obj.childNodes || [];
for (var i = 0; i < childNodes.length; i++) {
var childNode = childNodes[i];
nodeType = determineNodeType(childNode);
if (nodeType === Node.DOCUMENT_TYPE_NODE) {
doctype = toDOMPreserveNodes(childNode);
} else if (nodeType === Node.ELEMENT_NODE) {
documentElement = childNode;
if ('namespaceURI' in childNode) {
namespaceURI = childNode.namespaceURI;
} else {
namespaceURI = defaultNamespaceURI;
}
if ('tagName' in childNode) {
nodeName = childNode.tagName;
} else if ('nodeName' in childNode) {
nodeName = childNode.nodeName;
} else {
throw new TypeError('TagName or nodeName property is required in ELEMENT_NODE.');
}
} else {
otherChildNodes.push(childNode);
}
}
node = document.implementation.createDocument(namespaceURI, nodeName, doctype);
if (node.documentElement) {
setAttributes(node.documentElement, documentElement.attributes);
appendChildNodesPreserve(node.documentElement, documentElement.childNodes);
}
appendChildNodesPreserve(node, otherChildNodes);
return node;
case Node.DOCUMENT_TYPE_NODE:
if ('name' in obj) {
name = obj.name;
} else if ('nodeName' in obj) {
name = obj.nodeName;
} else {
throw new TypeError('Name or nodeName property is required to create a DOCUMENT_TYPE_NODE.');
}
publicId = obj.publicId;
systemId = obj.systemId;
if (!('publicId' in obj)) {
publicId = '';
}
if (!('systemId' in obj)) {
systemId = '';
}
return document.implementation.createDocumentType(name, publicId, systemId);
case Node.DOCUMENT_FRAGMENT_NODE:
node = document.createDocumentFragment();
break;
default:
return null;
}
appendChildNodesPreserve(node, obj.childNodes);
return node;
}
function setAttributes (dstElement, attributes) {
attributes = attributes || [];
for (var i = 0; i < attributes.length; i++) {
var attr = attributes[i];
if ('name' in attr && 'value' in attr) {
dstElement.setAttributeNS(namespaceURI, attr.name, attr.value);
} else {
throw new TypeError('Name and value are required in attribute.');
}
}
}
function appendChildNodes (dstElement, childNodes) {
childNodes = childNodes || [];
for (var i = 0; i < childNodes.length; i++) {
var childNode = toDOM(childNodes[i]);
if (childNode) {
dstElement.appendChild(childNode);
}
}
}
function appendChildNodesPreserve (dstElement, childNodes) {
childNodes = childNodes || [];
for (var i = 0; i < childNodes.length; i++) {
var childNode = toDOMPreserveNodes(childNodes[i]);
if (childNode) {
dstElement.appendChild(childNode);
}
}
}
function determineNodeType (node) {
if (node == null) {
return node;
}
var nodeType;
var nodeName = node.nodeName;
if (('nodeType' in node)) {
nodeType = +node.nodeType;
} else if (nodeName == '#text') {
nodeType = Node.TEXT_NODE;
} else if (nodeName == '#comment') {
nodeType = Node.COMMENT_NODE;
} else if (nodeName == '#document') {
nodeType = Node.DOCUMENT_NODE;
} else if (nodeName == '#document-fragment') {
nodeType = Node.DOCUMENT_FRAGMENT_NODE;
} else if ('tagName' in node) {
nodeType = Node.ELEMENT_NODE;
} else if ('name' in node) {
nodeType = Node.DOCUMENT_TYPE_NODE;
} else if ('nodeName' in node && 'nodeValue' in node) {
nodeType = Node.PROCESSING_INSTRUCTION_NODE;
} else if ('nodeName' in node) {
nodeType = Node.ELEMENT_NODE;
} else if ('nodeValue' in node) {
nodeType = Node.TEXT_NODE; // Sugar
} else {
throw new TypeError('Can not determine node type.');
}
return nodeType;
}
w['toObj'] = toObj;
w['toObjAll'] = toObjAll;
w['toDOM'] = toDOM;
w['toDOMPreserveNodes'] = toDOMPreserveNodes;
})(window, document.documentElement.namespaceURI);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment