window.xmlTreeViewer = (function() { var documentNode = null; var nodeParentPairs = []; var tree; var container; function prepareXMLViewer(node) { documentNode = node; var body = createHTMLElement('div'); var sourceXML = createHTMLElement('div'); sourceXML.id = 'xml-viewer-source-xml'; body.appendChild(sourceXML); var child; while (child = documentNode.firstChild) { documentNode.removeChild(child); if (child.nodeType != Node.DOCUMENT_TYPE_NODE) sourceXML.appendChild(child); } tree = createHTMLElement('div'); body.appendChild(tree); tree.classList.add('pretty-print'); //window.onload = sourceXMLLoaded; container = body; sourceXMLLoaded(); return body; } function sourceXMLLoaded() { //var sourceXML = container.getElementById('webkit-xml-viewer-source-xml'); var sourceXML = container.firstChild; if (!sourceXML) return; // Stop if some XML tree extension is already processing this document for (var child = sourceXML.firstChild; child; child = child.nextSibling) nodeParentPairs.push({parentElement: tree, node: child}); for (var i = 0; i < nodeParentPairs.length; i++) processNode(nodeParentPairs[i].parentElement, nodeParentPairs[i].node); initButtons(); return false; } // Tree processing. function processNode(parentElement, node) { var map = processNode.processorsMap; if (!map) { map = {}; processNode.processorsMap = map; map[Node.PROCESSING_INSTRUCTION_NODE] = processProcessingInstruction; map[Node.ELEMENT_NODE] = processElement; map[Node.COMMENT_NODE] = processComment; map[Node.TEXT_NODE] = processText; map[Node.CDATA_SECTION_NODE] = processCDATA; } if (processNode.processorsMap[node.nodeType]) processNode.processorsMap[node.nodeType].call(this, parentElement, node); } function processElement(parentElement, node) { if (!node.firstChild) processEmptyElement(parentElement, node); else { var child = node.firstChild; if (child.nodeType == Node.TEXT_NODE && isShort(child.nodeValue) && !child.nextSibling) processShortTextOnlyElement(parentElement, node); else processComplexElement(parentElement, node); } } function processEmptyElement(parentElement, node) { var line = createLine(); line.appendChild(createTag(node, false, true)); parentElement.appendChild(line); } function processShortTextOnlyElement(parentElement, node) { var line = createLine(); line.appendChild(createTag(node, false, false)); for (var child = node.firstChild; child; child = child.nextSibling) line.appendChild(createText(child.nodeValue)); line.appendChild(createTag(node, true, false)); parentElement.appendChild(line); } function processComplexElement(parentElement, node) { var collapsible = createCollapsible(); collapsible.expanded.start.appendChild(createTag(node, false, false)); for (var child = node.firstChild; child; child = child.nextSibling) nodeParentPairs.push({parentElement: collapsible.expanded.content, node: child}); collapsible.expanded.end.appendChild(createTag(node, true, false)); collapsible.collapsed.content.appendChild(createTag(node, false, false)); collapsible.collapsed.content.appendChild(createText('...')); collapsible.collapsed.content.appendChild(createTag(node, true, false)); parentElement.appendChild(collapsible); } function processComment(parentElement, node) { if (isShort(node.nodeValue)) { var line = createLine(); line.appendChild(createComment('')); parentElement.appendChild(line); } else { var collapsible = createCollapsible(); collapsible.expanded.start.appendChild(createComment('')); collapsible.collapsed.content.appendChild(createComment('')); parentElement.appendChild(collapsible); } } function processCDATA(parentElement, node) { if (isShort(node.nodeValue)) { var line = createLine(); line.appendChild(createText('')); parentElement.appendChild(line); } else { var collapsible = createCollapsible(); collapsible.expanded.start.appendChild(createText('')); collapsible.collapsed.content.appendChild(createText('')); parentElement.appendChild(collapsible); } } function processProcessingInstruction(parentElement, node) { if (isShort(node.nodeValue)) { var line = createLine(); line.appendChild(createComment('')); parentElement.appendChild(line); } else { var collapsible = createCollapsible(); collapsible.expanded.start.appendChild(createComment('')); collapsible.collapsed.content.appendChild(createComment('')); parentElement.appendChild(collapsible); } } function processText(parentElement, node) { parentElement.appendChild(createText(node.nodeValue)); } // Processing utils. function trim(value) { return value.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); } function isShort(value) { return trim(value).length <= 50; } // Tree rendering. function createHTMLElement(elementName) { //return document.createElementNS('http://www.w3.org/1999/xhtml', elementName); return document.createElement(elementName); } function createCollapsible() { var collapsible = createHTMLElement('div'); collapsible.classList.add('collapsible'); collapsible.expanded = createHTMLElement('div'); collapsible.expanded.classList.add('expanded'); collapsible.appendChild(collapsible.expanded); collapsible.expanded.start = createLine(); collapsible.expanded.start.appendChild(createCollapseButton()); collapsible.expanded.appendChild(collapsible.expanded.start); collapsible.expanded.content = createHTMLElement('div'); collapsible.expanded.content.classList.add('collapsible-content'); collapsible.expanded.appendChild(collapsible.expanded.content); collapsible.expanded.end = createLine(); collapsible.expanded.appendChild(collapsible.expanded.end); collapsible.collapsed = createHTMLElement('div'); collapsible.collapsed.classList.add('collapsed'); collapsible.collapsed.classList.add('hidden'); collapsible.appendChild(collapsible.collapsed); collapsible.collapsed.content = createLine(); collapsible.collapsed.content.appendChild(createExpandButton()); collapsible.collapsed.appendChild(collapsible.collapsed.content); return collapsible; } function createButton() { var button = createHTMLElement('span'); button.classList.add('button'); return button; } function createCollapseButton(str) { var button = createButton(); button.classList.add('collapse-button'); return button; } function createExpandButton(str) { var button = createButton(); button.classList.add('expand-button'); return button; } function createComment(commentString) { var comment = createHTMLElement('span'); comment.classList.add('comment'); comment.classList.add('html-comment'); comment.textContent = commentString; return comment; } function createText(value) { var text = createHTMLElement('span'); text.textContent = trim(value); text.classList.add('html-text'); return text; } function createLine() { var line = createHTMLElement('div'); line.classList.add('line'); return line; } function createTag(node, isClosing, isEmpty) { var tag = createHTMLElement('span'); tag.classList.add('html-tag'); var stringBeforeAttrs = '<'; if (isClosing) stringBeforeAttrs += '/'; //stringBeforeAttrs += node.nodeName; var textBeforeAttrs = document.createTextNode(stringBeforeAttrs); tag.appendChild(textBeforeAttrs); var tagNode = createHTMLElement('span'); tagNode.classList.add('html-tag-name'); tagNode.textContent = node.nodeName; tag.appendChild(tagNode); if (!isClosing) { for (var i = 0; i < node.attributes.length; i++) tag.appendChild(createAttribute(node.attributes[i])); } var stringAfterAttrs = ''; if (isEmpty) stringAfterAttrs += '/'; stringAfterAttrs += '>'; var textAfterAttrs = document.createTextNode(stringAfterAttrs); tag.appendChild(textAfterAttrs); return tag; } function createAttribute(attributeNode) { var attribute = createHTMLElement('span'); attribute.classList.add('html-attribute'); var attributeName = createHTMLElement('span'); attributeName.classList.add('html-attribute-name'); attributeName.textContent = attributeNode.name; var textBefore = document.createTextNode(' '); var textBetween = document.createTextNode('="'); var attributeValue = createHTMLElement('span'); attributeValue.classList.add('html-attribute-value'); attributeValue.textContent = attributeNode.value; var textAfter = document.createTextNode('"'); attribute.appendChild(textBefore); attribute.appendChild(attributeName); attribute.appendChild(textBetween); attribute.appendChild(attributeValue); attribute.appendChild(textAfter); return attribute; } function expandFunction(sectionId) { return function () { container.querySelector('#' + sectionId + ' > .expanded').className = 'expanded'; container.querySelector('#' + sectionId + ' > .collapsed').className = 'collapsed hidden'; }; } function collapseFunction(sectionId) { return function () { container.querySelector('#' + sectionId + ' > .expanded').className = 'expanded hidden'; container.querySelector('#' + sectionId + ' > .collapsed').className = 'collapsed'; }; } function initButtons() { var sections = container.querySelectorAll('.collapsible'); for (var i = 0; i < sections.length; i++) { var sectionId = 'collapsible' + i; sections[i].id = sectionId; var expandedPart = sections[i].querySelector('#' + sectionId + ' > .expanded'); var collapseButton = expandedPart.querySelector('.collapse-button'); collapseButton.onclick = collapseFunction(sectionId); collapseButton.onmousedown = handleButtonMouseDown; var collapsedPart = sections[i].querySelector('#' + sectionId + ' > .collapsed'); var expandButton = collapsedPart.querySelector('.expand-button'); expandButton.onclick = expandFunction(sectionId); expandButton.onmousedown = handleButtonMouseDown; } } function handleButtonMouseDown(e) { // To prevent selection on double click e.preventDefault(); } var parseXML = function( data ) { var xml, tmp; if ( !data || typeof data !== "string" ) { return null; } try { if ( window.DOMParser ) { // Standard tmp = new window.DOMParser(); xml = tmp.parseFromString( data, "text/xml" ); } else { // IE xml = new window.ActiveXObject( "Microsoft.XMLDOM" ); xml.async = "false"; xml.loadXML( data ); } } catch ( e ) { xml = undefined; } var errMsg; if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { errMsg = xml.getElementsByTagName( "parsererror" )[0].textContent; } return {xml: xml, errMsg: errMsg}; }; return { getXMLViewerNode: prepareXMLViewer, parseXML: parseXML } })();