2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
3 * Copyright (C) 2007 Matt Lilek (pewtermoose@gmail.com).
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 ignoreWhitespace: true,
32 showUserAgentStyles: true,
33 maxInlineTextChildLength: 80,
34 maxTextSearchResultLength: 80,
35 showInheritedComputedStyleProperties: false,
36 showMissingLocalizedStrings: false,
44 searchResultsHeight: 100,
46 missingLocalizedStrings: {},
50 if (!this._consolePanel)
51 this._consolePanel = new WebInspector.ConsolePanel();
53 return this._consolePanel;
58 if (!this._networkPanel)
59 this._networkPanel = new WebInspector.NetworkPanel();
61 return this._networkPanel;
64 get currentBackForwardIndex()
66 if (this._currentBackForwardIndex === undefined)
67 this._currentBackForwardIndex = -1;
69 return this._currentBackForwardIndex;
72 set currentBackForwardIndex(x)
74 if (this._currentBackForwardIndex === x)
77 this._currentBackForwardIndex = x;
78 this.updateBackForwardButtons();
81 get currentFocusElement()
83 return this._currentFocusElement;
86 set currentFocusElement(x)
88 if (!x || this._currentFocusElement === x)
91 if (this._currentFocusElement) {
92 this._currentFocusElement.removeStyleClass("focused");
93 this._currentFocusElement.addStyleClass("blurred");
94 if (this._currentFocusElement.blurred)
95 this._currentFocusElement.blurred();
98 this._currentFocusElement = x;
101 x.addStyleClass("focused");
102 x.removeStyleClass("blurred");
103 if (this._currentFocusElement.focused)
104 this._currentFocusElement.focused();
110 return this._currentPanel;
115 if (this._currentPanel === x)
118 if (this._currentPanel)
119 this._currentPanel.hide();
121 this._currentPanel = x;
129 return this._attached;
134 if (this._attached === x)
139 var body = document.body;
141 InspectorController.attach();
142 body.removeStyleClass("detached");
143 body.addStyleClass("attached");
145 InspectorController.detach();
146 body.removeStyleClass("attached");
147 body.addStyleClass("detached");
151 get showingStatusArea()
153 return this._showingStatusArea;
156 set showingStatusArea(x)
158 if (this._showingStatusArea === x)
161 this._showingStatusArea = x;
163 var list = document.getElementById("list");
164 var status = document.getElementById("status");
165 var statusButton = document.getElementById("statusToggle");
168 statusButton.addStyleClass("hide");
169 list.addStyleClass("status-visible");
170 status.addStyleClass("visible");
172 statusButton.removeStyleClass("hide");
173 list.removeStyleClass("status-visible");
174 status.removeStyleClass("visible");
178 get showingSearchResults()
180 return this._showingSearchResults;
183 set showingSearchResults(x)
185 if (this._showingSearchResults === x)
188 this._showingSearchResults = x;
190 var resultsContainer = document.getElementById("searchResults");
191 var searchResultsResizer = document.getElementById("searchResultsResizer");
194 searchResultsResizer.style.display = null;
196 {element: resultsContainer, end: {top: Preferences.toolbarHeight}},
197 {element: searchResultsResizer, end: {top: WebInspector.searchResultsHeight + Preferences.toolbarHeight - 2}},
198 {element: document.getElementById("main"), end: {top: WebInspector.searchResultsHeight + Preferences.toolbarHeight + 1}}
200 WebInspector.animateStyle(animations, 250);
202 searchResultsResizer.style.display = "none";
204 {element: resultsContainer, end: {top: Preferences.toolbarHeight - WebInspector.searchResultsHeight - 1}},
205 {element: searchResultsResizer, end: {top: 0}},
206 {element: document.getElementById("main"), end: {top: Preferences.toolbarHeight}}
208 WebInspector.animateStyle(animations, 250, function() { resultsContainer.removeChildren(); delete this.searchResultsTree; });
213 WebInspector.loaded = function()
215 this.fileOutline = new TreeOutline(document.getElementById("list"));
216 this.fileOutline.expandTreeElementsWhenArrowing = true;
218 this.statusOutline = new TreeOutline(document.getElementById("status"));
219 this.statusOutline.expandTreeElementsWhenArrowing = true;
221 this.resourceCategories = {
222 documents: new WebInspector.ResourceCategory(WebInspector.UIString("documents"), "documents"),
223 stylesheets: new WebInspector.ResourceCategory(WebInspector.UIString("stylesheets"), "stylesheets"),
224 images: new WebInspector.ResourceCategory(WebInspector.UIString("images"), "images"),
225 scripts: new WebInspector.ResourceCategory(WebInspector.UIString("scripts"), "scripts"),
226 fonts: new WebInspector.ResourceCategory(WebInspector.UIString("fonts"), "fonts"),
227 databases: new WebInspector.ResourceCategory(WebInspector.UIString("databases"), "databases"),
228 other: new WebInspector.ResourceCategory(WebInspector.UIString("other"), "other")
232 ResourceNotCompressed: {id: 0, message: WebInspector.UIString("You could save bandwidth by having your web server compress this transfer with gzip or zlib.")}
236 IncorrectMIMEType: {id: 0, message: WebInspector.UIString("Resource interpreted as %s but transferred with MIME type %s.")}
239 this.consoleListItem = new WebInspector.ConsoleStatusTreeElement(WebInspector.consolePanel);
240 this.statusOutline.appendChild(this.consoleListItem);
242 this.networkListItem = new WebInspector.StatusTreeElement(WebInspector.UIString("Network"), "network", WebInspector.networkPanel);
243 this.statusOutline.appendChild(this.networkListItem);
245 this.resourceCategories.documents.listItem.expand();
247 this.currentFocusElement = document.getElementById("sidebar");
249 this.addMainEventListeners(document);
251 window.addEventListener("unload", this.windowUnload.bind(this), true);
252 window.addEventListener("resize", this.windowResize.bind(this), true);
254 document.addEventListener("mousedown", this.changeFocus.bind(this), true);
255 document.addEventListener("focus", this.changeFocus.bind(this), true);
256 document.addEventListener("keypress", this.documentKeypress.bind(this), true);
257 document.addEventListener("beforecopy", this.documentCanCopy.bind(this), true);
258 document.addEventListener("copy", this.documentCopy.bind(this), true);
260 document.getElementById("back").title = WebInspector.UIString("Show previous panel.");
261 document.getElementById("forward").title = WebInspector.UIString("Show next panel.");
263 document.getElementById("search").setAttribute("placeholder", WebInspector.UIString("Search"));
265 document.getElementById("back").addEventListener("click", this.back.bind(this), true);
266 document.getElementById("forward").addEventListener("click", this.forward.bind(this), true);
267 this.updateBackForwardButtons();
269 document.getElementById("attachToggle").addEventListener("click", this.toggleAttach.bind(this), true);
270 document.getElementById("statusToggle").addEventListener("click", this.toggleStatusArea.bind(this), true);
272 document.getElementById("sidebarResizeWidget").addEventListener("mousedown", this.sidebarResizerDragStart, true);
273 document.getElementById("sidebarResizer").addEventListener("mousedown", this.sidebarResizerDragStart, true);
274 document.getElementById("searchResultsResizer").addEventListener("mousedown", this.searchResultsResizerDragStart, true);
276 document.body.addStyleClass("detached");
278 InspectorController.loaded();
281 var windowLoaded = function()
283 var localizedStringsURL = InspectorController.localizedStringsURL();
284 if (localizedStringsURL) {
285 var localizedStringsScriptElement = document.createElement("script");
286 localizedStringsScriptElement.addEventListener("load", WebInspector.loaded.bind(WebInspector), false);
287 localizedStringsScriptElement.type = "text/javascript";
288 localizedStringsScriptElement.src = localizedStringsURL;
289 document.getElementsByTagName("head").item(0).appendChild(localizedStringsScriptElement);
291 WebInspector.loaded();
294 window.removeEventListener("load", windowLoaded, false);
297 window.addEventListener("load", windowLoaded, false);
299 WebInspector.windowUnload = function(event)
301 InspectorController.windowUnloading();
304 WebInspector.windowResize = function(event)
306 if (this.currentPanel && this.currentPanel.resize)
307 this.currentPanel.resize();
310 WebInspector.windowFocused = function(event)
312 if (event.target.nodeType === Node.DOCUMENT_NODE)
313 document.body.removeStyleClass("inactive");
316 WebInspector.windowBlured = function(event)
318 if (event.target.nodeType === Node.DOCUMENT_NODE)
319 document.body.addStyleClass("inactive");
322 WebInspector.changeFocus = function(event)
324 var nextFocusElement;
326 var current = event.target;
328 if (current.nodeName.toLowerCase() === "input")
329 nextFocusElement = current;
330 current = current.parentNode;
333 if (!nextFocusElement)
334 nextFocusElement = event.target.firstParentWithClass("focusable");
336 this.currentFocusElement = nextFocusElement;
339 WebInspector.documentClick = function(event)
341 var anchor = event.target.firstParentOrSelfWithNodeName("a");
342 if (!anchor || !anchor.hasStyleClass("webkit-html-resource-link"))
345 if (WebInspector.showResourceForURL(anchor.getAttribute("href"))) {
346 event.preventDefault();
347 event.stopPropagation();
351 WebInspector.documentKeypress = function(event)
353 if (!this.currentFocusElement)
355 if (this.currentFocusElement.handleKeyEvent)
356 this.currentFocusElement.handleKeyEvent(event);
357 else if (this.currentFocusElement.id && this.currentFocusElement.id.length && WebInspector[this.currentFocusElement.id + "Keypress"])
358 WebInspector[this.currentFocusElement.id + "Keypress"](event);
361 WebInspector.documentCanCopy = function(event)
363 if (!this.currentFocusElement)
365 // Calling preventDefault() will say "we support copying, so enable the Copy menu".
366 if (this.currentFocusElement.handleCopyEvent)
367 event.preventDefault();
368 else if (this.currentFocusElement.id && this.currentFocusElement.id.length && WebInspector[this.currentFocusElement.id + "Copy"])
369 event.preventDefault();
372 WebInspector.documentCopy = function(event)
374 if (!this.currentFocusElement)
376 if (this.currentFocusElement.handleCopyEvent)
377 this.currentFocusElement.handleCopyEvent(event);
378 else if (this.currentFocusElement.id && this.currentFocusElement.id.length && WebInspector[this.currentFocusElement.id + "Copy"])
379 WebInspector[this.currentFocusElement.id + "Copy"](event);
382 WebInspector.sidebarKeypress = function(event)
384 var nextSelectedElement;
386 if (this.fileOutline.selectedTreeElement) {
387 if (!this.fileOutline.handleKeyEvent(event) && event.keyIdentifier === "Down" && !event.altKey && this.showingStatusArea) {
388 var nextSelectedElement = this.statusOutline.children[0];
389 while (nextSelectedElement && !nextSelectedElement.selectable)
390 nextSelectedElement = nextSelectedElement.traverseNextTreeElement(false);
392 } else if (this.statusOutline.selectedTreeElement) {
393 if (!this.showingStatusArea || (!this.statusOutline.handleKeyEvent(event) && event.keyIdentifier === "Up" && !event.altKey)) {
394 var nextSelectedElement = this.fileOutline.children[0];
395 var lastSelectable = null;
397 while (nextSelectedElement) {
398 if (nextSelectedElement.selectable)
399 lastSelectable = nextSelectedElement;
400 nextSelectedElement = nextSelectedElement.traverseNextTreeElement(false);
403 nextSelectedElement = lastSelectable;
407 if (nextSelectedElement) {
408 nextSelectedElement.reveal();
409 nextSelectedElement.select();
411 event.preventDefault();
412 event.stopPropagation();
416 WebInspector.sidebarCopy = function(event)
418 event.clipboardData.clearData();
419 event.preventDefault();
421 var selectedElement = this.fileOutline.selectedTreeElement;
422 if (!selectedElement || !selectedElement.representedObject || !selectedElement.representedObject.url)
425 event.clipboardData.setData("URL", this.fileOutline.selectedTreeElement.representedObject.url);
428 WebInspector.mainKeypress = function(event)
430 if (this.currentPanel && this.currentPanel.handleKeyEvent)
431 this.currentPanel.handleKeyEvent(event);
434 WebInspector.mainCopy = function(event)
436 if (this.currentPanel && this.currentPanel.handleCopyEvent)
437 this.currentPanel.handleCopyEvent(event);
440 WebInspector.searchResultsKeypress = function(event)
442 if (this.searchResultsTree)
443 this.searchResultsTree.handleKeyEvent(event);
446 WebInspector.animateStyle = function(animations, duration, callback, complete)
448 if (complete === undefined)
450 var slice = (1000 / 30); // 30 frames per second
452 var defaultUnit = "px";
453 var propertyUnit = {opacity: ""};
455 for (var i = 0; i < animations.length; ++i) {
456 var animation = animations[i];
461 for (key in animation) {
462 if (key === "element")
463 element = animation[key];
464 else if (key === "start")
465 start = animation[key];
466 else if (key === "current")
467 current = animation[key];
468 else if (key === "end")
469 end = animation[key];
472 if (!element || !end)
475 var computedStyle = element.ownerDocument.defaultView.getComputedStyle(element);
479 start[key] = parseInt(computedStyle.getPropertyValue(key));
480 animation.start = start;
481 } else if (complete == 0)
483 element.style.setProperty(key, start[key] + (key in propertyUnit ? propertyUnit[key] : defaultUnit));
488 current[key] = start[key];
489 animation.current = current;
492 function cubicInOut(t, b, c, d)
494 if ((t/=d/2) < 1) return c/2*t*t*t + b;
495 return c/2*((t-=2)*t*t + 2) + b;
498 var style = element.style;
500 var startValue = start[key];
501 var currentValue = current[key];
502 var endValue = end[key];
503 if ((complete + slice) < duration) {
504 var delta = (endValue - startValue) / (duration / slice);
505 var newValue = cubicInOut(complete, startValue, endValue - startValue, duration);
506 style.setProperty(key, newValue + (key in propertyUnit ? propertyUnit[key] : defaultUnit));
507 current[key] = newValue;
509 style.setProperty(key, endValue + (key in propertyUnit ? propertyUnit[key] : defaultUnit));
514 if (complete < duration)
515 setTimeout(WebInspector.animateStyle, slice, animations, duration, callback, complete + slice);
520 WebInspector.toggleAttach = function()
522 this.attached = !this.attached;
525 WebInspector.toggleStatusArea = function()
527 this.showingStatusArea = !this.showingStatusArea;
530 WebInspector.sidebarResizerDragStart = function(event)
532 WebInspector.dividerDragStart(document.getElementById("sidebar"), WebInspector.sidebarResizerDrag, WebInspector.sidebarResizerDragEnd, event, "col-resize");
535 WebInspector.sidebarResizerDragEnd = function(event)
537 WebInspector.dividerDragEnd(document.getElementById("sidebar"), WebInspector.sidebarResizerDrag, WebInspector.sidebarResizerDragEnd, event);
540 WebInspector.sidebarResizerDrag = function(event)
542 var sidebar = document.getElementById("sidebar");
543 if (sidebar.dragging == true) {
544 var x = event.clientX + window.scrollX;
546 // FIXME: We can should come up with a better hueristic for constraining the size
548 var newWidth = Number.constrain(x, 100, window.innerWidth - 100);
551 sidebar.dragLastX = x;
553 sidebar.style.width = newWidth + "px";
554 document.getElementById("sidebarResizer").style.left = (newWidth - 3) + "px";
555 document.getElementById("main").style.left = newWidth + "px";
556 document.getElementById("toolbarButtons").style.left = newWidth + "px";
557 document.getElementById("searchResults").style.left = newWidth + "px";
558 document.getElementById("searchResultsResizer").style.left = newWidth + "px";
560 if (WebInspector.currentPanel && WebInspector.currentPanel.resize)
561 WebInspector.currentPanel.resize();
563 event.preventDefault();
567 WebInspector.searchResultsResizerDragStart = function(event)
569 WebInspector.dividerDragStart(document.getElementById("searchResults"), WebInspector.searchResultsResizerDrag, WebInspector.searchResultsResizerDragEnd, event, "row-resize");
572 WebInspector.searchResultsResizerDragEnd = function(event)
574 WebInspector.dividerDragEnd(document.getElementById("searchResults"), WebInspector.searchResultsResizerDrag, WebInspector.searchResultsResizerDragEnd, event);
577 WebInspector.searchResultsResizerDrag = function(event)
579 var searchResults = document.getElementById("searchResults");
580 if (searchResults.dragging == true) {
581 var y = event.clientY;
582 var newHeight = Number.constrain(y, 100, window.innerHeight - 100);
585 searchResults.dragLastY = y;
587 WebInspector.searchResultsHeight = newHeight - Preferences.toolbarHeight;
588 searchResults.style.height = WebInspector.searchResultsHeight + "px";
589 document.getElementById("main").style.top = (newHeight + 1) + "px";
590 document.getElementById("searchResultsResizer").style.top = (newHeight - 2) + "px";
591 event.preventDefault();
595 WebInspector.dividerDragStart = function(element, dividerDrag, dividerDragEnd, event, cursor)
597 element.dragging = true;
598 element.dragLastY = event.clientY + window.scrollY;
599 element.dragLastX = event.clientX + window.scrollX;
600 document.addEventListener("mousemove", dividerDrag, true);
601 document.addEventListener("mouseup", dividerDragEnd, true);
602 document.body.style.cursor = cursor;
603 event.preventDefault();
606 WebInspector.dividerDragEnd = function(element, dividerDrag, dividerDragEnd, event)
608 element.dragging = false;
609 document.removeEventListener("mousemove", dividerDrag, true);
610 document.removeEventListener("mouseup", dividerDragEnd, true);
611 document.body.style.removeProperty("cursor");
614 WebInspector.back = function()
616 if (this.currentBackForwardIndex <= 0) {
617 console.error("Can't go back from index " + this.currentBackForwardIndex);
621 this.navigateToPanel(this.backForwardList[--this.currentBackForwardIndex], null, true);
624 WebInspector.forward = function()
626 if (this.currentBackForwardIndex >= this.backForwardList.length - 1) {
627 console.error("Can't go forward from index " + this.currentBackForwardIndex);
631 this.navigateToPanel(this.backForwardList[++this.currentBackForwardIndex], null, true);
634 WebInspector.updateBackForwardButtons = function()
636 var index = this.currentBackForwardIndex;
638 document.getElementById("back").disabled = index <= 0;
639 document.getElementById("forward").disabled = index >= this.backForwardList.length - 1;
642 WebInspector.showConsole = function()
644 this.showingStatusArea = true;
645 this.navigateToPanel(WebInspector.consolePanel);
648 WebInspector.showTimeline = function()
650 this.showingStatusArea = true;
651 this.navigateToPanel(WebInspector.networkPanel);
654 WebInspector.addResource = function(resource)
656 this.resources.push(resource);
658 if (resource.mainResource)
659 this.mainResource = resource;
662 this.resourceURLMap[resource.url] = resource;
663 this.networkPanel.addResourceToTimeline(resource);
667 WebInspector.removeResource = function(resource)
671 resource.category.removeResource(resource);
674 delete this.resourceURLMap[resource.url];
676 var resourcesLength = this.resources.length;
677 for (var i = 0; i < resourcesLength; ++i) {
678 if (this.resources[i] === resource) {
679 this.resources.splice(i, 1);
685 WebInspector.clearResources = function()
687 for (var category in this.resourceCategories)
688 this.resourceCategories[category].removeAllResources();
690 this.backForwardList = [];
691 this.currentBackForwardIndex = -1;
692 delete this.mainResource;
695 WebInspector.clearDatabaseResources = function()
697 this.resourceCategories.databases.removeAllResources();
700 WebInspector.resourceURLChanged = function(resource, oldURL)
702 delete this.resourceURLMap[oldURL];
703 this.resourceURLMap[resource.url] = resource;
706 WebInspector.addMessageToConsole = function(msg)
708 this.consolePanel.addMessage(msg);
710 case WebInspector.ConsoleMessage.MessageLevel.Warning:
711 ++this.consoleListItem.warnings;
712 this.showingStatusArea = true;
714 case WebInspector.ConsoleMessage.MessageLevel.Error:
715 ++this.consoleListItem.errors;
716 this.showingStatusArea = true;
721 WebInspector.clearConsoleMessages = function()
723 this.consolePanel.clearMessages();
724 this.consoleListItem.warnings = this.consoleListItem.errors = 0;
727 WebInspector.clearNetworkTimeline = function()
729 if (this._networkPanel)
730 this._networkPanel.clearTimeline();
733 WebInspector.drawLoadingPieChart = function(canvas, percent) {
734 var g = canvas.getContext("2d");
735 var darkColor = "rgb(122, 168, 218)";
736 var lightColor = "rgb(228, 241, 251)";
742 g.arc(cx, cy, r, 0, Math.PI * 2, false);
746 g.strokeStyle = darkColor;
747 g.fillStyle = lightColor;
751 var startangle = -Math.PI / 2;
752 var endangle = startangle + (percent * Math.PI * 2);
756 g.arc(cx, cy, r, startangle, endangle, false);
759 g.fillStyle = darkColor;
763 WebInspector.updateFocusedNode = function(node)
766 // FIXME: Should we deselect if null is passed in?
769 for (var i = 0; i < this.resourceCategories.documents.resources.length; ++i) {
770 var resource = this.resourceCategories.documents.resources[i];
771 if (resource.documentNode !== node.ownerDocument)
774 this.navigateToPanel(resource.panel, "dom");
775 resource.panel.focusedDOMNode = node;
777 this.currentFocusElement = document.getElementById("main");
783 WebInspector.resourceForURL = function(url)
785 for (var resourceURL in this.resourceURLMap) {
786 if (resourceURL.hasSubstring(url))
787 return this.resourceURLMap[resourceURL];
793 WebInspector.showResourceForURL = function(url)
795 var resource = this.resourceForURL(url);
799 this.navigateToResource(resource);
803 WebInspector.linkifyURL = function(url, linkText, classes, isExternal)
805 if (linkText === undefined)
806 linkText = url.escapeHTML();
807 classes = (classes === undefined) ? "" : classes + " ";
808 classes += isExternal ? "webkit-html-external-link" : "webkit-html-resource-link";
809 var link = "<a href=\"" + url + "\" class=\"" + classes + "\" target=\"_blank\">" + linkText + "</a>";
813 WebInspector.addMainEventListeners = function(doc)
815 doc.defaultView.addEventListener("focus", function(event) { WebInspector.windowFocused(event) }, true);
816 doc.defaultView.addEventListener("blur", function(event) { WebInspector.windowBlured(event) }, true);
817 doc.addEventListener("click", function(event) { WebInspector.documentClick(event) }, true);
820 WebInspector.performSearch = function(query)
822 if (!query || !query.length) {
823 this.showingSearchResults = false;
827 var resultsContainer = document.getElementById("searchResults");
828 resultsContainer.removeChildren();
830 var isXPath = query.indexOf("/") !== -1;
836 var escapedQuery = query.escapeCharacters("'");
837 xpathQuery = "//*[contains(name(),'" + escapedQuery + "') or contains(@*,'" + escapedQuery + "')] | //text()[contains(.,'" + escapedQuery + "')] | //comment()[contains(.,'" + escapedQuery + "')]";
840 var resourcesToSearch = [].concat(this.resourceCategories.documents.resources, this.resourceCategories.stylesheets.resources, this.resourceCategories.scripts.resources, this.resourceCategories.other.resources);
843 for (var i = 0; i < resourcesToSearch.length; ++i) {
844 var resource = resourcesToSearch[i];
846 var sourceResults = [];
847 if (!isXPath && "source" in resource.panel.views) {
848 resource.panel.setupSourceFrameIfNeeded();
849 sourceResults = InspectorController.search(resource.panel.views.source.frameElement.contentDocument, query);
853 if (resource.category === this.resourceCategories.documents) {
855 var doc = resource.documentNode;
856 var nodeList = doc.evaluate(xpathQuery, doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);
857 for (var j = 0; j < nodeList.snapshotLength; ++j)
858 domResults.push(nodeList.snapshotItem(i));
860 // ignore any exceptions. the query might be malformed, but we allow that.
864 if ((!sourceResults || !sourceResults.length) && !domResults.length)
867 files.push({resource: resource, sourceResults: sourceResults, domResults: domResults});
873 this.showingSearchResults = true;
875 var fileList = document.createElement("ol");
876 fileList.className = "outline-disclosure";
877 resultsContainer.appendChild(fileList);
879 this.searchResultsTree = new TreeOutline(fileList);
880 this.searchResultsTree.expandTreeElementsWhenArrowing = true;
882 var sourceResultSelected = function(element)
884 var selection = window.getSelection();
885 selection.removeAllRanges();
886 selection.addRange(element.representedObject.range);
888 WebInspector.navigateToPanel(element.representedObject.panel, "source");
889 element.representedObject.line.scrollIntoView(true);
890 resultsContainer.scrollToElement(element.listItemElement);
893 var domResultSelected = function(element)
895 WebInspector.navigateToPanel(element.representedObject.panel, "dom");
896 element.representedObject.panel.focusedDOMNode = element.representedObject.node;
897 resultsContainer.scrollToElement(element.listItemElement);
900 for (var i = 0; i < files.length; ++i) {
903 var fileItem = new TreeElement(file.resource.displayName, {}, true);
904 fileItem.expanded = true;
905 fileItem.selectable = false;
906 this.searchResultsTree.appendChild(fileItem);
908 if (file.sourceResults && file.sourceResults.length) {
909 for (var j = 0; j < file.sourceResults.length; ++j) {
910 var range = file.sourceResults[j];
912 var line = range.startContainer;
913 while (line.parentNode && line.nodeName.toLowerCase() != "tr")
914 line = line.parentNode;
915 var lineRange = file.resource.panel.views.source.frameElement.contentDocument.createRange();
916 lineRange.selectNodeContents(line);
918 // Don't include any error bubbles in the search result
919 var end = line.lastChild.lastChild;
920 if (end.nodeName.toLowerCase() == "div" && end.hasStyleClass("webkit-html-message-bubble")) {
921 while (end && end.nodeName.toLowerCase() == "div" && end.hasStyleClass("webkit-html-message-bubble"))
922 end = end.previousSibling;
923 lineRange.setEndAfter(end);
926 var beforeRange = file.resource.panel.views.source.frameElement.contentDocument.createRange();
927 beforeRange.setStart(lineRange.startContainer, lineRange.startOffset);
928 beforeRange.setEnd(range.startContainer, range.startOffset);
930 var afterRange = file.resource.panel.views.source.frameElement.contentDocument.createRange();
931 afterRange.setStart(range.endContainer, range.endOffset);
932 afterRange.setEnd(lineRange.endContainer, lineRange.endOffset);
934 var beforeText = beforeRange.toString().trimLeadingWhitespace();
935 var text = range.toString();
936 var afterText = afterRange.toString().trimTrailingWhitespace();
938 var length = beforeText.length + text.length + afterText.length;
939 if (length > Preferences.maxTextSearchResultLength) {
940 var beforeAfterLength = (Preferences.maxTextSearchResultLength - text.length) / 2;
941 if (beforeText.length > beforeAfterLength)
942 beforeText = "\u2026" + beforeText.substr(-beforeAfterLength);
943 if (afterText.length > beforeAfterLength)
944 afterText = afterText.substr(0, beforeAfterLength) + "\u2026";
947 var title = "<div class=\"selection selected\"></div>";
949 title += "<div class=\"search-results-section\">" + WebInspector.UIString("Source") + "</div>";
950 title += beforeText.escapeHTML() + "<span class=\"search-matched-string\">" + text.escapeHTML() + "</span>" + afterText.escapeHTML();
951 var item = new TreeElement(title, {panel: file.resource.panel, line: line, range: range}, false);
952 item.onselect = sourceResultSelected;
953 fileItem.appendChild(item);
957 if (file.domResults.length) {
958 for (var j = 0; j < file.domResults.length; ++j) {
959 var node = file.domResults[j];
960 var title = "<div class=\"selection selected\"></div>";
962 title += "<div class=\"search-results-section\">" + WebInspector.UIString("DOM") + "</div>";
963 title += nodeTitleInfo.call(node).title;
964 var item = new TreeElement(title, {panel: file.resource.panel, node: node}, false);
965 item.onselect = domResultSelected;
966 fileItem.appendChild(item);
972 WebInspector.navigateToResource = function(resource)
974 this.navigateToPanel(resource.panel);
977 WebInspector.navigateToPanel = function(panel, view, fromBackForwardAction)
979 if (this.currentPanel === panel) {
981 panel.currentView = view;
985 if (!fromBackForwardAction) {
986 var oldIndex = this.currentBackForwardIndex;
988 this.backForwardList.splice(oldIndex + 1, this.backForwardList.length - oldIndex);
989 this.currentBackForwardIndex++;
990 this.backForwardList.push(panel);
993 this.currentPanel = panel;
995 panel.currentView = view;
998 WebInspector.UIString = function(string)
1000 if (string in this.localizedStrings)
1001 string = this.localizedStrings[string];
1003 if (!(string in this.missingLocalizedStrings)) {
1004 console.error("Localized string \"" + string + "\" not found.");
1005 this.missingLocalizedStrings[string] = true;
1008 if (Preferences.showMissingLocalizedStrings)
1009 string += " (not localized)";
1012 return String.vsprintf(string, Array.prototype.slice.call(arguments, 1));
1015 WebInspector.StatusTreeElement = function(title, iconClass, panel)
1017 TreeElement.call(this, "<span class=\"title only\">" + title + "</span><span class=\"icon " + iconClass + "\"></span>", null, false);
1021 WebInspector.StatusTreeElement.prototype = {
1022 onselect: function()
1024 var selectedElement = WebInspector.fileOutline.selectedTreeElement;
1025 if (selectedElement)
1026 selectedElement.deselect();
1028 WebInspector.navigateToPanel(this.panel);
1031 ondeselect: function()
1038 WebInspector.StatusTreeElement.prototype.__proto__ = TreeElement.prototype;
1040 WebInspector.ConsoleStatusTreeElement = function(panel)
1042 WebInspector.StatusTreeElement.call(this, WebInspector.UIString("Console"), "console", panel);
1045 WebInspector.ConsoleStatusTreeElement.prototype = {
1048 if (!("_warnings" in this))
1051 return this._warnings;
1056 if (this._warnings === x)
1061 this._updateTitle();
1066 if (!("_errors" in this))
1069 return this._errors;
1074 if (this._errors === x)
1079 this._updateTitle();
1082 _updateTitle: function()
1084 var title = "<span class=\"title";
1085 if (!this.warnings && !this.errors)
1087 title += "\">" + WebInspector.UIString("Console") + "</span><span class=\"icon console\"></span>";
1089 if (this.warnings || this.errors) {
1090 title += "<span class=\"info\">";
1092 title += this.errors + " error";
1093 if (this.errors > 1)
1096 if (this.warnings) {
1099 title += this.warnings + " warning";
1100 if (this.warnings > 1)
1110 WebInspector.ConsoleStatusTreeElement.prototype.__proto__ = WebInspector.StatusTreeElement.prototype;
1112 // This table maps MIME types to the Resource.Types which are valid for them.
1113 // The following line:
1114 // "text/html": {0: 1},
1115 // means that text/html is a valid MIME type for resources that have type
1116 // WebInspector.Resource.Type.Document (which has a value of 0).
1117 WebInspector.MIMETypes = {
1118 "text/html": {0: true},
1119 "text/xml": {0: true},
1120 "text/plain": {0: true},
1121 "application/xhtml+xml": {0: true},
1122 "text/css": {1: true},
1123 "text/xsl": {1: true},
1124 "image/jpeg": {2: true},
1125 "image/png": {2: true},
1126 "image/gif": {2: true},
1127 "image/bmp": {2: true},
1128 "image/x-icon": {2: true},
1129 "image/x-xbitmap": {2: true},
1130 "font/ttf": {3: true},
1131 "font/opentype": {3: true},
1132 "application/x-font-type1": {3: true},
1133 "application/x-font-ttf": {3: true},
1134 "application/x-truetype-font": {3: true},
1135 "text/javascript": {4: true},
1136 "text/ecmascript": {4: true},
1137 "application/javascript": {4: true},
1138 "application/ecmascript": {4: true},
1139 "application/x-javascript": {4: true},
1140 "text/javascript1.1": {4: true},
1141 "text/javascript1.2": {4: true},
1142 "text/javascript1.3": {4: true},
1143 "text/jscript": {4: true},
1144 "text/livescript": {4: true},