chromium/third_party/blink/renderer/core/xml/DocumentXMLTreeViewer.js

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

"use strict";

var tree;

function prepareWebKitXMLViewer()
{
  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);
  var sourceXML = createHTMLElement('div');
  sourceXML.id = 'webkit-xml-viewer-source-xml';
  body.appendChild(sourceXML);

  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 =
      'This XML file does not appear to have any style information ' +
      'associated with it. The document tree is shown below.';
  header.appendChild(createHTMLElement('br'));

  tree = createHTMLElement('div');
  body.appendChild(tree);
  tree.classList.add('pretty-print');
  window.onload = sourceXMLLoaded;
}

function sourceXMLLoaded()
{
  var sourceXML = document.getElementById('webkit-xml-viewer-source-xml');
  if (!sourceXML)
    return;  // Stop if some XML tree extension is already processing this
             // document

  for (var child = sourceXML.firstChild; child; child = child.nextSibling)
    processNode(tree, child);

  initButtons();

  return false;
}

// Tree processing.

function processNode(parentElement, node)
{
  switch (node.nodeType) {
    case Node.PROCESSING_INSTRUCTION_NODE:
      processProcessingInstruction(parentElement, node);
      break;
    case Node.ELEMENT_NODE:
      processElement(parentElement, node);
      break;
    case Node.COMMENT_NODE:
      processComment(parentElement, node);
      break;
    case Node.TEXT_NODE:
      processText(parentElement, node);
      break;
    case Node.CDATA_SECTION_NODE:
      processCDATA(parentElement, node);
      break;
    default:
      // No-op for unsupported node types e.g. Node.DOCUMENT_FRAGMENT_NODE.
  }
}

function processElement(parentElement, node)
{
  if (!node.firstChild)
    processEmptyElement(parentElement, node);
  else {
    var child = node.firstChild;
    if (child.nodeType == Node.TEXT_NODE && !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 folder = createFolder();
  folder.start.appendChild(createTag(node, false, false));

  for (var child = node.firstChild; child; child = child.nextSibling)
    processNode(folder.openedContent, child);

  folder.end.appendChild(createTag(node, true, false));

  parentElement.appendChild(folder);
}

function processComment(parentElement, node)
{
  var line = createLine();
  line.appendChild(createComment('<!-- ' + node.nodeValue + ' -->'));
  parentElement.appendChild(line);
}

function processCDATA(parentElement, node)
{
  var line = createLine();
  line.appendChild(createText('<![CDATA[ ' + node.nodeValue + ' ]]>'));
  parentElement.appendChild(line);
}

function processProcessingInstruction(parentElement, node)
{
  var line = createLine();
  line.appendChild(
      createComment('<?' + node.nodeName + ' ' + node.nodeValue + '?>'));
  parentElement.appendChild(line);
}

function processText(parentElement, node)
{
  parentElement.appendChild(createText(node.nodeValue));
}

// Tree rendering.

function createHTMLElement(elementName)
{
  return document.createElementNS('http://www.w3.org/1999/xhtml', elementName)
}

function createFolder()
{
  var folder = createHTMLElement('div');
  folder.classList.add('folder');

  folder.start = createLine();
  folder.start.appendChild(createFolderButton());
  folder.appendChild(folder.start);

  folder.openedContent = createHTMLElement('div');
  folder.openedContent.classList.add('opened');
  folder.appendChild(folder.openedContent);

  // Folded content.
  folder.foldedContent = createText('...');
  folder.foldedContent.classList.add('folded');
  folder.foldedContent.classList.add('hidden');
  folder.appendChild(folder.foldedContent);

  folder.end = createLine();
  folder.appendChild(folder.end);

  return folder;
}

function createFolderButton(str) {
  var button = createHTMLElement('span');
  button.classList.add('folder-button');
  button.classList.add('fold');
  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 = value;
  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);

  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 toggleFunction(sectionId) {
  return function() {
    var foldedContent = document.querySelector('#' + sectionId + ' > .folded');
    var openedContent = document.querySelector('#' + sectionId + ' > .opened');
    var folderButton =
        document.querySelector('#' + sectionId + ' > .line > .folder-button');

    if (foldedContent) {
      if (foldedContent.className.includes('hidden'))
        foldedContent.className = 'folded';
      else
        foldedContent.className = 'folded hidden';
    }

    if (openedContent) {
      if (openedContent.className.includes('hidden'))
        openedContent.className = 'opened';
      else
        openedContent.className = 'opened hidden';
    }

    if (folderButton) {
      if (folderButton.className.includes('open'))
        folderButton.className = 'folder-button fold';
      else
        folderButton.className = 'folder-button open';
    }
  };
}

function initButtons()
{
  var sections = document.querySelectorAll('.folder');
  for (var i = 0; i < sections.length; i++) {
    var sectionId = 'folder' + i;
    sections[i].id = sectionId;

    var folderButton = sections[i].querySelector('.folder-button');
    folderButton.onclick = toggleFunction(sectionId);
    folderButton.onmousedown = handleButtonMouseDown;
  }
}

function handleButtonMouseDown(e)
{
   // To prevent selection on double click
   e.preventDefault();
}

prepareWebKitXMLViewer();