1/* 2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29WebInspector.Panel = function() 30{ 31 WebInspector.View.call(this); 32 33 this.element.addStyleClass("panel"); 34} 35 36WebInspector.Panel.prototype = { 37 get toolbarItem() 38 { 39 if (this._toolbarItem) 40 return this._toolbarItem; 41 42 // Sample toolbar item as markup: 43 // <button class="toolbar-item resources toggleable"> 44 // <div class="toolbar-icon"></div> 45 // <div class="toolbar-label">Resources</div> 46 // </button> 47 48 this._toolbarItem = document.createElement("button"); 49 this._toolbarItem.className = "toolbar-item toggleable"; 50 this._toolbarItem.panel = this; 51 52 if ("toolbarItemClass" in this) 53 this._toolbarItem.addStyleClass(this.toolbarItemClass); 54 55 var iconElement = document.createElement("div"); 56 iconElement.className = "toolbar-icon"; 57 this._toolbarItem.appendChild(iconElement); 58 59 if ("toolbarItemLabel" in this) { 60 var labelElement = document.createElement("div"); 61 labelElement.className = "toolbar-label"; 62 labelElement.textContent = this.toolbarItemLabel; 63 this._toolbarItem.appendChild(labelElement); 64 } 65 66 return this._toolbarItem; 67 }, 68 69 createStatusBarButton: function() 70 { 71 var button = document.createElement("button"); 72 var glyph = document.createElement("div"); 73 glyph.className = "glyph"; 74 button.appendChild(glyph); 75 var glyphShadow = document.createElement("div"); 76 glyphShadow.className = "glyph shadow"; 77 button.appendChild(glyphShadow); 78 return button; 79 }, 80 81 show: function() 82 { 83 WebInspector.View.prototype.show.call(this); 84 85 var statusBarItems = this.statusBarItems; 86 if (statusBarItems) { 87 this._statusBarItemContainer = document.createElement("div"); 88 for (var i = 0; i < statusBarItems.length; ++i) 89 this._statusBarItemContainer.appendChild(statusBarItems[i]); 90 document.getElementById("main-status-bar").appendChild(this._statusBarItemContainer); 91 } 92 93 if ("_toolbarItem" in this) 94 this._toolbarItem.addStyleClass("toggled-on"); 95 96 WebInspector.currentFocusElement = document.getElementById("main-panels"); 97 }, 98 99 hide: function() 100 { 101 WebInspector.View.prototype.hide.call(this); 102 103 if (this._statusBarItemContainer && this._statusBarItemContainer.parentNode) 104 this._statusBarItemContainer.parentNode.removeChild(this._statusBarItemContainer); 105 delete this._statusBarItemContainer; 106 if ("_toolbarItem" in this) 107 this._toolbarItem.removeStyleClass("toggled-on"); 108 }, 109 110 attach: function() 111 { 112 if (!this.element.parentNode) 113 document.getElementById("main-panels").appendChild(this.element); 114 }, 115 116 searchCanceled: function(startingNewSearch) 117 { 118 if (this._searchResults) { 119 for (var i = 0; i < this._searchResults.length; ++i) { 120 var view = this._searchResults[i]; 121 if (view.searchCanceled) 122 view.searchCanceled(); 123 delete view.currentQuery; 124 } 125 } 126 127 WebInspector.updateSearchMatchesCount(0, this); 128 129 if (this._currentSearchChunkIntervalIdentifier) { 130 clearInterval(this._currentSearchChunkIntervalIdentifier); 131 delete this._currentSearchChunkIntervalIdentifier; 132 } 133 134 this._totalSearchMatches = 0; 135 this._currentSearchResultIndex = 0; 136 this._searchResults = []; 137 }, 138 139 performSearch: function(query) 140 { 141 // Call searchCanceled since it will reset everything we need before doing a new search. 142 this.searchCanceled(true); 143 144 var searchableViews = this.searchableViews; 145 if (!searchableViews || !searchableViews.length) 146 return; 147 148 var parentElement = this.viewsContainerElement; 149 var visibleView = this.visibleView; 150 var sortFuction = this.searchResultsSortFunction; 151 152 var matchesCountUpdateTimeout = null; 153 154 function updateMatchesCount() 155 { 156 WebInspector.updateSearchMatchesCount(this._totalSearchMatches, this); 157 matchesCountUpdateTimeout = null; 158 } 159 160 function updateMatchesCountSoon() 161 { 162 if (matchesCountUpdateTimeout) 163 return; 164 // Update the matches count every half-second so it doesn't feel twitchy. 165 matchesCountUpdateTimeout = setTimeout(updateMatchesCount.bind(this), 500); 166 } 167 168 function finishedCallback(view, searchMatches) 169 { 170 if (!searchMatches) 171 return; 172 173 this._totalSearchMatches += searchMatches; 174 this._searchResults.push(view); 175 176 if (sortFuction) 177 this._searchResults.sort(sortFuction); 178 179 if (this.searchMatchFound) 180 this.searchMatchFound(view, searchMatches); 181 182 updateMatchesCountSoon.call(this); 183 184 if (view === visibleView) 185 view.jumpToFirstSearchResult(); 186 } 187 188 var i = 0; 189 var panel = this; 190 var boundFinishedCallback = finishedCallback.bind(this); 191 var chunkIntervalIdentifier = null; 192 193 // Split up the work into chunks so we don't block the 194 // UI thread while processing. 195 196 function processChunk() 197 { 198 var view = searchableViews[i]; 199 200 if (++i >= searchableViews.length) { 201 if (panel._currentSearchChunkIntervalIdentifier === chunkIntervalIdentifier) 202 delete panel._currentSearchChunkIntervalIdentifier; 203 clearInterval(chunkIntervalIdentifier); 204 } 205 206 if (!view) 207 return; 208 209 if (view.element.parentNode !== parentElement && view.element.parentNode && parentElement) 210 view.detach(); 211 212 view.currentQuery = query; 213 view.performSearch(query, boundFinishedCallback); 214 } 215 216 processChunk(); 217 218 chunkIntervalIdentifier = setInterval(processChunk, 25); 219 this._currentSearchChunkIntervalIdentifier = chunkIntervalIdentifier; 220 }, 221 222 jumpToNextSearchResult: function() 223 { 224 if (!this.showView || !this._searchResults || !this._searchResults.length) 225 return; 226 227 var showFirstResult = false; 228 229 this._currentSearchResultIndex = this._searchResults.indexOf(this.visibleView); 230 if (this._currentSearchResultIndex === -1) { 231 this._currentSearchResultIndex = 0; 232 showFirstResult = true; 233 } 234 235 var currentView = this._searchResults[this._currentSearchResultIndex]; 236 237 if (currentView.showingLastSearchResult()) { 238 if (++this._currentSearchResultIndex >= this._searchResults.length) 239 this._currentSearchResultIndex = 0; 240 currentView = this._searchResults[this._currentSearchResultIndex]; 241 showFirstResult = true; 242 } 243 244 if (currentView !== this.visibleView) 245 this.showView(currentView); 246 247 if (showFirstResult) 248 currentView.jumpToFirstSearchResult(); 249 else 250 currentView.jumpToNextSearchResult(); 251 }, 252 253 jumpToPreviousSearchResult: function() 254 { 255 if (!this.showView || !this._searchResults || !this._searchResults.length) 256 return; 257 258 var showLastResult = false; 259 260 this._currentSearchResultIndex = this._searchResults.indexOf(this.visibleView); 261 if (this._currentSearchResultIndex === -1) { 262 this._currentSearchResultIndex = 0; 263 showLastResult = true; 264 } 265 266 var currentView = this._searchResults[this._currentSearchResultIndex]; 267 268 if (currentView.showingFirstSearchResult()) { 269 if (--this._currentSearchResultIndex < 0) 270 this._currentSearchResultIndex = (this._searchResults.length - 1); 271 currentView = this._searchResults[this._currentSearchResultIndex]; 272 showLastResult = true; 273 } 274 275 if (currentView !== this.visibleView) 276 this.showView(currentView); 277 278 if (showLastResult) 279 currentView.jumpToLastSearchResult(); 280 else 281 currentView.jumpToPreviousSearchResult(); 282 } 283} 284 285WebInspector.Panel.prototype.__proto__ = WebInspector.View.prototype; 286