diff options
Diffstat (limited to 'WebCore/page/inspector/inspector.js')
-rw-r--r-- | WebCore/page/inspector/inspector.js | 1186 |
1 files changed, 1186 insertions, 0 deletions
diff --git a/WebCore/page/inspector/inspector.js b/WebCore/page/inspector/inspector.js new file mode 100644 index 0000000..a9f5fcf --- /dev/null +++ b/WebCore/page/inspector/inspector.js @@ -0,0 +1,1186 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007 Matt Lilek (pewtermoose@gmail.com). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +var Preferences = { + ignoreWhitespace: true, + showUserAgentStyles: true, + maxInlineTextChildLength: 80, + maxTextSearchResultLength: 80, + showInheritedComputedStyleProperties: false, + showMissingLocalizedStrings: false +} + +var WebInspector = { + resources: [], + resourceURLMap: {}, + backForwardList: [], + searchResultsHeight: 100, + localizedStrings: {}, + missingLocalizedStrings: {}, + + get consolePanel() + { + if (!this._consolePanel) + this._consolePanel = new WebInspector.ConsolePanel(); + + return this._consolePanel; + }, + + get networkPanel() + { + if (!this._networkPanel) + this._networkPanel = new WebInspector.NetworkPanel(); + + return this._networkPanel; + }, + + get currentBackForwardIndex() + { + if (this._currentBackForwardIndex === undefined) + this._currentBackForwardIndex = -1; + + return this._currentBackForwardIndex; + }, + + set currentBackForwardIndex(x) + { + if (this._currentBackForwardIndex === x) + return; + + this._currentBackForwardIndex = x; + this.updateBackForwardButtons(); + }, + + get currentFocusElement() + { + return this._currentFocusElement; + }, + + set currentFocusElement(x) + { + if (!x || this._currentFocusElement === x) + return; + + if (this._currentFocusElement) { + this._currentFocusElement.removeStyleClass("focused"); + this._currentFocusElement.addStyleClass("blurred"); + if (this._currentFocusElement.blurred) + this._currentFocusElement.blurred(); + } + + this._currentFocusElement = x; + + if (x) { + x.addStyleClass("focused"); + x.removeStyleClass("blurred"); + if (this._currentFocusElement.focused) + this._currentFocusElement.focused(); + } + }, + + get currentPanel() + { + return this._currentPanel; + }, + + set currentPanel(x) + { + if (this._currentPanel === x) + return; + + if (this._currentPanel) + this._currentPanel.hide(); + + this._currentPanel = x; + + if (x) + x.show(); + }, + + get attached() + { + return this._attached; + }, + + set attached(x) + { + if (this._attached === x) + return; + + this._attached = x; + + var body = document.body; + if (x) { + InspectorController.attach(); + body.removeStyleClass("detached"); + body.addStyleClass("attached"); + } else { + InspectorController.detach(); + body.removeStyleClass("attached"); + body.addStyleClass("detached"); + } + }, + + get showingSearchResults() + { + return this._showingSearchResults; + }, + + set showingSearchResults(x) + { + if (this._showingSearchResults === x) + return; + + this._showingSearchResults = x; + + var resultsContainer = document.getElementById("searchResults"); + var searchResultsResizer = document.getElementById("searchResultsResizer"); + + if (x) { + resultsContainer.removeStyleClass("hidden"); + searchResultsResizer.removeStyleClass("hidden"); + + var animations = [ + {element: resultsContainer, end: {top: 0}}, + {element: searchResultsResizer, end: {top: WebInspector.searchResultsHeight - 3}}, + {element: document.getElementById("panels"), end: {top: WebInspector.searchResultsHeight}} + ]; + + WebInspector.animateStyle(animations, 250); + } else { + searchResultsResizer.addStyleClass("hidden"); + + var animations = [ + {element: resultsContainer, end: {top: -WebInspector.searchResultsHeight}}, + {element: searchResultsResizer, end: {top: 0}}, + {element: document.getElementById("panels"), end: {top: 0}} + ]; + + var animationFinished = function() + { + resultsContainer.addStyleClass("hidden"); + resultsContainer.removeChildren(); + delete this.searchResultsTree; + }; + + WebInspector.animateStyle(animations, 250, animationFinished); + } + } +} + +WebInspector.loaded = function() +{ + var platform = InspectorController.platform(); + document.body.addStyleClass("platform-" + platform); + + this.fileOutline = new TreeOutline(document.getElementById("list")); + this.fileOutline.expandTreeElementsWhenArrowing = true; + + this.statusOutline = new TreeOutline(document.getElementById("status")); + this.statusOutline.expandTreeElementsWhenArrowing = true; + + this.resourceCategories = { + documents: new WebInspector.ResourceCategory(WebInspector.UIString("documents"), "documents"), + stylesheets: new WebInspector.ResourceCategory(WebInspector.UIString("stylesheets"), "stylesheets"), + images: new WebInspector.ResourceCategory(WebInspector.UIString("images"), "images"), + scripts: new WebInspector.ResourceCategory(WebInspector.UIString("scripts"), "scripts"), + fonts: new WebInspector.ResourceCategory(WebInspector.UIString("fonts"), "fonts"), + databases: new WebInspector.ResourceCategory(WebInspector.UIString("databases"), "databases"), + other: new WebInspector.ResourceCategory(WebInspector.UIString("other"), "other") + }; + + this.Tips = { + ResourceNotCompressed: {id: 0, message: WebInspector.UIString("You could save bandwidth by having your web server compress this transfer with gzip or zlib.")} + }; + + this.Warnings = { + IncorrectMIMEType: {id: 0, message: WebInspector.UIString("Resource interpreted as %s but transferred with MIME type %s.")} + }; + + this.consoleListItem = new WebInspector.ConsoleStatusTreeElement(WebInspector.consolePanel); + this.statusOutline.appendChild(this.consoleListItem); + + this.networkListItem = new WebInspector.StatusTreeElement(WebInspector.UIString("Network"), "network", WebInspector.networkPanel); + this.statusOutline.appendChild(this.networkListItem); + + this.resourceCategories.documents.listItem.expand(); + + this.currentFocusElement = document.getElementById("sidebar"); + + this.addMainEventListeners(document); + + window.addEventListener("unload", this.windowUnload.bind(this), true); + window.addEventListener("resize", this.windowResize.bind(this), true); + + document.addEventListener("mousedown", this.changeFocus.bind(this), true); + document.addEventListener("focus", this.changeFocus.bind(this), true); + document.addEventListener("keydown", this.documentKeyDown.bind(this), true); + document.addEventListener("beforecopy", this.documentCanCopy.bind(this), true); + document.addEventListener("copy", this.documentCopy.bind(this), true); + + document.getElementById("back").title = WebInspector.UIString("Show previous panel."); + document.getElementById("forward").title = WebInspector.UIString("Show next panel."); + + document.getElementById("search").setAttribute("placeholder", WebInspector.UIString("Search")); + + document.getElementById("back").addEventListener("click", this.back.bind(this), true); + document.getElementById("forward").addEventListener("click", this.forward.bind(this), true); + this.updateBackForwardButtons(); + + document.getElementById("attachToggle").addEventListener("click", this.toggleAttach.bind(this), true); + + document.getElementById("sidebarResizeWidget").addEventListener("mousedown", this.sidebarResizerDragStart, true); + document.getElementById("sidebarResizer").addEventListener("mousedown", this.sidebarResizerDragStart, true); + document.getElementById("searchResultsResizer").addEventListener("mousedown", this.searchResultsResizerDragStart, true); + + if (platform === "mac-leopard") + document.getElementById("toolbar").addEventListener("mousedown", this.toolbarDragStart, true); + + document.body.addStyleClass("detached"); + + InspectorController.loaded(); +} + +var windowLoaded = function() +{ + var localizedStringsURL = InspectorController.localizedStringsURL(); + if (localizedStringsURL) { + var localizedStringsScriptElement = document.createElement("script"); + localizedStringsScriptElement.addEventListener("load", WebInspector.loaded.bind(WebInspector), false); + localizedStringsScriptElement.type = "text/javascript"; + localizedStringsScriptElement.src = localizedStringsURL; + document.getElementsByTagName("head").item(0).appendChild(localizedStringsScriptElement); + } else + WebInspector.loaded(); + + window.removeEventListener("load", windowLoaded, false); + delete windowLoaded; +}; + +window.addEventListener("load", windowLoaded, false); + +WebInspector.windowUnload = function(event) +{ + InspectorController.windowUnloading(); +} + +WebInspector.windowResize = function(event) +{ + if (this.currentPanel && this.currentPanel.resize) + this.currentPanel.resize(); +} + +WebInspector.windowFocused = function(event) +{ + if (event.target.nodeType === Node.DOCUMENT_NODE) + document.body.removeStyleClass("inactive"); +} + +WebInspector.windowBlured = function(event) +{ + if (event.target.nodeType === Node.DOCUMENT_NODE) + document.body.addStyleClass("inactive"); +} + +WebInspector.changeFocus = function(event) +{ + var nextFocusElement; + + var current = event.target; + while (current) { + if (current.nodeName.toLowerCase() === "input") + nextFocusElement = current; + current = current.parentNode; + } + + if (!nextFocusElement) + nextFocusElement = event.target.firstParentWithClass("focusable"); + + this.currentFocusElement = nextFocusElement; +} + +WebInspector.documentClick = function(event) +{ + var anchor = event.target.firstParentOrSelfWithNodeName("a"); + if (!anchor || !anchor.hasStyleClass("webkit-html-resource-link")) + return; + + if (WebInspector.showResourceForURL(anchor.getAttribute("href"))) { + event.preventDefault(); + event.stopPropagation(); + } +} + +WebInspector.documentKeyDown = function(event) +{ + if (!this.currentFocusElement) + return; + if (this.currentFocusElement.handleKeyEvent) + this.currentFocusElement.handleKeyEvent(event); + else if (this.currentFocusElement.id && this.currentFocusElement.id.length && WebInspector[this.currentFocusElement.id + "KeyDown"]) + WebInspector[this.currentFocusElement.id + "KeyDown"](event); +} + +WebInspector.documentCanCopy = function(event) +{ + if (!this.currentFocusElement) + return; + // Calling preventDefault() will say "we support copying, so enable the Copy menu". + if (this.currentFocusElement.handleCopyEvent) + event.preventDefault(); + else if (this.currentFocusElement.id && this.currentFocusElement.id.length && WebInspector[this.currentFocusElement.id + "Copy"]) + event.preventDefault(); +} + +WebInspector.documentCopy = function(event) +{ + if (!this.currentFocusElement) + return; + if (this.currentFocusElement.handleCopyEvent) + this.currentFocusElement.handleCopyEvent(event); + else if (this.currentFocusElement.id && this.currentFocusElement.id.length && WebInspector[this.currentFocusElement.id + "Copy"]) + WebInspector[this.currentFocusElement.id + "Copy"](event); +} + +WebInspector.sidebarKeyDown = function(event) +{ + var nextSelectedElement; + + if (this.fileOutline.selectedTreeElement) { + if (!this.fileOutline.handleKeyEvent(event) && event.keyIdentifier === "Down" && !event.altKey) { + var nextSelectedElement = this.statusOutline.children[0]; + while (nextSelectedElement && !nextSelectedElement.selectable) + nextSelectedElement = nextSelectedElement.traverseNextTreeElement(false); + } + } else if (this.statusOutline.selectedTreeElement) { + if (!this.statusOutline.handleKeyEvent(event) && event.keyIdentifier === "Up" && !event.altKey) { + var nextSelectedElement = this.fileOutline.children[0]; + var lastSelectable = null; + + while (nextSelectedElement) { + if (nextSelectedElement.selectable) + lastSelectable = nextSelectedElement; + nextSelectedElement = nextSelectedElement.traverseNextTreeElement(false); + } + + nextSelectedElement = lastSelectable; + } + } + + if (nextSelectedElement) { + nextSelectedElement.reveal(); + nextSelectedElement.select(); + + event.preventDefault(); + event.stopPropagation(); + } +} + +WebInspector.sidebarCopy = function(event) +{ + event.clipboardData.clearData(); + event.preventDefault(); + + var selectedElement = this.fileOutline.selectedTreeElement; + if (!selectedElement || !selectedElement.representedObject || !selectedElement.representedObject.url) + return; + + event.clipboardData.setData("URL", this.fileOutline.selectedTreeElement.representedObject.url); +} + +WebInspector.mainKeyDown = function(event) +{ + if (this.currentPanel && this.currentPanel.handleKeyEvent) + this.currentPanel.handleKeyEvent(event); +} + +WebInspector.mainCopy = function(event) +{ + if (this.currentPanel && this.currentPanel.handleCopyEvent) + this.currentPanel.handleCopyEvent(event); +} + +WebInspector.searchResultsKeyDown = function(event) +{ + if (this.searchResultsTree) + this.searchResultsTree.handleKeyEvent(event); +} + +WebInspector.animateStyle = function(animations, duration, callback, complete) +{ + if (complete === undefined) + complete = 0; + var slice = (1000 / 30); // 30 frames per second + + var defaultUnit = "px"; + var propertyUnit = {opacity: ""}; + + for (var i = 0; i < animations.length; ++i) { + var animation = animations[i]; + var element = null; + var start = null; + var current = null; + var end = null; + for (key in animation) { + if (key === "element") + element = animation[key]; + else if (key === "start") + start = animation[key]; + else if (key === "current") + current = animation[key]; + else if (key === "end") + end = animation[key]; + } + + if (!element || !end) + continue; + + var computedStyle = element.ownerDocument.defaultView.getComputedStyle(element); + if (!start) { + start = {}; + for (key in end) + start[key] = parseInt(computedStyle.getPropertyValue(key)); + animation.start = start; + } else if (complete == 0) + for (key in start) + element.style.setProperty(key, start[key] + (key in propertyUnit ? propertyUnit[key] : defaultUnit)); + + if (!current) { + current = {}; + for (key in start) + current[key] = start[key]; + animation.current = current; + } + + function cubicInOut(t, b, c, d) + { + if ((t/=d/2) < 1) return c/2*t*t*t + b; + return c/2*((t-=2)*t*t + 2) + b; + } + + var style = element.style; + for (key in end) { + var startValue = start[key]; + var currentValue = current[key]; + var endValue = end[key]; + if ((complete + slice) < duration) { + var delta = (endValue - startValue) / (duration / slice); + var newValue = cubicInOut(complete, startValue, endValue - startValue, duration); + style.setProperty(key, newValue + (key in propertyUnit ? propertyUnit[key] : defaultUnit)); + current[key] = newValue; + } else { + style.setProperty(key, endValue + (key in propertyUnit ? propertyUnit[key] : defaultUnit)); + } + } + } + + if (complete < duration) + setTimeout(WebInspector.animateStyle, slice, animations, duration, callback, complete + slice); + else if (callback) + callback(); +} + +WebInspector.toggleAttach = function() +{ + this.attached = !this.attached; +} + +WebInspector.toolbarDragStart = function(event) +{ + var toolbar = document.getElementById("toolbar"); + if (event.target !== toolbar || WebInspector.attached) + return; + + toolbar.lastScreenX = event.screenX; + toolbar.lastScreenY = event.screenY; + + document.addEventListener("mousemove", WebInspector.toolbarDrag, true); + document.addEventListener("mouseup", WebInspector.toolbarDragEnd, true); + document.body.style.cursor = "default"; + + event.preventDefault(); +} + +WebInspector.toolbarDragEnd = function(event) +{ + var toolbar = document.getElementById("toolbar"); + delete toolbar.lastScreenX; + delete toolbar.lastScreenY; + + document.removeEventListener("mousemove", WebInspector.toolbarDrag, true); + document.removeEventListener("mouseup", WebInspector.toolbarDragEnd, true); + document.body.style.removeProperty("cursor"); + + event.preventDefault(); +} + +WebInspector.toolbarDrag = function(event) +{ + var toolbar = document.getElementById("toolbar"); + + var x = event.screenX - toolbar.lastScreenX; + var y = event.screenY - toolbar.lastScreenY; + + toolbar.lastScreenX = event.screenX; + toolbar.lastScreenY = event.screenY; + + // We cannot call window.moveBy here because it restricts the movement of the window + // at the edges. + InspectorController.moveByUnrestricted(x, y); + + event.preventDefault(); +} + +WebInspector.sidebarResizerDragStart = function(event) +{ + WebInspector.elementDragStart(document.getElementById("sidebar"), WebInspector.sidebarResizerDrag, WebInspector.sidebarResizerDragEnd, event, "col-resize"); +} + +WebInspector.sidebarResizerDragEnd = function(event) +{ + WebInspector.elementDragEnd(document.getElementById("sidebar"), WebInspector.sidebarResizerDrag, WebInspector.sidebarResizerDragEnd, event); +} + +WebInspector.sidebarResizerDrag = function(event) +{ + var x = event.pageX; + + // FIXME: We can should come up with a better hueristic for constraining the size of the sidebar. + var newWidth = Number.constrain(x, 100, window.innerWidth - 100); + + document.getElementById("sidebar").style.width = newWidth + "px"; + document.getElementById("sidebarResizer").style.left = (newWidth - 3) + "px"; + document.getElementById("main").style.left = newWidth + "px"; + document.getElementById("toolbarButtons").style.left = newWidth + "px"; + + if (WebInspector.currentPanel && WebInspector.currentPanel.resize) + WebInspector.currentPanel.resize(); + + event.preventDefault(); +} + +WebInspector.searchResultsResizerDragStart = function(event) +{ + WebInspector.elementDragStart(document.getElementById("searchResults"), WebInspector.searchResultsResizerDrag, WebInspector.searchResultsResizerDragEnd, event, "row-resize"); +} + +WebInspector.searchResultsResizerDragEnd = function(event) +{ + WebInspector.elementDragEnd(document.getElementById("searchResults"), WebInspector.searchResultsResizerDrag, WebInspector.searchResultsResizerDragEnd, event); +} + +WebInspector.searchResultsResizerDrag = function(event) +{ + var y = event.pageY - document.getElementById("main").offsetTop; + var newHeight = Number.constrain(y, 100, window.innerHeight - 100); + + WebInspector.searchResultsHeight = newHeight; + + document.getElementById("searchResults").style.height = WebInspector.searchResultsHeight + "px"; + document.getElementById("panels").style.top = newHeight + "px"; + document.getElementById("searchResultsResizer").style.top = (newHeight - 3) + "px"; + + event.preventDefault(); +} + +WebInspector.elementDragStart = function(element, dividerDrag, elementDragEnd, event, cursor) +{ + if (WebInspector.draggingElement) + return elementDragEnd(event); + + WebInspector.draggingElement = true; + + document.addEventListener("mousemove", dividerDrag, true); + document.addEventListener("mouseup", elementDragEnd, true); + document.body.style.cursor = cursor; + + event.preventDefault(); +} + +WebInspector.elementDragEnd = function(element, dividerDrag, elementDragEnd, event) +{ + document.removeEventListener("mousemove", dividerDrag, true); + document.removeEventListener("mouseup", elementDragEnd, true); + document.body.style.removeProperty("cursor"); + + delete WebInspector.draggingElement; + + event.preventDefault(); +} + +WebInspector.back = function() +{ + if (this.currentBackForwardIndex <= 0) { + console.error("Can't go back from index " + this.currentBackForwardIndex); + return; + } + + this.navigateToPanel(this.backForwardList[--this.currentBackForwardIndex], null, true); +} + +WebInspector.forward = function() +{ + if (this.currentBackForwardIndex >= this.backForwardList.length - 1) { + console.error("Can't go forward from index " + this.currentBackForwardIndex); + return; + } + + this.navigateToPanel(this.backForwardList[++this.currentBackForwardIndex], null, true); +} + +WebInspector.updateBackForwardButtons = function() +{ + var index = this.currentBackForwardIndex; + + document.getElementById("back").disabled = index <= 0; + document.getElementById("forward").disabled = index >= this.backForwardList.length - 1; +} + +WebInspector.showConsole = function() +{ + this.navigateToPanel(WebInspector.consolePanel); +} + +WebInspector.showTimeline = function() +{ + this.navigateToPanel(WebInspector.networkPanel); +} + +WebInspector.addResource = function(resource) +{ + this.resources.push(resource); + + if (resource.mainResource) + this.mainResource = resource; + + if (resource.url) { + this.resourceURLMap[resource.url] = resource; + this.networkPanel.addResourceToTimeline(resource); + } +} + +WebInspector.removeResource = function(resource) +{ + resource.detach(); + + resource.category.removeResource(resource); + + if (resource.url) + delete this.resourceURLMap[resource.url]; + + var resourcesLength = this.resources.length; + for (var i = 0; i < resourcesLength; ++i) { + if (this.resources[i] === resource) { + this.resources.splice(i, 1); + break; + } + } +} + +WebInspector.clearResources = function() +{ + for (var category in this.resourceCategories) + this.resourceCategories[category].removeAllResources(); + this.resources = []; + this.backForwardList = []; + this.currentBackForwardIndex = -1; + delete this.mainResource; +} + +WebInspector.clearDatabaseResources = function() +{ + this.resourceCategories.databases.removeAllResources(); +} + +WebInspector.resourceURLChanged = function(resource, oldURL) +{ + delete this.resourceURLMap[oldURL]; + this.resourceURLMap[resource.url] = resource; +} + +WebInspector.addMessageToConsole = function(msg) +{ + this.consolePanel.addMessage(msg); + switch (msg.level) { + case WebInspector.ConsoleMessage.MessageLevel.Warning: + ++this.consoleListItem.warnings; + break; + case WebInspector.ConsoleMessage.MessageLevel.Error: + ++this.consoleListItem.errors; + break; + } +} + +WebInspector.clearConsoleMessages = function() +{ + this.consolePanel.clearMessages(); + this.consoleListItem.warnings = this.consoleListItem.errors = 0; +} + +WebInspector.clearNetworkTimeline = function() +{ + if (this._networkPanel) + this._networkPanel.clearTimeline(); +} + +WebInspector.drawLoadingPieChart = function(canvas, percent) { + var g = canvas.getContext("2d"); + var darkColor = "rgb(122, 168, 218)"; + var lightColor = "rgb(228, 241, 251)"; + var cx = 8; + var cy = 8; + var r = 7; + + g.beginPath(); + g.arc(cx, cy, r, 0, Math.PI * 2, false); + g.closePath(); + + g.lineWidth = 1; + g.strokeStyle = darkColor; + g.fillStyle = lightColor; + g.fill(); + g.stroke(); + + var startangle = -Math.PI / 2; + var endangle = startangle + (percent * Math.PI * 2); + + g.beginPath(); + g.moveTo(cx, cy); + g.arc(cx, cy, r, startangle, endangle, false); + g.closePath(); + + g.fillStyle = darkColor; + g.fill(); +} + +WebInspector.updateFocusedNode = function(node) +{ + if (!node) + // FIXME: Should we deselect if null is passed in? + return; + + for (var i = 0; i < this.resourceCategories.documents.resources.length; ++i) { + var resource = this.resourceCategories.documents.resources[i]; + if (resource.documentNode !== node.ownerDocument) + continue; + + this.navigateToPanel(resource.panel, "dom"); + resource.panel.focusedDOMNode = node; + + this.currentFocusElement = document.getElementById("main"); + + break; + } +} + +WebInspector.resourceForURL = function(url) +{ + for (var resourceURL in this.resourceURLMap) { + if (resourceURL.hasSubstring(url)) + return this.resourceURLMap[resourceURL]; + } + + return null; +} + +WebInspector.showResourceForURL = function(url) +{ + var resource = this.resourceForURL(url); + if (!resource) + return false; + + this.navigateToResource(resource); + return true; +} + +WebInspector.linkifyURL = function(url, linkText, classes, isExternal) +{ + if (linkText === undefined) + linkText = url.escapeHTML(); + classes = (classes === undefined) ? "" : classes + " "; + classes += isExternal ? "webkit-html-external-link" : "webkit-html-resource-link"; + var link = "<a href=\"" + url + "\" class=\"" + classes + "\" title=\"" + url + "\" target=\"_blank\">" + linkText + "</a>"; + return link; +} + +WebInspector.addMainEventListeners = function(doc) +{ + doc.defaultView.addEventListener("focus", function(event) { WebInspector.windowFocused(event) }, true); + doc.defaultView.addEventListener("blur", function(event) { WebInspector.windowBlured(event) }, true); + doc.addEventListener("click", function(event) { WebInspector.documentClick(event) }, true); +} + +WebInspector.performSearch = function(query) +{ + if (!query || !query.length) { + this.showingSearchResults = false; + return; + } + + var resultsContainer = document.getElementById("searchResults"); + resultsContainer.removeChildren(); + + var isXPath = query.indexOf("/") !== -1; + + var xpathQuery; + if (isXPath) + xpathQuery = query; + else { + var escapedQuery = query.escapeCharacters("'"); + xpathQuery = "//*[contains(name(),'" + escapedQuery + "') or contains(@*,'" + escapedQuery + "')] | //text()[contains(.,'" + escapedQuery + "')] | //comment()[contains(.,'" + escapedQuery + "')]"; + } + + var resourcesToSearch = [].concat(this.resourceCategories.documents.resources, this.resourceCategories.stylesheets.resources, this.resourceCategories.scripts.resources, this.resourceCategories.other.resources); + + var files = []; + for (var i = 0; i < resourcesToSearch.length; ++i) { + var resource = resourcesToSearch[i]; + + var sourceResults = []; + if (!isXPath && "source" in resource.panel.views) { + resource.panel.setupSourceFrameIfNeeded(); + sourceResults = InspectorController.search(resource.panel.views.source.frameElement.contentDocument, query); + } + + var domResults = []; + const searchResultsProperty = "__includedInInspectorSearchResults"; + function addNodesToDOMResults(nodes, length, getItem) + { + for (var i = 0; i < length; ++i) { + var node = getItem(nodes, i); + if (searchResultsProperty in node) + continue; + node[searchResultsProperty] = true; + domResults.push(node); + } + } + + function cleanUpDOMResultsNodes() + { + for (var i = 0; i < domResults.length; ++i) + delete domResults[i][searchResultsProperty]; + } + + if (resource.category === this.resourceCategories.documents) { + var doc = resource.documentNode; + try { + var result = Document.prototype.evaluate.call(doc, xpathQuery, doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE); + addNodesToDOMResults(result, result.snapshotLength, function(l, i) { return l.snapshotItem(i); }); + } catch(err) { + // ignore any exceptions. the query might be malformed, but we allow that. + } + + var result = Document.prototype.querySelectorAll.call(doc, query); + addNodesToDOMResults(result, result.length, function(l, i) { return l.item(i); }); + + cleanUpDOMResultsNodes(); + } + + if ((!sourceResults || !sourceResults.length) && !domResults.length) + continue; + + files.push({resource: resource, sourceResults: sourceResults, domResults: domResults}); + } + + if (!files.length) + return; + + this.showingSearchResults = true; + + var fileList = document.createElement("ol"); + fileList.className = "outline-disclosure"; + resultsContainer.appendChild(fileList); + + this.searchResultsTree = new TreeOutline(fileList); + this.searchResultsTree.expandTreeElementsWhenArrowing = true; + + var sourceResultSelected = function(element) + { + var selection = window.getSelection(); + selection.removeAllRanges(); + selection.addRange(element.representedObject.range); + + WebInspector.navigateToPanel(element.representedObject.panel, "source"); + element.representedObject.line.scrollIntoViewIfNeeded(true); + element.listItemElement.scrollIntoViewIfNeeded(false); + } + + var domResultSelected = function(element) + { + WebInspector.navigateToPanel(element.representedObject.panel, "dom"); + element.representedObject.panel.focusedDOMNode = element.representedObject.node; + element.listItemElement.scrollIntoViewIfNeeded(false); + } + + for (var i = 0; i < files.length; ++i) { + var file = files[i]; + + var fileItem = new TreeElement(file.resource.displayName, {}, true); + fileItem.expanded = true; + fileItem.selectable = false; + this.searchResultsTree.appendChild(fileItem); + + if (file.sourceResults && file.sourceResults.length) { + for (var j = 0; j < file.sourceResults.length; ++j) { + var range = file.sourceResults[j]; + + var line = range.startContainer; + while (line.parentNode && line.nodeName.toLowerCase() != "tr") + line = line.parentNode; + var lineRange = file.resource.panel.views.source.frameElement.contentDocument.createRange(); + lineRange.selectNodeContents(line); + + // Don't include any error bubbles in the search result + var end = line.lastChild.lastChild; + if (end.nodeName.toLowerCase() == "div" && end.hasStyleClass("webkit-html-message-bubble")) { + while (end && end.nodeName.toLowerCase() == "div" && end.hasStyleClass("webkit-html-message-bubble")) + end = end.previousSibling; + lineRange.setEndAfter(end); + } + + var beforeRange = file.resource.panel.views.source.frameElement.contentDocument.createRange(); + beforeRange.setStart(lineRange.startContainer, lineRange.startOffset); + beforeRange.setEnd(range.startContainer, range.startOffset); + + var afterRange = file.resource.panel.views.source.frameElement.contentDocument.createRange(); + afterRange.setStart(range.endContainer, range.endOffset); + afterRange.setEnd(lineRange.endContainer, lineRange.endOffset); + + var beforeText = beforeRange.toString().trimLeadingWhitespace(); + var text = range.toString(); + var afterText = afterRange.toString().trimTrailingWhitespace(); + + var length = beforeText.length + text.length + afterText.length; + if (length > Preferences.maxTextSearchResultLength) { + var beforeAfterLength = (Preferences.maxTextSearchResultLength - text.length) / 2; + if (beforeText.length > beforeAfterLength) + beforeText = "\u2026" + beforeText.substr(-beforeAfterLength); + if (afterText.length > beforeAfterLength) + afterText = afterText.substr(0, beforeAfterLength) + "\u2026"; + } + + var title = "<div class=\"selection selected\"></div>"; + if (j == 0) + title += "<div class=\"search-results-section\">" + WebInspector.UIString("Source") + "</div>"; + title += beforeText.escapeHTML() + "<span class=\"search-matched-string\">" + text.escapeHTML() + "</span>" + afterText.escapeHTML(); + var item = new TreeElement(title, {panel: file.resource.panel, line: line, range: range}, false); + item.onselect = sourceResultSelected; + fileItem.appendChild(item); + } + } + + if (file.domResults.length) { + for (var j = 0; j < file.domResults.length; ++j) { + var node = file.domResults[j]; + var title = "<div class=\"selection selected\"></div>"; + if (j == 0) + title += "<div class=\"search-results-section\">" + WebInspector.UIString("DOM") + "</div>"; + title += nodeTitleInfo.call(node).title; + var item = new TreeElement(title, {panel: file.resource.panel, node: node}, false); + item.onselect = domResultSelected; + fileItem.appendChild(item); + } + } + } +} + +WebInspector.navigateToResource = function(resource) +{ + this.navigateToPanel(resource.panel); +} + +WebInspector.navigateToPanel = function(panel, view, fromBackForwardAction) +{ + if (this.currentPanel === panel) { + if (panel && view) + panel.currentView = view; + return; + } + + if (!fromBackForwardAction) { + var oldIndex = this.currentBackForwardIndex; + if (oldIndex >= 0) + this.backForwardList.splice(oldIndex + 1, this.backForwardList.length - oldIndex); + this.currentBackForwardIndex++; + this.backForwardList.push(panel); + } + + this.currentPanel = panel; + if (panel && view) + panel.currentView = view; +} + +WebInspector.UIString = function(string) +{ + if (string in this.localizedStrings) + string = this.localizedStrings[string]; + else { + if (!(string in this.missingLocalizedStrings)) { + console.error("Localized string \"" + string + "\" not found."); + this.missingLocalizedStrings[string] = true; + } + + if (Preferences.showMissingLocalizedStrings) + string += " (not localized)"; + } + + return String.vsprintf(string, Array.prototype.slice.call(arguments, 1)); +} + +WebInspector.StatusTreeElement = function(title, iconClass, panel) +{ + TreeElement.call(this, "<span class=\"title only\">" + title + "</span><span class=\"icon " + iconClass + "\"></span>", null, false); + this.panel = panel; +} + +WebInspector.StatusTreeElement.prototype = { + onselect: function() + { + var selectedElement = WebInspector.fileOutline.selectedTreeElement; + if (selectedElement) + selectedElement.deselect(); + if (this.panel) + WebInspector.navigateToPanel(this.panel); + }, + + ondeselect: function() + { + if (this.panel) + this.panel.hide(); + } +} + +WebInspector.StatusTreeElement.prototype.__proto__ = TreeElement.prototype; + +WebInspector.ConsoleStatusTreeElement = function(panel) +{ + WebInspector.StatusTreeElement.call(this, WebInspector.UIString("Console"), "console", panel); +} + +WebInspector.ConsoleStatusTreeElement.prototype = { + get warnings() + { + if (!("_warnings" in this)) + this._warnings = 0; + + return this._warnings; + }, + + set warnings(x) + { + if (this._warnings === x) + return; + + this._warnings = x; + + this._updateTitle(); + }, + + get errors() + { + if (!("_errors" in this)) + this._errors = 0; + + return this._errors; + }, + + set errors(x) + { + if (this._errors === x) + return; + + this._errors = x; + + this._updateTitle(); + }, + + _updateTitle: function() + { + var title = "<span class=\"title"; + if (!this.warnings && !this.errors) + title += " only"; + title += "\">" + WebInspector.UIString("Console") + "</span><span class=\"icon console\"></span>"; + + if (this.warnings || this.errors) { + title += "<span class=\"info\">"; + if (this.errors) { + title += this.errors + " error"; + if (this.errors > 1) + title += "s"; + } + if (this.warnings) { + if (this.errors) + title += ", "; + title += this.warnings + " warning"; + if (this.warnings > 1) + title += "s"; + } + title += "</span>"; + } + + this.title = title; + } +} + +WebInspector.ConsoleStatusTreeElement.prototype.__proto__ = WebInspector.StatusTreeElement.prototype; + +// This table maps MIME types to the Resource.Types which are valid for them. +// The following line: +// "text/html": {0: 1}, +// means that text/html is a valid MIME type for resources that have type +// WebInspector.Resource.Type.Document (which has a value of 0). +WebInspector.MIMETypes = { + "text/html": {0: true}, + "text/xml": {0: true}, + "text/plain": {0: true}, + "application/xhtml+xml": {0: true}, + "text/css": {1: true}, + "text/xsl": {1: true}, + "image/jpeg": {2: true}, + "image/png": {2: true}, + "image/gif": {2: true}, + "image/bmp": {2: true}, + "image/x-icon": {2: true}, + "image/x-xbitmap": {2: true}, + "font/ttf": {3: true}, + "font/opentype": {3: true}, + "application/x-font-type1": {3: true}, + "application/x-font-ttf": {3: true}, + "application/x-truetype-font": {3: true}, + "text/javascript": {4: true}, + "text/ecmascript": {4: true}, + "application/javascript": {4: true}, + "application/ecmascript": {4: true}, + "application/x-javascript": {4: true}, + "text/javascript1.1": {4: true}, + "text/javascript1.2": {4: true}, + "text/javascript1.3": {4: true}, + "text/jscript": {4: true}, + "text/livescript": {4: true}, +} |