• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2011 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS
17 * “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC.
20 * OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29var nodeParentPairs = [];
30
31// Script entry point.
32
33function prepareWebKitXMLViewer(noStyleMessage)
34{
35    var html = createHTMLElement('html');
36    var head = createHTMLElement('head');
37    html.appendChild(head);
38    var style = createHTMLElement('style');
39    style.id = 'xml-viewer-style';
40    head.appendChild(style);
41    var body = createHTMLElement('body');
42    html.appendChild(body);
43    var sourceXML = createHTMLElement('div');
44    sourceXML.id = 'webkit-xml-viewer-source-xml';
45    body.appendChild(sourceXML);
46
47    var child;
48    while (child = document.firstChild) {
49        document.removeChild(child);
50        if (child.nodeType != Node.DOCUMENT_TYPE_NODE)
51            sourceXML.appendChild(child);
52    }
53    document.appendChild(html);
54
55    var header = createHTMLElement('div');
56    body.appendChild(header);
57    header.classList.add('header');
58    var headerSpan = createHTMLElement('span');
59    header.appendChild(headerSpan);
60    headerSpan.textContent = noStyleMessage;
61    header.appendChild(createHTMLElement('br'));
62
63    var tree = createHTMLElement('div');
64    body.appendChild(tree);
65    tree.classList.add('pretty-print');
66    tree.id = 'tree';
67    window.onload = sourceXMLLoaded;
68}
69
70function sourceXMLLoaded()
71{
72    var sourceXML = document.getElementById('webkit-xml-viewer-source-xml');
73    if (!sourceXML)
74        return; // Stop if some XML tree extension is already processing this document
75    //var style = document.head.firstChild;
76    //document.head.removeChild(style);
77    //document.head.appendChild(style);
78    var root = document.getElementById('tree');
79
80    for (var child = sourceXML.firstChild; child; child = child.nextSibling)
81        nodeParentPairs.push({parentElement: root, node: child});
82
83    for (var i = 0; i < nodeParentPairs.length; i++)
84        processNode(nodeParentPairs[i].parentElement, nodeParentPairs[i].node);
85
86    drawArrows();
87    initButtons();
88
89    if (typeof(onAfterWebkitXMLViewerLoaded) == 'function')
90      onAfterWebkitXMLViewerLoaded();
91}
92
93// Tree processing.
94
95function processNode(parentElement, node)
96{
97    if (!processNode.processorsMap) {
98        processNode.processorsMap = {};
99        processNode.processorsMap[Node.PROCESSING_INSTRUCTION_NODE] = processProcessingInstruction;
100        processNode.processorsMap[Node.ELEMENT_NODE] = processElement;
101        processNode.processorsMap[Node.COMMENT_NODE] = processComment;
102        processNode.processorsMap[Node.TEXT_NODE] = processText;
103        processNode.processorsMap[Node.CDATA_SECTION_NODE] = processCDATA;
104    }
105    if (processNode.processorsMap[node.nodeType])
106        processNode.processorsMap[node.nodeType].call(this, parentElement, node);
107}
108
109function processElement(parentElement, node)
110{
111    if (!node.firstChild)
112        processEmptyElement(parentElement, node);
113    else {
114        var child = node.firstChild;
115        if (child.nodeType == Node.TEXT_NODE && isShort(child.nodeValue) && !child.nextSibling)
116            processShortTextOnlyElement(parentElement, node);
117        else
118            processComplexElement(parentElement, node);
119    }
120}
121
122function processEmptyElement(parentElement, node)
123{
124    var line = createLine();
125    line.appendChild(createTag(node, false, true));
126    parentElement.appendChild(line);
127}
128
129function processShortTextOnlyElement(parentElement, node)
130{
131    var line = createLine();
132    line.appendChild(createTag(node, false, false));
133    for (var child = node.firstChild; child; child = child.nextSibling)
134        line.appendChild(createText(child.nodeValue));
135    line.appendChild(createTag(node, true, false));
136    parentElement.appendChild(line);
137}
138
139function processComplexElement(parentElement, node)
140{
141    var collapsible = createCollapsible();
142
143    collapsible.expanded.start.appendChild(createTag(node, false, false));
144    for (var child = node.firstChild; child; child = child.nextSibling)
145        nodeParentPairs.push({parentElement: collapsible.expanded.content, node: child});
146    collapsible.expanded.end.appendChild(createTag(node, true, false));
147
148    collapsible.collapsed.content.appendChild(createTag(node, false, false));
149    collapsible.collapsed.content.appendChild(createText('...'));
150    collapsible.collapsed.content.appendChild(createTag(node, true, false));
151    parentElement.appendChild(collapsible);
152}
153
154function processComment(parentElement, node)
155{
156    if (isShort(node.nodeValue)) {
157        var line = createLine();
158        line.appendChild(createComment('<!-- ' + node.nodeValue + ' -->'));
159        parentElement.appendChild(line);
160    } else {
161        var collapsible = createCollapsible();
162
163        collapsible.expanded.start.appendChild(createComment('<!--'));
164        collapsible.expanded.content.appendChild(createComment(node.nodeValue));
165        collapsible.expanded.end.appendChild(createComment('-->'));
166
167        collapsible.collapsed.content.appendChild(createComment('<!--'));
168        collapsible.collapsed.content.appendChild(createComment('...'));
169        collapsible.collapsed.content.appendChild(createComment('-->'));
170        parentElement.appendChild(collapsible);
171    }
172}
173
174function processCDATA(parentElement, node)
175{
176    if (isShort(node.nodeValue)) {
177        var line = createLine();
178        line.appendChild(createText('<![CDATA[ ' + node.nodeValue + ' ]]>'));
179        parentElement.appendChild(line);
180    } else {
181        var collapsible = createCollapsible();
182
183        collapsible.expanded.start.appendChild(createText('<![CDATA['));
184        collapsible.expanded.content.appendChild(createText(node.nodeValue));
185        collapsible.expanded.end.appendChild(createText(']]>'));
186
187        collapsible.collapsed.content.appendChild(createText('<![CDATA['));
188        collapsible.collapsed.content.appendChild(createText('...'));
189        collapsible.collapsed.content.appendChild(createText(']]>'));
190        parentElement.appendChild(collapsible);
191    }
192}
193
194function processProcessingInstruction(parentElement, node)
195{
196    if (isShort(node.nodeValue)) {
197        var line = createLine();
198        line.appendChild(createComment('<?' + node.nodeName + ' ' + node.nodeValue + '?>'));
199        parentElement.appendChild(line);
200    } else {
201        var collapsible = createCollapsible();
202
203        collapsible.expanded.start.appendChild(createComment('<?' + node.nodeName));
204        collapsible.expanded.content.appendChild(createComment(node.nodeValue));
205        collapsible.expanded.end.appendChild(createComment('?>'));
206
207        collapsible.collapsed.content.appendChild(createComment('<?' + node.nodeName));
208        collapsible.collapsed.content.appendChild(createComment('...'));
209        collapsible.collapsed.content.appendChild(createComment('?>'));
210        parentElement.appendChild(collapsible);
211    }
212}
213
214function processText(parentElement, node)
215{
216    parentElement.appendChild(createText(node.nodeValue));
217}
218
219// Processing utils.
220
221function trim(value)
222{
223    return value.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
224}
225
226function isShort(value)
227{
228    return trim(value).length <= 50;
229}
230
231// Tree rendering.
232
233function createHTMLElement(elementName)
234{
235    return document.createElementNS('http://www.w3.org/1999/xhtml', elementName)
236}
237
238function createCollapsible()
239{
240    var collapsible = createHTMLElement('div');
241    collapsible.classList.add('collapsible');
242    collapsible.expanded = createHTMLElement('div');
243    collapsible.expanded.classList.add('expanded');
244    collapsible.appendChild(collapsible.expanded);
245
246    collapsible.expanded.start = createLine();
247    collapsible.expanded.start.appendChild(createCollapseButton());
248    collapsible.expanded.appendChild(collapsible.expanded.start);
249
250    collapsible.expanded.content = createHTMLElement('div');
251    collapsible.expanded.content.classList.add('collapsible-content');
252    collapsible.expanded.appendChild(collapsible.expanded.content);
253
254    collapsible.expanded.end = createLine();
255    collapsible.expanded.appendChild(collapsible.expanded.end);
256
257    collapsible.collapsed = createHTMLElement('div');
258    collapsible.collapsed.classList.add('collapsed');
259    collapsible.collapsed.classList.add('hidden');
260    collapsible.appendChild(collapsible.collapsed);
261    collapsible.collapsed.content = createLine();
262    collapsible.collapsed.content.appendChild(createExpandButton());
263    collapsible.collapsed.appendChild(collapsible.collapsed.content);
264
265    return collapsible;
266}
267
268function createButton()
269{
270    var button = createHTMLElement('span');
271    button.classList.add('button');
272    return button;
273}
274
275function createCollapseButton(str)
276{
277    var button = createButton();
278    button.classList.add('collapse-button');
279    return button;
280}
281
282function createExpandButton(str)
283{
284    var button = createButton();
285    button.classList.add('expand-button');
286    return button;
287}
288
289function createComment(commentString)
290{
291    var comment = createHTMLElement('span');
292    comment.classList.add('webkit-html-comment');
293    comment.textContent = commentString;
294    return comment;
295}
296
297function createText(value)
298{
299    var text = createHTMLElement('span');
300    text.textContent = trim(value);
301    text.classList.add('text');
302    return text;
303}
304
305function createLine()
306{
307    var line = createHTMLElement('div');
308    line.classList.add('line');
309    return line;
310}
311
312function createTag(node, isClosing, isEmpty)
313{
314    var tag = createHTMLElement('span');
315    tag.classList.add('webkit-html-tag');
316
317    var stringBeforeAttrs = '<';
318    if (isClosing)
319        stringBeforeAttrs += '/';
320    stringBeforeAttrs += node.nodeName;
321    var textBeforeAttrs = document.createTextNode(stringBeforeAttrs);
322    tag.appendChild(textBeforeAttrs);
323
324    if (!isClosing) {
325        for (var i = 0; i < node.attributes.length; i++)
326            tag.appendChild(createAttribute(node.attributes[i]));
327    }
328
329    var stringAfterAttrs = '';
330    if (isEmpty)
331        stringAfterAttrs += '/';
332    stringAfterAttrs += '>';
333    var textAfterAttrs = document.createTextNode(stringAfterAttrs);
334    tag.appendChild(textAfterAttrs);
335
336    return tag;
337}
338
339function createAttribute(attributeNode)
340{
341    var attribute = createHTMLElement('span');
342    attribute.classList.add('webkit-html-attribute');
343
344    var attributeName = createHTMLElement('span');
345    attributeName.classList.add('webkit-html-attribute-name');
346    attributeName.textContent = attributeNode.name;
347
348    var textBefore = document.createTextNode(' ');
349    var textBetween = document.createTextNode('="');
350
351    var attributeValue = createHTMLElement('span');
352    attributeValue.classList.add('webkit-html-attribute-value');
353    attributeValue.textContent = attributeNode.value;
354
355    var textAfter = document.createTextNode('"');
356
357    attribute.appendChild(textBefore);
358    attribute.appendChild(attributeName);
359    attribute.appendChild(textBetween);
360    attribute.appendChild(attributeValue);
361    attribute.appendChild(textAfter);
362    return attribute;
363}
364
365// Tree behaviour.
366
367function drawArrows()
368{
369    var ctx = document.getCSSCanvasContext("2d", "arrowRight", 10, 11);
370
371    ctx.fillStyle = "rgb(90,90,90)";
372    ctx.beginPath();
373    ctx.moveTo(0, 0);
374    ctx.lineTo(0, 8);
375    ctx.lineTo(7, 4);
376    ctx.lineTo(0, 0);
377    ctx.fill();
378    ctx.closePath();
379
380    var ctx = document.getCSSCanvasContext("2d", "arrowDown", 10, 10);
381
382    ctx.fillStyle = "rgb(90,90,90)";
383    ctx.beginPath();
384    ctx.moveTo(0, 0);
385    ctx.lineTo(8, 0);
386    ctx.lineTo(4, 7);
387    ctx.lineTo(0, 0);
388    ctx.fill();
389    ctx.closePath();
390}
391
392function expandFunction(sectionId)
393{
394    return function()
395    {
396        document.querySelector('#' + sectionId + ' > .expanded').className = 'expanded';
397        document.querySelector('#' + sectionId + ' > .collapsed').className = 'collapsed hidden';
398    };
399}
400
401function collapseFunction(sectionId)
402{
403    return function()
404    {
405        document.querySelector('#' + sectionId + ' > .expanded').className = 'expanded hidden';
406        document.querySelector('#' + sectionId + ' > .collapsed').className = 'collapsed';
407    };
408}
409
410function initButtons()
411{
412    var sections = document.querySelectorAll('.collapsible');
413    for (var i = 0; i < sections.length; i++) {
414        var sectionId = 'collapsible' + i;
415        sections[i].id = sectionId;
416
417        var expandedPart = sections[i].querySelector('#' + sectionId + ' > .expanded');
418        var collapseButton = expandedPart.querySelector('.collapse-button');
419        collapseButton.onclick = collapseFunction(sectionId);
420        collapseButton.onmousedown = handleButtonMouseDown;
421
422        var collapsedPart = sections[i].querySelector('#' + sectionId + ' > .collapsed');
423        var expandButton = collapsedPart.querySelector('.expand-button');
424        expandButton.onclick = expandFunction(sectionId);
425        expandButton.onmousedown = handleButtonMouseDown;
426    }
427
428}
429
430function handleButtonMouseDown(e)
431{
432   // To prevent selection on double click
433   e.preventDefault();
434}
435