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.
7 installClass("Document", function(DocumentPrototype) {
8 // FIXME: The stylesheet should be defined in a separate css file
11 " border-bottom: 2px solid black;",
12 " padding-bottom: 5px;",
16 "div.collapsible > div.hidden {",
22 " margin-left: 20px;",
23 " font-family: monospace;",
27 "#webkit-xml-viewer-source-xml {",
31 ".collapsible-content {",
39 " -webkit-user-select: none;",
41 " display: inline-block;",
42 " margin-left: -10px;",
44 " background-repeat: no-repeat;",
45 " background-position: left top;",
46 " vertical-align: bottom;",
50 " background-image: -webkit-canvas(arrowDown);",
55 " background-image: -webkit-canvas(arrowRight);",
58 var nodeParentPairs = [];
61 function prepareWebKitXMLViewer(noStyleMessage)
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);
77 while (child = document.firstChild) {
78 document.removeChild(child);
79 if (child.nodeType != Node.DOCUMENT_TYPE_NODE)
80 sourceXML.appendChild(child);
82 document.appendChild(html);
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'));
92 tree = createHTMLElement('div');
93 body.appendChild(tree);
94 tree.classList.add('pretty-print');
95 window.onload = sourceXMLLoaded;
98 function sourceXMLLoaded()
100 var sourceXML = document.getElementById('webkit-xml-viewer-source-xml');
102 return; // Stop if some XML tree extension is already processing this document
104 for (var child = sourceXML.firstChild; child; child = child.nextSibling)
105 nodeParentPairs.push({parentElement: tree, node: child});
107 for (var i = 0; i < nodeParentPairs.length; i++)
108 processNode(nodeParentPairs[i].parentElement, nodeParentPairs[i].node);
118 function processNode(parentElement, node)
120 var map = processNode.processorsMap;
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;
130 if (processNode.processorsMap[node.nodeType])
131 processNode.processorsMap[node.nodeType].call(this, parentElement, node);
134 function processElement(parentElement, node)
136 if (!node.firstChild)
137 processEmptyElement(parentElement, node);
139 var child = node.firstChild;
140 if (child.nodeType == Node.TEXT_NODE && isShort(child.nodeValue) && !child.nextSibling)
141 processShortTextOnlyElement(parentElement, node);
143 processComplexElement(parentElement, node);
147 function processEmptyElement(parentElement, node)
149 var line = createLine();
150 line.appendChild(createTag(node, false, true));
151 parentElement.appendChild(line);
154 function processShortTextOnlyElement(parentElement, node)
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);
164 function processComplexElement(parentElement, node)
166 var collapsible = createCollapsible();
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));
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);
179 function processComment(parentElement, node)
181 if (isShort(node.nodeValue)) {
182 var line = createLine();
183 line.appendChild(createComment('<!-- ' + node.nodeValue + ' -->'));
184 parentElement.appendChild(line);
186 var collapsible = createCollapsible();
188 collapsible.expanded.start.appendChild(createComment('<!--'));
189 collapsible.expanded.content.appendChild(createComment(node.nodeValue));
190 collapsible.expanded.end.appendChild(createComment('-->'));
192 collapsible.collapsed.content.appendChild(createComment('<!--'));
193 collapsible.collapsed.content.appendChild(createComment('...'));
194 collapsible.collapsed.content.appendChild(createComment('-->'));
195 parentElement.appendChild(collapsible);
199 function processCDATA(parentElement, node)
201 if (isShort(node.nodeValue)) {
202 var line = createLine();
203 line.appendChild(createText('<![CDATA[ ' + node.nodeValue + ' ]]>'));
204 parentElement.appendChild(line);
206 var collapsible = createCollapsible();
208 collapsible.expanded.start.appendChild(createText('<![CDATA['));
209 collapsible.expanded.content.appendChild(createText(node.nodeValue));
210 collapsible.expanded.end.appendChild(createText(']]>'));
212 collapsible.collapsed.content.appendChild(createText('<![CDATA['));
213 collapsible.collapsed.content.appendChild(createText('...'));
214 collapsible.collapsed.content.appendChild(createText(']]>'));
215 parentElement.appendChild(collapsible);
219 function processProcessingInstruction(parentElement, node)
221 if (isShort(node.nodeValue)) {
222 var line = createLine();
223 line.appendChild(createComment('<?' + node.nodeName + ' ' + node.nodeValue + '?>'));
224 parentElement.appendChild(line);
226 var collapsible = createCollapsible();
228 collapsible.expanded.start.appendChild(createComment('<?' + node.nodeName));
229 collapsible.expanded.content.appendChild(createComment(node.nodeValue));
230 collapsible.expanded.end.appendChild(createComment('?>'));
232 collapsible.collapsed.content.appendChild(createComment('<?' + node.nodeName));
233 collapsible.collapsed.content.appendChild(createComment('...'));
234 collapsible.collapsed.content.appendChild(createComment('?>'));
235 parentElement.appendChild(collapsible);
239 function processText(parentElement, node)
241 parentElement.appendChild(createText(node.nodeValue));
248 return value.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
251 function isShort(value)
253 return trim(value).length <= 50;
258 function createHTMLElement(elementName)
260 return document.createElementNS('http://www.w3.org/1999/xhtml', elementName)
263 function createCollapsible()
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);
271 collapsible.expanded.start = createLine();
272 collapsible.expanded.start.appendChild(createCollapseButton());
273 collapsible.expanded.appendChild(collapsible.expanded.start);
275 collapsible.expanded.content = createHTMLElement('div');
276 collapsible.expanded.content.classList.add('collapsible-content');
277 collapsible.expanded.appendChild(collapsible.expanded.content);
279 collapsible.expanded.end = createLine();
280 collapsible.expanded.appendChild(collapsible.expanded.end);
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);
293 function createButton()
295 var button = createHTMLElement('span');
296 button.classList.add('button');
300 function createCollapseButton(str)
302 var button = createButton();
303 button.classList.add('collapse-button');
307 function createExpandButton(str)
309 var button = createButton();
310 button.classList.add('expand-button');
314 function createComment(commentString)
316 var comment = createHTMLElement('span');
317 comment.classList.add('comment');
318 comment.classList.add('webkit-html-comment');
319 comment.textContent = commentString;
323 function createText(value)
325 var text = createHTMLElement('span');
326 text.textContent = trim(value);
327 text.classList.add('text');
331 function createLine()
333 var line = createHTMLElement('div');
334 line.classList.add('line');
338 function createTag(node, isClosing, isEmpty)
340 var tag = createHTMLElement('span');
341 tag.classList.add('webkit-html-tag');
343 var stringBeforeAttrs = '<';
345 stringBeforeAttrs += '/';
346 stringBeforeAttrs += node.nodeName;
347 var textBeforeAttrs = document.createTextNode(stringBeforeAttrs);
348 tag.appendChild(textBeforeAttrs);
351 for (var i = 0; i < node.attributes.length; i++)
352 tag.appendChild(createAttribute(node.attributes[i]));
355 var stringAfterAttrs = '';
357 stringAfterAttrs += '/';
358 stringAfterAttrs += '>';
359 var textAfterAttrs = document.createTextNode(stringAfterAttrs);
360 tag.appendChild(textAfterAttrs);
365 function createAttribute(attributeNode)
367 var attribute = createHTMLElement('span');
368 attribute.classList.add('webkit-html-attribute');
370 var attributeName = createHTMLElement('span');
371 attributeName.classList.add('webkit-html-attribute-name');
372 attributeName.textContent = attributeNode.name;
374 var textBefore = document.createTextNode(' ');
375 var textBetween = document.createTextNode('="');
377 var attributeValue = createHTMLElement('span');
378 attributeValue.classList.add('webkit-html-attribute-value');
379 attributeValue.textContent = attributeNode.value;
381 var textAfter = document.createTextNode('"');
383 attribute.appendChild(textBefore);
384 attribute.appendChild(attributeName);
385 attribute.appendChild(textBetween);
386 attribute.appendChild(attributeValue);
387 attribute.appendChild(textAfter);
393 function drawArrows()
395 var ctx = document.getCSSCanvasContext("2d", "arrowRight", 10, 11);
397 ctx.fillStyle = "rgb(90,90,90)";
406 var ctx = document.getCSSCanvasContext("2d", "arrowDown", 10, 10);
408 ctx.fillStyle = "rgb(90,90,90)";
418 function expandFunction(sectionId)
422 document.querySelector('#' + sectionId + ' > .expanded').className = 'expanded';
423 document.querySelector('#' + sectionId + ' > .collapsed').className = 'collapsed hidden';
427 function collapseFunction(sectionId)
431 document.querySelector('#' + sectionId + ' > .expanded').className = 'expanded hidden';
432 document.querySelector('#' + sectionId + ' > .collapsed').className = 'collapsed';
436 function initButtons()
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;
443 var expandedPart = sections[i].querySelector('#' + sectionId + ' > .expanded');
444 var collapseButton = expandedPart.querySelector('.collapse-button');
445 collapseButton.onclick = collapseFunction(sectionId);
446 collapseButton.onmousedown = handleButtonMouseDown;
448 var collapsedPart = sections[i].querySelector('#' + sectionId + ' > .collapsed');
449 var expandButton = collapsedPart.querySelector('.expand-button');
450 expandButton.onclick = expandFunction(sectionId);
451 expandButton.onmousedown = handleButtonMouseDown;
456 function handleButtonMouseDown(e)
458 // To prevent selection on double click
462 DocumentPrototype.transformDocumentToTreeView = function(noStyleMessage) {
463 prepareWebKitXMLViewer(noStyleMessage);