| /* |
| * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. |
| * |
| * 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. |
| */ |
| |
| WebInspector.Panel = function(name) |
| { |
| WebInspector.View.call(this); |
| |
| this.element.addStyleClass("panel"); |
| this.element.addStyleClass(name); |
| this._panelName = name; |
| |
| WebInspector.settings.installApplicationSetting(this._sidebarWidthSettingName(), undefined); |
| } |
| |
| // Should by in sync with style declarations. |
| WebInspector.Panel.counterRightMargin = 25; |
| |
| WebInspector.Panel.prototype = { |
| get toolbarItem() |
| { |
| if (this._toolbarItem) |
| return this._toolbarItem; |
| |
| this._toolbarItem = WebInspector.Toolbar.createPanelToolbarItem(this); |
| return this._toolbarItem; |
| }, |
| |
| get name() |
| { |
| return this._panelName; |
| }, |
| |
| show: function() |
| { |
| WebInspector.View.prototype.show.call(this); |
| |
| var statusBarItems = this.statusBarItems; |
| if (statusBarItems) { |
| this._statusBarItemContainer = document.createElement("div"); |
| for (var i = 0; i < statusBarItems.length; ++i) |
| this._statusBarItemContainer.appendChild(statusBarItems[i]); |
| document.getElementById("main-status-bar").appendChild(this._statusBarItemContainer); |
| } |
| |
| if ("_toolbarItem" in this) |
| this._toolbarItem.addStyleClass("toggled-on"); |
| |
| WebInspector.currentFocusElement = this.defaultFocusedElement; |
| |
| this.restoreSidebarWidth(); |
| this._restoreScrollPositions(); |
| WebInspector.extensionServer.notifyPanelShown(this.name); |
| }, |
| |
| hide: function() |
| { |
| this._storeScrollPositions(); |
| WebInspector.View.prototype.hide.call(this); |
| |
| if (this._statusBarItemContainer && this._statusBarItemContainer.parentNode) |
| this._statusBarItemContainer.parentNode.removeChild(this._statusBarItemContainer); |
| delete this._statusBarItemContainer; |
| if ("_toolbarItem" in this) |
| this._toolbarItem.removeStyleClass("toggled-on"); |
| WebInspector.extensionServer.notifyPanelHidden(this.name); |
| }, |
| |
| reset: function() |
| { |
| this.searchCanceled(); |
| WebInspector.resetFocusElement(); |
| }, |
| |
| get defaultFocusedElement() |
| { |
| return this.sidebarTreeElement || this.element; |
| }, |
| |
| attach: function() |
| { |
| if (!this.element.parentNode) |
| document.getElementById("main-panels").appendChild(this.element); |
| }, |
| |
| searchCanceled: function() |
| { |
| if (this._searchResults) { |
| for (var i = 0; i < this._searchResults.length; ++i) { |
| var view = this._searchResults[i]; |
| if (view.searchCanceled) |
| view.searchCanceled(); |
| delete view.currentQuery; |
| } |
| } |
| |
| WebInspector.searchController.updateSearchMatchesCount(0, this); |
| |
| if (this._currentSearchChunkIntervalIdentifier) { |
| clearInterval(this._currentSearchChunkIntervalIdentifier); |
| delete this._currentSearchChunkIntervalIdentifier; |
| } |
| |
| this._totalSearchMatches = 0; |
| this._currentSearchResultIndex = 0; |
| this._searchResults = []; |
| }, |
| |
| performSearch: function(query) |
| { |
| // Call searchCanceled since it will reset everything we need before doing a new search. |
| this.searchCanceled(true); |
| |
| var searchableViews = this.searchableViews; |
| if (!searchableViews || !searchableViews.length) |
| return; |
| |
| var parentElement = this.viewsContainerElement; |
| var visibleView = this.visibleView; |
| var sortFuction = this.searchResultsSortFunction; |
| |
| var matchesCountUpdateTimeout = null; |
| |
| function updateMatchesCount() |
| { |
| WebInspector.searchController.updateSearchMatchesCount(this._totalSearchMatches, this); |
| matchesCountUpdateTimeout = null; |
| } |
| |
| function updateMatchesCountSoon() |
| { |
| if (matchesCountUpdateTimeout) |
| return; |
| // Update the matches count every half-second so it doesn't feel twitchy. |
| matchesCountUpdateTimeout = setTimeout(updateMatchesCount.bind(this), 500); |
| } |
| |
| function finishedCallback(view, searchMatches) |
| { |
| if (!searchMatches) |
| return; |
| |
| this._totalSearchMatches += searchMatches; |
| this._searchResults.push(view); |
| |
| if (sortFuction) |
| this._searchResults.sort(sortFuction); |
| |
| if (this.searchMatchFound) |
| this.searchMatchFound(view, searchMatches); |
| |
| updateMatchesCountSoon.call(this); |
| |
| if (view === visibleView) |
| view.jumpToFirstSearchResult(); |
| } |
| |
| var i = 0; |
| var panel = this; |
| var boundFinishedCallback = finishedCallback.bind(this); |
| var chunkIntervalIdentifier = null; |
| |
| // Split up the work into chunks so we don't block the |
| // UI thread while processing. |
| |
| function processChunk() |
| { |
| var view = searchableViews[i]; |
| |
| if (++i >= searchableViews.length) { |
| if (panel._currentSearchChunkIntervalIdentifier === chunkIntervalIdentifier) |
| delete panel._currentSearchChunkIntervalIdentifier; |
| clearInterval(chunkIntervalIdentifier); |
| } |
| |
| if (!view) |
| return; |
| |
| if (view.element.parentNode !== parentElement && view.element.parentNode && parentElement) |
| view.detach(); |
| |
| view.currentQuery = query; |
| view.performSearch(query, boundFinishedCallback); |
| } |
| |
| processChunk(); |
| |
| chunkIntervalIdentifier = setInterval(processChunk, 25); |
| this._currentSearchChunkIntervalIdentifier = chunkIntervalIdentifier; |
| }, |
| |
| jumpToNextSearchResult: function() |
| { |
| if (!this.showView || !this._searchResults || !this._searchResults.length) |
| return; |
| |
| var showFirstResult = false; |
| |
| this._currentSearchResultIndex = this._searchResults.indexOf(this.visibleView); |
| if (this._currentSearchResultIndex === -1) { |
| this._currentSearchResultIndex = 0; |
| showFirstResult = true; |
| } |
| |
| var currentView = this._searchResults[this._currentSearchResultIndex]; |
| |
| if (currentView.showingLastSearchResult()) { |
| if (++this._currentSearchResultIndex >= this._searchResults.length) |
| this._currentSearchResultIndex = 0; |
| currentView = this._searchResults[this._currentSearchResultIndex]; |
| showFirstResult = true; |
| } |
| |
| if (currentView !== this.visibleView) { |
| this.showView(currentView); |
| WebInspector.searchController.focusSearchField(); |
| } |
| |
| if (showFirstResult) |
| currentView.jumpToFirstSearchResult(); |
| else |
| currentView.jumpToNextSearchResult(); |
| }, |
| |
| jumpToPreviousSearchResult: function() |
| { |
| if (!this.showView || !this._searchResults || !this._searchResults.length) |
| return; |
| |
| var showLastResult = false; |
| |
| this._currentSearchResultIndex = this._searchResults.indexOf(this.visibleView); |
| if (this._currentSearchResultIndex === -1) { |
| this._currentSearchResultIndex = 0; |
| showLastResult = true; |
| } |
| |
| var currentView = this._searchResults[this._currentSearchResultIndex]; |
| |
| if (currentView.showingFirstSearchResult()) { |
| if (--this._currentSearchResultIndex < 0) |
| this._currentSearchResultIndex = (this._searchResults.length - 1); |
| currentView = this._searchResults[this._currentSearchResultIndex]; |
| showLastResult = true; |
| } |
| |
| if (currentView !== this.visibleView) { |
| this.showView(currentView); |
| WebInspector.searchController.focusSearchField(); |
| } |
| |
| if (showLastResult) |
| currentView.jumpToLastSearchResult(); |
| else |
| currentView.jumpToPreviousSearchResult(); |
| }, |
| |
| createSidebar: function(parentElement, resizerParentElement) |
| { |
| if (this.sidebarElement) |
| return; |
| |
| if (!parentElement) |
| parentElement = this.element; |
| |
| if (!resizerParentElement) |
| resizerParentElement = parentElement; |
| |
| this.sidebarElement = document.createElement("div"); |
| this.sidebarElement.className = "sidebar"; |
| parentElement.appendChild(this.sidebarElement); |
| |
| this.sidebarResizeElement = document.createElement("div"); |
| this.sidebarResizeElement.className = "sidebar-resizer-vertical"; |
| this.sidebarResizeElement.addEventListener("mousedown", this._startSidebarDragging.bind(this), false); |
| resizerParentElement.appendChild(this.sidebarResizeElement); |
| |
| this.sidebarTreeElement = document.createElement("ol"); |
| this.sidebarTreeElement.className = "sidebar-tree"; |
| this.sidebarElement.appendChild(this.sidebarTreeElement); |
| |
| this.sidebarTree = new TreeOutline(this.sidebarTreeElement); |
| this.sidebarTree.panel = this; |
| }, |
| |
| _sidebarWidthSettingName: function() |
| { |
| return this._panelName + "SidebarWidth"; |
| }, |
| |
| _startSidebarDragging: function(event) |
| { |
| WebInspector.elementDragStart(this.sidebarResizeElement, this._sidebarDragging.bind(this), this._endSidebarDragging.bind(this), event, "col-resize"); |
| }, |
| |
| _sidebarDragging: function(event) |
| { |
| this.updateSidebarWidth(event.pageX); |
| |
| event.preventDefault(); |
| }, |
| |
| _endSidebarDragging: function(event) |
| { |
| WebInspector.elementDragEnd(event); |
| this.saveSidebarWidth(); |
| }, |
| |
| updateSidebarWidth: function(width) |
| { |
| if (!this.sidebarElement) |
| return; |
| |
| if (this.sidebarElement.offsetWidth <= 0) { |
| // The stylesheet hasn't loaded yet or the window is closed, |
| // so we can't calculate what is need. Return early. |
| return; |
| } |
| |
| if (!("_currentSidebarWidth" in this)) |
| this._currentSidebarWidth = this.sidebarElement.offsetWidth; |
| |
| if (typeof width === "undefined") |
| width = this._currentSidebarWidth; |
| |
| width = Number.constrain(width, Preferences.minSidebarWidth, window.innerWidth / 2); |
| |
| this._currentSidebarWidth = width; |
| this.setSidebarWidth(width); |
| |
| this.updateMainViewWidth(width); |
| }, |
| |
| setSidebarWidth: function(width) |
| { |
| this.sidebarElement.style.width = width + "px"; |
| this.sidebarResizeElement.style.left = (width - 3) + "px"; |
| }, |
| |
| restoreSidebarWidth: function() |
| { |
| var sidebarWidth = WebInspector.settings[this._sidebarWidthSettingName()]; |
| this.updateSidebarWidth(sidebarWidth); |
| }, |
| |
| saveSidebarWidth: function() |
| { |
| if (!this.sidebarElement) |
| return; |
| WebInspector.settings[this._sidebarWidthSettingName()] = this.sidebarElement.offsetWidth; |
| }, |
| |
| updateMainViewWidth: function(width) |
| { |
| // Should be implemented by ancestors. |
| }, |
| |
| resize: function() |
| { |
| var visibleView = this.visibleView; |
| if (visibleView && "resize" in visibleView) |
| visibleView.resize(); |
| }, |
| |
| canShowAnchorLocation: function(anchor) |
| { |
| return false; |
| }, |
| |
| showAnchorLocation: function(anchor) |
| { |
| return false; |
| }, |
| |
| elementsToRestoreScrollPositionsFor: function() |
| { |
| return []; |
| }, |
| |
| _storeScrollPositions: function() |
| { |
| var elements = this.elementsToRestoreScrollPositionsFor(); |
| for (var i = 0; i < elements.length; ++i) { |
| var container = elements[i]; |
| container._scrollTop = container.scrollTop; |
| } |
| }, |
| |
| _restoreScrollPositions: function() |
| { |
| var elements = this.elementsToRestoreScrollPositionsFor(); |
| for (var i = 0; i < elements.length; ++i) { |
| var container = elements[i]; |
| if (container._scrollTop) |
| container.scrollTop = container._scrollTop; |
| } |
| } |
| } |
| |
| WebInspector.Panel.prototype.__proto__ = WebInspector.View.prototype; |