| /* |
| * Copyright (C) 2011 Google Inc. All rights reserved. |
| * Copyright (C) 2013 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS |
| * “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. |
| * OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| var nodeParentPairs = []; |
| var sourceXML; |
| |
| // Script entry point. |
| |
| function prepareWebKitXMLViewer(noStyleMessage) |
| { |
| var html = createHTMLElement('html'); |
| var head = createHTMLElement('head'); |
| html.appendChild(head); |
| var style = createHTMLElement('style'); |
| style.id = 'xml-viewer-style'; |
| head.appendChild(style); |
| var body = createHTMLElement('body'); |
| html.appendChild(body); |
| sourceXML = createHTMLElement('div'); |
| |
| var child; |
| while (child = document.firstChild) { |
| document.removeChild(child); |
| if (child.nodeType != Node.DOCUMENT_TYPE_NODE) |
| sourceXML.appendChild(child); |
| } |
| document.appendChild(html); |
| |
| var header = createHTMLElement('div'); |
| body.appendChild(header); |
| header.classList.add('header'); |
| var headerSpan = createHTMLElement('span'); |
| header.appendChild(headerSpan); |
| headerSpan.textContent = noStyleMessage; |
| header.appendChild(createHTMLElement('br')); |
| |
| var tree = createHTMLElement('div'); |
| body.appendChild(tree); |
| tree.classList.add('pretty-print'); |
| tree.id = 'tree'; |
| window.onload = sourceXMLLoaded; |
| } |
| |
| function sourceXMLLoaded() |
| { |
| var root = document.getElementById('tree'); |
| |
| for (var child = sourceXML.firstChild; child; child = child.nextSibling) |
| nodeParentPairs.push({parentElement: root, node: child}); |
| |
| for (var i = 0; i < nodeParentPairs.length; i++) |
| processNode(nodeParentPairs[i].parentElement, nodeParentPairs[i].node); |
| |
| drawArrows(); |
| initButtons(); |
| |
| if (typeof(onAfterWebkitXMLViewerLoaded) == 'function') |
| onAfterWebkitXMLViewerLoaded(); |
| } |
| |
| // Tree processing. |
| |
| function processNode(parentElement, node) |
| { |
| if (!processNode.processorsMap) { |
| processNode.processorsMap = {}; |
| processNode.processorsMap[Node.PROCESSING_INSTRUCTION_NODE] = processProcessingInstruction; |
| processNode.processorsMap[Node.ELEMENT_NODE] = processElement; |
| processNode.processorsMap[Node.COMMENT_NODE] = processComment; |
| processNode.processorsMap[Node.TEXT_NODE] = processText; |
| processNode.processorsMap[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('<!-- ' + node.nodeValue + ' -->')); |
| parentElement.appendChild(line); |
| } else { |
| var collapsible = createCollapsible(); |
| |
| collapsible.expanded.start.appendChild(createComment('<!--')); |
| collapsible.expanded.content.appendChild(createComment(node.nodeValue)); |
| collapsible.expanded.end.appendChild(createComment('-->')); |
| |
| collapsible.collapsed.content.appendChild(createComment('<!--')); |
| collapsible.collapsed.content.appendChild(createComment('...')); |
| collapsible.collapsed.content.appendChild(createComment('-->')); |
| parentElement.appendChild(collapsible); |
| } |
| } |
| |
| function processCDATA(parentElement, node) |
| { |
| if (isShort(node.nodeValue)) { |
| var line = createLine(); |
| line.appendChild(createText('<![CDATA[ ' + node.nodeValue + ' ]]>')); |
| parentElement.appendChild(line); |
| } else { |
| var collapsible = createCollapsible(); |
| |
| collapsible.expanded.start.appendChild(createText('<![CDATA[')); |
| collapsible.expanded.content.appendChild(createText(node.nodeValue)); |
| collapsible.expanded.end.appendChild(createText(']]>')); |
| |
| collapsible.collapsed.content.appendChild(createText('<![CDATA[')); |
| collapsible.collapsed.content.appendChild(createText('...')); |
| collapsible.collapsed.content.appendChild(createText(']]>')); |
| parentElement.appendChild(collapsible); |
| } |
| } |
| |
| function processProcessingInstruction(parentElement, node) |
| { |
| if (isShort(node.nodeValue)) { |
| var line = createLine(); |
| line.appendChild(createComment('<?' + node.nodeName + ' ' + node.nodeValue + '?>')); |
| parentElement.appendChild(line); |
| } else { |
| var collapsible = createCollapsible(); |
| |
| collapsible.expanded.start.appendChild(createComment('<?' + node.nodeName)); |
| collapsible.expanded.content.appendChild(createComment(node.nodeValue)); |
| collapsible.expanded.end.appendChild(createComment('?>')); |
| |
| collapsible.collapsed.content.appendChild(createComment('<?' + node.nodeName)); |
| collapsible.collapsed.content.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) |
| } |
| |
| 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.textContent = commentString; |
| return comment; |
| } |
| |
| function createText(value) |
| { |
| var text = createHTMLElement('span'); |
| text.textContent = trim(value); |
| text.classList.add('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('tag'); |
| |
| var stringBeforeAttrs = '<'; |
| if (isClosing) |
| stringBeforeAttrs += '/'; |
| stringBeforeAttrs += node.nodeName; |
| var textBeforeAttrs = document.createTextNode(stringBeforeAttrs); |
| tag.appendChild(textBeforeAttrs); |
| |
| 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'); |
| |
| var attributeName = createHTMLElement('span'); |
| attributeName.classList.add('attribute-name'); |
| attributeName.textContent = attributeNode.name; |
| |
| var textBefore = document.createTextNode(' '); |
| var textBetween = document.createTextNode('="'); |
| |
| var attributeValue = createHTMLElement('span'); |
| attributeValue.classList.add('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; |
| } |
| |
| // Tree behaviour. |
| |
| function drawArrows() |
| { |
| var ctx = document.getCSSCanvasContext("2d", "arrowRight", 10, 11); |
| |
| ctx.fillStyle = "rgb(90,90,90)"; |
| ctx.beginPath(); |
| ctx.moveTo(0, 0); |
| ctx.lineTo(0, 8); |
| ctx.lineTo(7, 4); |
| ctx.lineTo(0, 0); |
| ctx.fill(); |
| ctx.closePath(); |
| |
| var ctx = document.getCSSCanvasContext("2d", "arrowDown", 10, 10); |
| |
| ctx.fillStyle = "rgb(90,90,90)"; |
| ctx.beginPath(); |
| ctx.moveTo(0, 0); |
| ctx.lineTo(8, 0); |
| ctx.lineTo(4, 7); |
| ctx.lineTo(0, 0); |
| ctx.fill(); |
| ctx.closePath(); |
| } |
| |
| function expandFunction(sectionId) |
| { |
| return function() { |
| document.querySelector('#' + sectionId + ' > .expanded').className = 'expanded'; |
| document.querySelector('#' + sectionId + ' > .collapsed').className = 'collapsed hidden'; |
| }; |
| } |
| |
| function collapseFunction(sectionId) |
| { |
| return function() { |
| document.querySelector('#' + sectionId + ' > .expanded').className = 'expanded hidden'; |
| document.querySelector('#' + sectionId + ' > .collapsed').className = 'collapsed'; |
| }; |
| } |
| |
| function initButtons() |
| { |
| var sections = document.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(); |
| } |