• 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
33var tree;
34
35function prepareWebKitXMLViewer(noStyleMessage)
36{
37    var html = createHTMLElement('html');
38    var head = createHTMLElement('head');
39    html.appendChild(head);
40    var style = createHTMLElement('style');
41    style.id = 'xml-viewer-style';
42    head.appendChild(style);
43    var body = createHTMLElement('body');
44    html.appendChild(body);
45    var sourceXML = createHTMLElement('div');
46    sourceXML.id = 'webkit-xml-viewer-source-xml';
47    body.appendChild(sourceXML);
48
49    var child;
50    while (child = document.firstChild) {
51        document.removeChild(child);
52        if (child.nodeType != Node.DOCUMENT_TYPE_NODE)
53            sourceXML.appendChild(child);
54    }
55    document.appendChild(html);
56
57    var header = createHTMLElement('div');
58    body.appendChild(header);
59    header.classList.add('header');
60    var headerSpan = createHTMLElement('span');
61    header.appendChild(headerSpan);
62    headerSpan.textContent = noStyleMessage;
63    header.appendChild(createHTMLElement('br'));
64
65    tree = createHTMLElement('div');
66    body.appendChild(tree);
67    tree.classList.add('pretty-print');
68    window.onload = sourceXMLLoaded;
69}
70
71function sourceXMLLoaded()
72{
73    var sourceXML = document.getElementById('webkit-xml-viewer-source-xml');
74    if (!sourceXML)
75        return; // Stop if some XML tree extension is already processing this document
76
77    for (var child = sourceXML.firstChild; child; child = child.nextSibling)
78        nodeParentPairs.push({parentElement: tree, node: child});
79
80    for (var i = 0; i < nodeParentPairs.length; i++)
81        processNode(nodeParentPairs[i].parentElement, nodeParentPairs[i].node);
82
83    drawArrows();
84    initButtons();
85
86    if (typeof(onAfterWebkitXMLViewerLoaded) == 'function')
87      onAfterWebkitXMLViewerLoaded();
88}
89
90// Tree processing.
91
92function processNode(parentElement, node)
93{
94    if (!processNode.processorsMap) {
95        processNode.processorsMap = {};
96        processNode.processorsMap[Node.PROCESSING_INSTRUCTION_NODE] = processProcessingInstruction;
97        processNode.processorsMap[Node.ELEMENT_NODE] = processElement;
98        processNode.processorsMap[Node.COMMENT_NODE] = processComment;
99        processNode.processorsMap[Node.TEXT_NODE] = processText;
100        processNode.processorsMap[Node.CDATA_SECTION_NODE] = processCDATA;
101    }
102    if (processNode.processorsMap[node.nodeType])
103        processNode.processorsMap[node.nodeType].call(this, parentElement, node);
104}
105
106function processElement(parentElement, node)
107{
108    if (!node.firstChild)
109        processEmptyElement(parentElement, node);
110    else {
111        var child = node.firstChild;
112        if (child.nodeType == Node.TEXT_NODE && isShort(child.nodeValue) && !child.nextSibling)
113            processShortTextOnlyElement(parentElement, node);
114        else
115            processComplexElement(parentElement, node);
116    }
117}
118
119function processEmptyElement(parentElement, node)
120{
121    var line = createLine();
122    line.appendChild(createTag(node, false, true));
123    parentElement.appendChild(line);
124}
125
126function processShortTextOnlyElement(parentElement, node)
127{
128    var line = createLine();
129    line.appendChild(createTag(node, false, false));
130    for (var child = node.firstChild; child; child = child.nextSibling)
131        line.appendChild(createText(child.nodeValue));
132    line.appendChild(createTag(node, true, false));
133    parentElement.appendChild(line);
134}
135
136function processComplexElement(parentElement, node)
137{
138    var collapsible = createCollapsible();
139
140    collapsible.expanded.start.appendChild(createTag(node, false, false));
141    for (var child = node.firstChild; child; child = child.nextSibling)
142        nodeParentPairs.push({parentElement: collapsible.expanded.content, node: child});
143    collapsible.expanded.end.appendChild(createTag(node, true, false));
144
145    collapsible.collapsed.content.appendChild(createTag(node, false, false));
146    collapsible.collapsed.content.appendChild(createText('...'));
147    collapsible.collapsed.content.appendChild(createTag(node, true, false));
148    parentElement.appendChild(collapsible);
149}
150
151function processComment(parentElement, node)
152{
153    if (isShort(node.nodeValue)) {
154        var line = createLine();
155        line.appendChild(createComment('<!-- ' + node.nodeValue + ' -->'));
156        parentElement.appendChild(line);
157    } else {
158        var collapsible = createCollapsible();
159
160        collapsible.expanded.start.appendChild(createComment('<!--'));
161        collapsible.expanded.content.appendChild(createComment(node.nodeValue));
162        collapsible.expanded.end.appendChild(createComment('-->'));
163
164        collapsible.collapsed.content.appendChild(createComment('<!--'));
165        collapsible.collapsed.content.appendChild(createComment('...'));
166        collapsible.collapsed.content.appendChild(createComment('-->'));
167        parentElement.appendChild(collapsible);
168    }
169}
170
171function processCDATA(parentElement, node)
172{
173    if (isShort(node.nodeValue)) {
174        var line = createLine();
175        line.appendChild(createText('<![CDATA[ ' + node.nodeValue + ' ]]>'));
176        parentElement.appendChild(line);
177    } else {
178        var collapsible = createCollapsible();
179
180        collapsible.expanded.start.appendChild(createText('<![CDATA['));
181        collapsible.expanded.content.appendChild(createText(node.nodeValue));
182        collapsible.expanded.end.appendChild(createText(']]>'));
183
184        collapsible.collapsed.content.appendChild(createText('<![CDATA['));
185        collapsible.collapsed.content.appendChild(createText('...'));
186        collapsible.collapsed.content.appendChild(createText(']]>'));
187        parentElement.appendChild(collapsible);
188    }
189}
190
191function processProcessingInstruction(parentElement, node)
192{
193    if (isShort(node.nodeValue)) {
194        var line = createLine();
195        line.appendChild(createComment('<?' + node.nodeName + ' ' + node.nodeValue + '?>'));
196        parentElement.appendChild(line);
197    } else {
198        var collapsible = createCollapsible();
199
200        collapsible.expanded.start.appendChild(createComment('<?' + node.nodeName));
201        collapsible.expanded.content.appendChild(createComment(node.nodeValue));
202        collapsible.expanded.end.appendChild(createComment('?>'));
203
204        collapsible.collapsed.content.appendChild(createComment('<?' + node.nodeName));
205        collapsible.collapsed.content.appendChild(createComment('...'));
206        collapsible.collapsed.content.appendChild(createComment('?>'));
207        parentElement.appendChild(collapsible);
208    }
209}
210
211function processText(parentElement, node)
212{
213    parentElement.appendChild(createText(node.nodeValue));
214}
215
216// Processing utils.
217
218function trim(value)
219{
220    return value.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
221}
222
223function isShort(value)
224{
225    return trim(value).length <= 50;
226}
227
228// Tree rendering.
229
230function createHTMLElement(elementName)
231{
232    return document.createElementNS('http://www.w3.org/1999/xhtml', elementName)
233}
234
235function createCollapsible()
236{
237    var collapsible = createHTMLElement('div');
238    collapsible.classList.add('collapsible');
239    collapsible.expanded = createHTMLElement('div');
240    collapsible.expanded.classList.add('expanded');
241    collapsible.appendChild(collapsible.expanded);
242
243    collapsible.expanded.start = createLine();
244    collapsible.expanded.start.appendChild(createCollapseButton());
245    collapsible.expanded.appendChild(collapsible.expanded.start);
246
247    collapsible.expanded.content = createHTMLElement('div');
248    collapsible.expanded.content.classList.add('collapsible-content');
249    collapsible.expanded.appendChild(collapsible.expanded.content);
250
251    collapsible.expanded.end = createLine();
252    collapsible.expanded.appendChild(collapsible.expanded.end);
253
254    collapsible.collapsed = createHTMLElement('div');
255    collapsible.collapsed.classList.add('collapsed');
256    collapsible.collapsed.classList.add('hidden');
257    collapsible.appendChild(collapsible.collapsed);
258    collapsible.collapsed.content = createLine();
259    collapsible.collapsed.content.appendChild(createExpandButton());
260    collapsible.collapsed.appendChild(collapsible.collapsed.content);
261
262    return collapsible;
263}
264
265function createButton()
266{
267    var button = createHTMLElement('span');
268    button.classList.add('button');
269    return button;
270}
271
272function createCollapseButton(str)
273{
274    var button = createButton();
275    button.classList.add('collapse-button');
276    return button;
277}
278
279function createExpandButton(str)
280{
281    var button = createButton();
282    button.classList.add('expand-button');
283    return button;
284}
285
286function createComment(commentString)
287{
288    var comment = createHTMLElement('span');
289    comment.classList.add('comment');
290    comment.classList.add('webkit-html-comment');
291    comment.textContent = commentString;
292    return comment;
293}
294
295function createText(value)
296{
297    var text = createHTMLElement('span');
298    text.textContent = trim(value);
299    text.classList.add('text');
300    return text;
301}
302
303function createLine()
304{
305    var line = createHTMLElement('div');
306    line.classList.add('line');
307    return line;
308}
309
310function createTag(node, isClosing, isEmpty)
311{
312    var tag = createHTMLElement('span');
313    tag.classList.add('webkit-html-tag');
314
315    var stringBeforeAttrs = '<';
316    if (isClosing)
317        stringBeforeAttrs += '/';
318    stringBeforeAttrs += node.nodeName;
319    var textBeforeAttrs = document.createTextNode(stringBeforeAttrs);
320    tag.appendChild(textBeforeAttrs);
321
322    if (!isClosing) {
323        for (var i = 0; i < node.attributes.length; i++)
324            tag.appendChild(createAttribute(node.attributes[i]));
325    }
326
327    var stringAfterAttrs = '';
328    if (isEmpty)
329        stringAfterAttrs += '/';
330    stringAfterAttrs += '>';
331    var textAfterAttrs = document.createTextNode(stringAfterAttrs);
332    tag.appendChild(textAfterAttrs);
333
334    return tag;
335}
336
337function createAttribute(attributeNode)
338{
339    var attribute = createHTMLElement('span');
340    attribute.classList.add('webkit-html-attribute');
341
342    var attributeName = createHTMLElement('span');
343    attributeName.classList.add('webkit-html-attribute-name');
344    attributeName.textContent = attributeNode.name;
345
346    var textBefore = document.createTextNode(' ');
347    var textBetween = document.createTextNode('="');
348
349    var attributeValue = createHTMLElement('span');
350    attributeValue.classList.add('webkit-html-attribute-value');
351    attributeValue.textContent = attributeNode.value;
352
353    var textAfter = document.createTextNode('"');
354
355    attribute.appendChild(textBefore);
356    attribute.appendChild(attributeName);
357    attribute.appendChild(textBetween);
358    attribute.appendChild(attributeValue);
359    attribute.appendChild(textAfter);
360    return attribute;
361}
362
363// Tree behaviour.
364
365function drawArrows()
366{
367    var ctx = document.getCSSCanvasContext("2d", "arrowRight", 10, 11);
368
369    ctx.fillStyle = "rgb(90,90,90)";
370    ctx.beginPath();
371    ctx.moveTo(0, 0);
372    ctx.lineTo(0, 8);
373    ctx.lineTo(7, 4);
374    ctx.lineTo(0, 0);
375    ctx.fill();
376    ctx.closePath();
377
378    var ctx = document.getCSSCanvasContext("2d", "arrowDown", 10, 10);
379
380    ctx.fillStyle = "rgb(90,90,90)";
381    ctx.beginPath();
382    ctx.moveTo(0, 0);
383    ctx.lineTo(8, 0);
384    ctx.lineTo(4, 7);
385    ctx.lineTo(0, 0);
386    ctx.fill();
387    ctx.closePath();
388}
389
390function expandFunction(sectionId)
391{
392    return function()
393    {
394        document.querySelector('#' + sectionId + ' > .expanded').className = 'expanded';
395        document.querySelector('#' + sectionId + ' > .collapsed').className = 'collapsed hidden';
396    };
397}
398
399function collapseFunction(sectionId)
400{
401    return function()
402    {
403        document.querySelector('#' + sectionId + ' > .expanded').className = 'expanded hidden';
404        document.querySelector('#' + sectionId + ' > .collapsed').className = 'collapsed';
405    };
406}
407
408function initButtons()
409{
410    var sections = document.querySelectorAll('.collapsible');
411    for (var i = 0; i < sections.length; i++) {
412        var sectionId = 'collapsible' + i;
413        sections[i].id = sectionId;
414
415        var expandedPart = sections[i].querySelector('#' + sectionId + ' > .expanded');
416        var collapseButton = expandedPart.querySelector('.collapse-button');
417        collapseButton.onclick = collapseFunction(sectionId);
418        collapseButton.onmousedown = handleButtonMouseDown;
419
420        var collapsedPart = sections[i].querySelector('#' + sectionId + ' > .collapsed');
421        var expandButton = collapsedPart.querySelector('.expand-button');
422        expandButton.onclick = expandFunction(sectionId);
423        expandButton.onmousedown = handleButtonMouseDown;
424    }
425
426}
427
428function handleButtonMouseDown(e)
429{
430   // To prevent selection on double click
431   e.preventDefault();
432}
433