dfc1787ded5bd80c291fcb5248ffbd636eb06635
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / xml / DocumentXMLTreeViewer.js
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 "use strict";
6
7 installClass("Document", function(DocumentPrototype) {
8     // FIXME: The stylesheet should be defined in a separate css file
9     var styleSheet = [
10         "div.header {",
11         "    border-bottom: 2px solid black;",
12         "    padding-bottom: 5px;",
13         "    margin: 10px;",
14         "}",
15         "",
16         "div.collapsible > div.hidden {",
17         "    display:none;",
18         "}",
19         "",
20         ".pretty-print {",
21         "    margin-top: 1em;",
22         "    margin-left: 20px;",
23         "    font-family: monospace;",
24         "    font-size: 13px;",
25         "}",
26         "",
27         "#webkit-xml-viewer-source-xml {",
28         "    display: none;",
29         "}",
30         "",
31         ".collapsible-content {",
32         "    margin-left: 1em;",
33         "}",
34         ".comment {",
35         "    white-space: pre;",
36         "}",
37         "",
38         ".button {",
39         "    -webkit-user-select: none;",
40         "    cursor: pointer;",
41         "    display: inline-block;",
42         "    margin-left: -10px;",
43         "    width: 10px;",
44         "    background-repeat: no-repeat;",
45         "    background-position: left top;",
46         "    vertical-align: bottom;",
47         "}",
48         "",
49         ".collapse-button {",
50         "    background-image: -webkit-canvas(arrowDown);",
51         "    height: 10px;",
52         "}",
53         "",
54         ".expand-button {",
55         "    background-image: -webkit-canvas(arrowRight);",
56         "    height: 11px;",
57         "}"].join('');
58     var nodeParentPairs = [];
59     var tree;
60
61     function prepareWebKitXMLViewer(noStyleMessage)
62     {
63         var html = createHTMLElement('html');
64         var head = createHTMLElement('head');
65         html.appendChild(head);
66         var style = createHTMLElement('style');
67         style.id = 'xml-viewer-style';
68         style.appendChild(document.createTextNode(styleSheet));
69         head.appendChild(style);
70         var body = createHTMLElement('body');
71         html.appendChild(body);
72         var sourceXML = createHTMLElement('div');
73         sourceXML.id = 'webkit-xml-viewer-source-xml';
74         body.appendChild(sourceXML);
75
76         var child;
77         while (child = document.firstChild) {
78             document.removeChild(child);
79             if (child.nodeType != Node.DOCUMENT_TYPE_NODE)
80                 sourceXML.appendChild(child);
81         }
82         document.appendChild(html);
83
84         var header = createHTMLElement('div');
85         body.appendChild(header);
86         header.classList.add('header');
87         var headerSpan = createHTMLElement('span');
88         header.appendChild(headerSpan);
89         headerSpan.textContent = noStyleMessage;
90         header.appendChild(createHTMLElement('br'));
91
92         tree = createHTMLElement('div');
93         body.appendChild(tree);
94         tree.classList.add('pretty-print');
95         window.onload = sourceXMLLoaded;
96     }
97
98     function sourceXMLLoaded()
99     {
100         var sourceXML = document.getElementById('webkit-xml-viewer-source-xml');
101         if (!sourceXML)
102             return; // Stop if some XML tree extension is already processing this document
103
104         for (var child = sourceXML.firstChild; child; child = child.nextSibling)
105             nodeParentPairs.push({parentElement: tree, node: child});
106
107         for (var i = 0; i < nodeParentPairs.length; i++)
108             processNode(nodeParentPairs[i].parentElement, nodeParentPairs[i].node);
109
110         drawArrows();
111         initButtons();
112
113         return false;
114     }
115
116     // Tree processing.
117
118     function processNode(parentElement, node)
119     {
120         var map = processNode.processorsMap;
121         if (!map) {
122             map = {};
123             processNode.processorsMap = map;
124             map[Node.PROCESSING_INSTRUCTION_NODE] = processProcessingInstruction;
125             map[Node.ELEMENT_NODE] = processElement;
126             map[Node.COMMENT_NODE] = processComment;
127             map[Node.TEXT_NODE] = processText;
128             map[Node.CDATA_SECTION_NODE] = processCDATA;
129         }
130         if (processNode.processorsMap[node.nodeType])
131             processNode.processorsMap[node.nodeType].call(this, parentElement, node);
132     }
133
134     function processElement(parentElement, node)
135     {
136         if (!node.firstChild)
137             processEmptyElement(parentElement, node);
138         else {
139             var child = node.firstChild;
140             if (child.nodeType == Node.TEXT_NODE && isShort(child.nodeValue) && !child.nextSibling)
141                 processShortTextOnlyElement(parentElement, node);
142             else
143                 processComplexElement(parentElement, node);
144         }
145     }
146
147     function processEmptyElement(parentElement, node)
148     {
149         var line = createLine();
150         line.appendChild(createTag(node, false, true));
151         parentElement.appendChild(line);
152     }
153
154     function processShortTextOnlyElement(parentElement, node)
155     {
156         var line = createLine();
157         line.appendChild(createTag(node, false, false));
158         for (var child = node.firstChild; child; child = child.nextSibling)
159             line.appendChild(createText(child.nodeValue));
160         line.appendChild(createTag(node, true, false));
161         parentElement.appendChild(line);
162     }
163
164     function processComplexElement(parentElement, node)
165     {
166         var collapsible = createCollapsible();
167
168         collapsible.expanded.start.appendChild(createTag(node, false, false));
169         for (var child = node.firstChild; child; child = child.nextSibling)
170             nodeParentPairs.push({parentElement: collapsible.expanded.content, node: child});
171         collapsible.expanded.end.appendChild(createTag(node, true, false));
172
173         collapsible.collapsed.content.appendChild(createTag(node, false, false));
174         collapsible.collapsed.content.appendChild(createText('...'));
175         collapsible.collapsed.content.appendChild(createTag(node, true, false));
176         parentElement.appendChild(collapsible);
177     }
178
179     function processComment(parentElement, node)
180     {
181         if (isShort(node.nodeValue)) {
182             var line = createLine();
183             line.appendChild(createComment('<!-- ' + node.nodeValue + ' -->'));
184             parentElement.appendChild(line);
185         } else {
186             var collapsible = createCollapsible();
187
188             collapsible.expanded.start.appendChild(createComment('<!--'));
189             collapsible.expanded.content.appendChild(createComment(node.nodeValue));
190             collapsible.expanded.end.appendChild(createComment('-->'));
191
192             collapsible.collapsed.content.appendChild(createComment('<!--'));
193             collapsible.collapsed.content.appendChild(createComment('...'));
194             collapsible.collapsed.content.appendChild(createComment('-->'));
195             parentElement.appendChild(collapsible);
196         }
197     }
198
199     function processCDATA(parentElement, node)
200     {
201         if (isShort(node.nodeValue)) {
202             var line = createLine();
203             line.appendChild(createText('<![CDATA[ ' + node.nodeValue + ' ]]>'));
204             parentElement.appendChild(line);
205         } else {
206             var collapsible = createCollapsible();
207
208             collapsible.expanded.start.appendChild(createText('<![CDATA['));
209             collapsible.expanded.content.appendChild(createText(node.nodeValue));
210             collapsible.expanded.end.appendChild(createText(']]>'));
211
212             collapsible.collapsed.content.appendChild(createText('<![CDATA['));
213             collapsible.collapsed.content.appendChild(createText('...'));
214             collapsible.collapsed.content.appendChild(createText(']]>'));
215             parentElement.appendChild(collapsible);
216         }
217     }
218
219     function processProcessingInstruction(parentElement, node)
220     {
221         if (isShort(node.nodeValue)) {
222             var line = createLine();
223             line.appendChild(createComment('<?' + node.nodeName + ' ' + node.nodeValue + '?>'));
224             parentElement.appendChild(line);
225         } else {
226             var collapsible = createCollapsible();
227
228             collapsible.expanded.start.appendChild(createComment('<?' + node.nodeName));
229             collapsible.expanded.content.appendChild(createComment(node.nodeValue));
230             collapsible.expanded.end.appendChild(createComment('?>'));
231
232             collapsible.collapsed.content.appendChild(createComment('<?' + node.nodeName));
233             collapsible.collapsed.content.appendChild(createComment('...'));
234             collapsible.collapsed.content.appendChild(createComment('?>'));
235             parentElement.appendChild(collapsible);
236         }
237     }
238
239     function processText(parentElement, node)
240     {
241         parentElement.appendChild(createText(node.nodeValue));
242     }
243
244     // Processing utils.
245
246     function trim(value)
247     {
248         return value.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
249     }
250
251     function isShort(value)
252     {
253         return trim(value).length <= 50;
254     }
255
256     // Tree rendering.
257
258     function createHTMLElement(elementName)
259     {
260         return document.createElementNS('http://www.w3.org/1999/xhtml', elementName)
261     }
262
263     function createCollapsible()
264     {
265         var collapsible = createHTMLElement('div');
266         collapsible.classList.add('collapsible');
267         collapsible.expanded = createHTMLElement('div');
268         collapsible.expanded.classList.add('expanded');
269         collapsible.appendChild(collapsible.expanded);
270
271         collapsible.expanded.start = createLine();
272         collapsible.expanded.start.appendChild(createCollapseButton());
273         collapsible.expanded.appendChild(collapsible.expanded.start);
274
275         collapsible.expanded.content = createHTMLElement('div');
276         collapsible.expanded.content.classList.add('collapsible-content');
277         collapsible.expanded.appendChild(collapsible.expanded.content);
278
279         collapsible.expanded.end = createLine();
280         collapsible.expanded.appendChild(collapsible.expanded.end);
281
282         collapsible.collapsed = createHTMLElement('div');
283         collapsible.collapsed.classList.add('collapsed');
284         collapsible.collapsed.classList.add('hidden');
285         collapsible.appendChild(collapsible.collapsed);
286         collapsible.collapsed.content = createLine();
287         collapsible.collapsed.content.appendChild(createExpandButton());
288         collapsible.collapsed.appendChild(collapsible.collapsed.content);
289
290         return collapsible;
291     }
292
293     function createButton()
294     {
295         var button = createHTMLElement('span');
296         button.classList.add('button');
297         return button;
298     }
299
300     function createCollapseButton(str)
301     {
302         var button = createButton();
303         button.classList.add('collapse-button');
304         return button;
305     }
306
307     function createExpandButton(str)
308     {
309         var button = createButton();
310         button.classList.add('expand-button');
311         return button;
312     }
313
314     function createComment(commentString)
315     {
316         var comment = createHTMLElement('span');
317         comment.classList.add('comment');
318         comment.classList.add('webkit-html-comment');
319         comment.textContent = commentString;
320         return comment;
321     }
322
323     function createText(value)
324     {
325         var text = createHTMLElement('span');
326         text.textContent = trim(value);
327         text.classList.add('text');
328         return text;
329     }
330
331     function createLine()
332     {
333         var line = createHTMLElement('div');
334         line.classList.add('line');
335         return line;
336     }
337
338     function createTag(node, isClosing, isEmpty)
339     {
340         var tag = createHTMLElement('span');
341         tag.classList.add('webkit-html-tag');
342
343         var stringBeforeAttrs = '<';
344         if (isClosing)
345             stringBeforeAttrs += '/';
346         stringBeforeAttrs += node.nodeName;
347         var textBeforeAttrs = document.createTextNode(stringBeforeAttrs);
348         tag.appendChild(textBeforeAttrs);
349
350         if (!isClosing) {
351             for (var i = 0; i < node.attributes.length; i++)
352                 tag.appendChild(createAttribute(node.attributes[i]));
353         }
354
355         var stringAfterAttrs = '';
356         if (isEmpty)
357             stringAfterAttrs += '/';
358         stringAfterAttrs += '>';
359         var textAfterAttrs = document.createTextNode(stringAfterAttrs);
360         tag.appendChild(textAfterAttrs);
361
362         return tag;
363     }
364
365     function createAttribute(attributeNode)
366     {
367         var attribute = createHTMLElement('span');
368         attribute.classList.add('webkit-html-attribute');
369
370         var attributeName = createHTMLElement('span');
371         attributeName.classList.add('webkit-html-attribute-name');
372         attributeName.textContent = attributeNode.name;
373
374         var textBefore = document.createTextNode(' ');
375         var textBetween = document.createTextNode('="');
376
377         var attributeValue = createHTMLElement('span');
378         attributeValue.classList.add('webkit-html-attribute-value');
379         attributeValue.textContent = attributeNode.value;
380
381         var textAfter = document.createTextNode('"');
382
383         attribute.appendChild(textBefore);
384         attribute.appendChild(attributeName);
385         attribute.appendChild(textBetween);
386         attribute.appendChild(attributeValue);
387         attribute.appendChild(textAfter);
388         return attribute;
389     }
390
391     // Tree behaviour.
392
393     function drawArrows()
394     {
395         var ctx = document.getCSSCanvasContext("2d", "arrowRight", 10, 11);
396
397         ctx.fillStyle = "rgb(90,90,90)";
398         ctx.beginPath();
399         ctx.moveTo(0, 0);
400         ctx.lineTo(0, 8);
401         ctx.lineTo(7, 4);
402         ctx.lineTo(0, 0);
403         ctx.fill();
404         ctx.closePath();
405
406         var ctx = document.getCSSCanvasContext("2d", "arrowDown", 10, 10);
407
408         ctx.fillStyle = "rgb(90,90,90)";
409         ctx.beginPath();
410         ctx.moveTo(0, 0);
411         ctx.lineTo(8, 0);
412         ctx.lineTo(4, 7);
413         ctx.lineTo(0, 0);
414         ctx.fill();
415         ctx.closePath();
416     }
417
418     function expandFunction(sectionId)
419     {
420         return function()
421         {
422             document.querySelector('#' + sectionId + ' > .expanded').className = 'expanded';
423             document.querySelector('#' + sectionId + ' > .collapsed').className = 'collapsed hidden';
424         };
425     }
426
427     function collapseFunction(sectionId)
428     {
429         return function()
430         {
431             document.querySelector('#' + sectionId + ' > .expanded').className = 'expanded hidden';
432             document.querySelector('#' + sectionId + ' > .collapsed').className = 'collapsed';
433         };
434     }
435
436     function initButtons()
437     {
438         var sections = document.querySelectorAll('.collapsible');
439         for (var i = 0; i < sections.length; i++) {
440             var sectionId = 'collapsible' + i;
441             sections[i].id = sectionId;
442
443             var expandedPart = sections[i].querySelector('#' + sectionId + ' > .expanded');
444             var collapseButton = expandedPart.querySelector('.collapse-button');
445             collapseButton.onclick = collapseFunction(sectionId);
446             collapseButton.onmousedown = handleButtonMouseDown;
447
448             var collapsedPart = sections[i].querySelector('#' + sectionId + ' > .collapsed');
449             var expandButton = collapsedPart.querySelector('.expand-button');
450             expandButton.onclick = expandFunction(sectionId);
451             expandButton.onmousedown = handleButtonMouseDown;
452         }
453
454     }
455
456     function handleButtonMouseDown(e)
457     {
458        // To prevent selection on double click
459        e.preventDefault();
460     }
461
462     DocumentPrototype.transformDocumentToTreeView = function(noStyleMessage) {
463         prepareWebKitXMLViewer(noStyleMessage);
464     }
465 });
466