(function() { var XHTML = "http://www.w3.org/1999/xhtml"; // Time slicing constants var LIMIT = 10; // Maximum number of nodes to process before checking time var DURATION = 200; // Maximum amount of time (ms) to process before unblocking UI var DELAY = 15; // Amount of time (ms) to unblock UI // Tree building state var iterator; var nextNode; var root; var rootFirstChild; var time; // Template References var attrTemplate, attrName, attrValue; var elmStartTemplate, elmStartName; var elmEndTemplate, elmEndName; var cdataTemplate, cdataValue; var commentTemplate, commentValue; var style; // Only invoke this script if it was injected by our parser. Test for a condition that is // impossible for a markup to create - two direct children of the document. var secondRootElement = document.documentElement.nextElementSibling; if (secondRootElement == null) { // We've been included externally, bail return; } // Remove the script element from the document so that it doesn't show up in the XML tree view document.removeChild(secondRootElement); // Main entry point. Build the tree view. function buildTree() { time = (new Date()).getTime(); // Cache references to the root element and its first child for later use root = document.documentElement; rootFirstChild = root.firstChild; // Initialize templates buildAttributeTemplate(); buildCDATASectionTemplate(); buildCommentTemplate(); buildElementTemplate(); buildStyles(); // Start processing the document buildXMLDeclaration(); iterator = document.createNodeIterator(document, NodeFilter.SHOW_ALL, null, false); nextNode = iterator.nextNode(); buildNode(); } // Resume tree building after pausing to unblock UI function resumeBuilder() { time = (new Date()).getTime(); buildNode(); } // Build the tree incrementally and delegate to appropriate node function function buildNode() { var node, i = 0; while(nextNode) { if(i++ <= LIMIT) { node = nextNode; nextNode = iterator.nextNode(); switch(node.nodeType) { case Node.CDATA_SECTION_NODE : buildCDATASection (node); break; case Node.COMMENT_NODE : buildComment (node); break; case Node.DOCUMENT_TYPE_NODE : buildDocumentType (node); break; case Node.ELEMENT_NODE : buildElement (node); break; case Node.PROCESSING_INSTRUCTION_NODE : buildProcessingInstruction(node); break; // No additional handling is needed for text nodes } } else if((new Date()).getTime() - time >= DURATION) { setTimeout(resumeBuilder, DELAY); break; } else { i = 0; } } } // Attr function buildAttribute(attr, treeNode) { attrName.textContent = attr.nodeName; attrValue.textContent = attr.nodeValue; // Custom Styling for xmlns or xml if(attr.namespaceURI && (attr.namespaceURI == "http://www.w3.org/2000/xmlns/" || attr.namespaceURI == "http://www.w3.org/XML/1998/namespace")) { attrName.style.color = "red"; attrValue.style.color = "red"; } else { attrName.style.color = "#900"; attrValue.style.color = "black"; } treeNode.appendChild(attrTemplate.cloneNode(true)); } // Attr Template function buildAttributeTemplate() { // Template root attrTemplate = document.createDocumentFragment(); attrTemplate.appendChild(document.createTextNode(" ")); // Attribute name attrName = document.createElementNS(XHTML, "span"); attrTemplate.appendChild(attrName); attrTemplate.appendChild(document.createTextNode('="')); // Attribute value attrValue = document.createElementNS(XHTML, "span"); attrValue.style.fontWeight = "bold"; attrTemplate.appendChild(attrValue); attrTemplate.appendChild(document.createTextNode('"')); } // CDATASection function buildCDATASection(cdata) { cdataValue.textContent = cdata.nodeValue; cdata.parentNode.insertBefore(cdataTemplate.cloneNode(true), cdata); cdata.parentNode.removeChild(cdata); } // CDATASection Template function buildCDATASectionTemplate() { cdataTemplate = document.createElementNS(XHTML, "div"); cdataTemplate.style.color = "blue"; cdataTemplate.style.fontWeight = "normal"; cdataTemplate.style.marginLeft = "-2em"; var start = document.createTextNode(""); cdataTemplate.appendChild(end); } // Comment function buildComment(comment) { commentValue.textContent = comment.nodeValue; insertBefore(commentTemplate.cloneNode(true), comment); } // Comment Template function buildCommentTemplate() { commentTemplate = document.createElementNS(XHTML, "div"); commentTemplate.style.color = "blue"; commentTemplate.style.fontWeight = "normal"; var start = document.createTextNode(""); commentTemplate.appendChild(end); } // DocumentType function buildDocumentType(doctype) { var treeNode = document.createElementNS(XHTML, "div"); treeNode.style.fontWeight = "normal"; treeNode.style.color = "blue"; treeNode.textContent = ""; insertBefore(treeNode, doctype); } // Element function buildElement(elm) { // Skip HTML elements since they are part of the tree view, not the original XML if(elm instanceof HTMLElement) return; // Retrieve information about this element's content model (has children, needs to be collapsable) var firstChild = (elm == root) ? rootFirstChild : elm.firstChild; var hasChildren = (null != firstChild); var isCollapsable = (hasChildren && (null != firstChild.nextSibling || firstChild.nodeType != Node.TEXT_NODE)); // Build the start tag from the element start template elmStartName.textContent = elm.nodeName; var treeNode = elmStartTemplate.cloneNode(true); // Check if this needs to be a collapsable element if(isCollapsable) { // Set the appropriate state for expand/collapse behavior treeNode.href = "#"; treeNode.className = "collapse"; treeNode.style.position = "relative"; treeNode.onclick = toggle; } // Insert the generated start tag into the tree if(elm == root) { // Ensure the start tag for the root element appears after // the display for preceeding comments, processing instructions, etc. elm.insertBefore(treeNode, rootFirstChild); } else { // For other elements, simply place make the start tag the first child elm.insertBefore(treeNode, elm.firstChild); } // Generate representation of attributes for(var i = 0; i < elm.attributes.length; i++) { buildAttribute(elm.attributes[i], treeNode); } // Close the start tag appropriately treeNode.appendChild(document.createTextNode((hasChildren) ? ">" : "/>")); // Create an end tag (if needed) if(hasChildren) { elmEndName.textContent = elm.nodeName; treeNode = elmEndTemplate.cloneNode(true); if(isCollapsable) { treeNode.className = "block"; treeNode.style.marginLeft = "-2em"; } elm.appendChild(treeNode); } } // Element Template function buildElementTemplate() { // Start tag elmStartTemplate = document.createElementNS(XHTML, "a"); elmStartTemplate.style.color = "blue"; elmStartTemplate.style.marginLeft = "-2em"; elmStartTemplate.appendChild(document.createTextNode("<")); elmStartName = document.createElementNS(XHTML, "span"); elmStartName.style.color = "#900"; elmStartTemplate.appendChild(elmStartName); // End tag elmEndTemplate = document.createElementNS(XHTML, "span"); elmEndTemplate.style.color = "blue"; elmEndTemplate.appendChild(document.createTextNode("")); } // ProcessingInstruction function buildProcessingInstruction(pi) { var treeNode = document.createElementNS(XHTML, "div"); treeNode.style.color = "blue"; treeNode.style.fontWeight = "normal"; treeNode.textContent = ""; insertBefore(treeNode, pi); } // Create a stylesheet to apply general styles to the tree view function buildStyles() { // Style definitions var styles = "@namespace html url(" + XHTML + ");" // Setup styles on the root element + " :root { " + " font:small Verdana; " + " font-weight: bold; " + " padding: 2em; " + " padding-left:4em; " + " } " // Default all elements to block styling // This allows generic elements to provide structure + " * { " + " display: block; " + " padding-left: 2em; " + " } " // Ensure the style element stays hidden + " html|style { " + " display: none; " + " } " // Default styling for tag-like spans/anchors + " html|span, html|a { " + " display: inline; " + " padding: 0; " + " font-weight: normal; " + " text-decoration: none; " + " } " + " html|span.block { " + " display: block; " + " } " // Hide content under a collapsed element + " *[html|hidden], " + " span.block[html|hidden] { " + " display: none; " + " } " // Show "+" for elements that can be expanded + " .expand { " + " display: block; " + " } " + " .expand:before { " + " content: '+'; " + " color: red; " + " position: absolute; " + " left: -1em; " + " } " // Show "-" for elements that can be collapsed + " .collapse { " + " display: block; " + " } " + " .collapse:before { " + " content: '-'; " + " color: red; " + " position: absolute; " + " left:-1em; " + " } "; // Build a