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. 4 5/** 6 * @constructor 7 * @extends {WebInspector.VBox} 8 */ 9WebInspector.AdvancedSearchView = function() 10{ 11 WebInspector.VBox.call(this); 12 13 this._searchId = 0; 14 15 this.element.classList.add("search-view"); 16 17 this._searchPanelElement = this.element.createChild("div", "search-drawer-header"); 18 this._searchPanelElement.addEventListener("keydown", this._onKeyDown.bind(this), false); 19 20 this._searchResultsElement = this.element.createChild("div"); 21 this._searchResultsElement.className = "search-results"; 22 23 this._search = this._searchPanelElement.createChild("input"); 24 this._search.placeholder = WebInspector.UIString("Search sources"); 25 this._search.setAttribute("type", "text"); 26 this._search.classList.add("search-config-search"); 27 this._search.setAttribute("results", "0"); 28 this._search.setAttribute("size", 30); 29 30 this._ignoreCaseLabel = this._searchPanelElement.createChild("label"); 31 this._ignoreCaseLabel.classList.add("search-config-label"); 32 this._ignoreCaseCheckbox = this._ignoreCaseLabel.createChild("input"); 33 this._ignoreCaseCheckbox.setAttribute("type", "checkbox"); 34 this._ignoreCaseCheckbox.classList.add("search-config-checkbox"); 35 this._ignoreCaseLabel.appendChild(document.createTextNode(WebInspector.UIString("Ignore case"))); 36 37 this._regexLabel = this._searchPanelElement.createChild("label"); 38 this._regexLabel.classList.add("search-config-label"); 39 this._regexCheckbox = this._regexLabel.createChild("input"); 40 this._regexCheckbox.setAttribute("type", "checkbox"); 41 this._regexCheckbox.classList.add("search-config-checkbox"); 42 this._regexLabel.appendChild(document.createTextNode(WebInspector.UIString("Regular expression"))); 43 44 this._searchStatusBarElement = this.element.createChild("div", "search-status-bar-summary"); 45 this._searchMessageElement = this._searchStatusBarElement.createChild("span"); 46 this._searchResultsMessageElement = document.createElement("span"); 47 48 WebInspector.settings.advancedSearchConfig = WebInspector.settings.createSetting("advancedSearchConfig", new WebInspector.SearchConfig("", true, false).toPlainObject()); 49 this._load(); 50} 51 52WebInspector.AdvancedSearchView.prototype = { 53 /** 54 * @return {!WebInspector.SearchConfig} 55 */ 56 _buildSearchConfig: function() 57 { 58 return new WebInspector.SearchConfig(this._search.value, this._ignoreCaseCheckbox.checked, this._regexCheckbox.checked); 59 }, 60 61 toggle: function() 62 { 63 var selection = window.getSelection(); 64 var queryCandidate; 65 if (selection.rangeCount) 66 queryCandidate = selection.toString().replace(/\r?\n.*/, ""); 67 68 if (!this.isShowing()) 69 WebInspector.inspectorView.showViewInDrawer("search"); 70 if (queryCandidate) 71 this._search.value = queryCandidate; 72 this.focus(); 73 74 this._startIndexing(); 75 }, 76 77 /** 78 * @param {boolean} finished 79 */ 80 _onIndexingFinished: function(finished) 81 { 82 delete this._isIndexing; 83 this._indexingFinished(finished); 84 if (!finished) 85 delete this._pendingSearchConfig; 86 if (!this._pendingSearchConfig) 87 return; 88 var searchConfig = this._pendingSearchConfig 89 delete this._pendingSearchConfig; 90 this._innerStartSearch(searchConfig); 91 }, 92 93 _startIndexing: function() 94 { 95 this._isIndexing = true; 96 // FIXME: this._currentSearchScope should be initialized based on searchConfig 97 this._currentSearchScope = this._searchScopes()[0]; 98 if (this._progressIndicator) 99 this._progressIndicator.done(); 100 this._progressIndicator = new WebInspector.ProgressIndicator(); 101 this._indexingStarted(this._progressIndicator); 102 this._currentSearchScope.performIndexing(this._progressIndicator, this._onIndexingFinished.bind(this)); 103 }, 104 105 /** 106 * @param {number} searchId 107 * @param {!WebInspector.FileBasedSearchResult} searchResult 108 */ 109 _onSearchResult: function(searchId, searchResult) 110 { 111 if (searchId !== this._searchId) 112 return; 113 this._addSearchResult(searchResult); 114 if (!searchResult.searchMatches.length) 115 return; 116 if (!this._searchResultsPane) 117 this._searchResultsPane = this._currentSearchScope.createSearchResultsPane(this._searchConfig); 118 this._resetResults(); 119 this._searchResultsElement.appendChild(this._searchResultsPane.element); 120 this._searchResultsPane.addSearchResult(searchResult); 121 }, 122 123 /** 124 * @param {number} searchId 125 * @param {boolean} finished 126 */ 127 _onSearchFinished: function(searchId, finished) 128 { 129 if (searchId !== this._searchId) 130 return; 131 if (!this._searchResultsPane) 132 this._nothingFound(); 133 this._searchFinished(finished); 134 delete this._searchConfig; 135 }, 136 137 /** 138 * @param {!WebInspector.SearchConfig} searchConfig 139 */ 140 _startSearch: function(searchConfig) 141 { 142 this._resetSearch(); 143 ++this._searchId; 144 if (!this._isIndexing) 145 this._startIndexing(); 146 this._pendingSearchConfig = searchConfig; 147 }, 148 149 /** 150 * @param {!WebInspector.SearchConfig} searchConfig 151 */ 152 _innerStartSearch: function(searchConfig) 153 { 154 this._searchConfig = searchConfig; 155 // FIXME: this._currentSearchScope should be initialized based on searchConfig 156 this._currentSearchScope = this._searchScopes()[0]; 157 158 if (this._progressIndicator) 159 this._progressIndicator.done(); 160 this._progressIndicator = new WebInspector.ProgressIndicator(); 161 this._searchStarted(this._progressIndicator); 162 this._currentSearchScope.performSearch(searchConfig, this._progressIndicator, this._onSearchResult.bind(this, this._searchId), this._onSearchFinished.bind(this, this._searchId)); 163 }, 164 165 _resetSearch: function() 166 { 167 this._stopSearch(); 168 169 if (this._searchResultsPane) { 170 this._resetResults(); 171 delete this._searchResultsPane; 172 } 173 }, 174 175 _stopSearch: function() 176 { 177 if (this._progressIndicator) 178 this._progressIndicator.cancel(); 179 if (this._currentSearchScope) 180 this._currentSearchScope.stopSearch(); 181 delete this._searchConfig; 182 }, 183 184 /** 185 * @return {!Array.<!WebInspector.SearchScope>} 186 */ 187 _searchScopes: function() 188 { 189 // FIXME: implement multiple search scopes. 190 return /** @type {!Array.<!WebInspector.SearchScope>} */ (WebInspector.moduleManager.instances(WebInspector.SearchScope)); 191 }, 192 193 /** 194 * @param {!WebInspector.ProgressIndicator} progressIndicator 195 */ 196 _searchStarted: function(progressIndicator) 197 { 198 this._resetResults(); 199 this._resetCounters(); 200 201 this._searchMessageElement.textContent = WebInspector.UIString("Searching..."); 202 progressIndicator.show(this._searchStatusBarElement); 203 this._updateSearchResultsMessage(); 204 205 if (!this._searchingView) 206 this._searchingView = new WebInspector.EmptyView(WebInspector.UIString("Searching...")); 207 this._searchingView.show(this._searchResultsElement); 208 }, 209 210 /** 211 * @param {!WebInspector.ProgressIndicator} progressIndicator 212 */ 213 _indexingStarted: function(progressIndicator) 214 { 215 this._searchMessageElement.textContent = WebInspector.UIString("Indexing..."); 216 progressIndicator.show(this._searchStatusBarElement); 217 }, 218 219 /** 220 * @param {boolean} finished 221 */ 222 _indexingFinished: function(finished) 223 { 224 this._searchMessageElement.textContent = finished ? "" : WebInspector.UIString("Indexing interrupted."); 225 }, 226 227 _updateSearchResultsMessage: function() 228 { 229 if (this._searchMatchesCount && this._searchResultsCount) 230 this._searchResultsMessageElement.textContent = WebInspector.UIString("Found %d matches in %d files.", this._searchMatchesCount, this._nonEmptySearchResultsCount); 231 else 232 this._searchResultsMessageElement.textContent = ""; 233 }, 234 235 _resetResults: function() 236 { 237 if (this._searchingView) 238 this._searchingView.detach(); 239 if (this._notFoundView) 240 this._notFoundView.detach(); 241 this._searchResultsElement.removeChildren(); 242 }, 243 244 _resetCounters: function() 245 { 246 this._searchMatchesCount = 0; 247 this._searchResultsCount = 0; 248 this._nonEmptySearchResultsCount = 0; 249 }, 250 251 _nothingFound: function() 252 { 253 this._resetResults(); 254 255 if (!this._notFoundView) 256 this._notFoundView = new WebInspector.EmptyView(WebInspector.UIString("No matches found.")); 257 this._notFoundView.show(this._searchResultsElement); 258 this._searchResultsMessageElement.textContent = WebInspector.UIString("No matches found."); 259 }, 260 261 /** 262 * @param {!WebInspector.FileBasedSearchResult} searchResult 263 */ 264 _addSearchResult: function(searchResult) 265 { 266 this._searchMatchesCount += searchResult.searchMatches.length; 267 this._searchResultsCount++; 268 if (searchResult.searchMatches.length) 269 this._nonEmptySearchResultsCount++; 270 this._updateSearchResultsMessage(); 271 }, 272 273 /** 274 * @param {boolean} finished 275 */ 276 _searchFinished: function(finished) 277 { 278 this._searchMessageElement.textContent = finished ? WebInspector.UIString("Search finished.") : WebInspector.UIString("Search interrupted."); 279 }, 280 281 focus: function() 282 { 283 WebInspector.setCurrentFocusElement(this._search); 284 this._search.select(); 285 }, 286 287 willHide: function() 288 { 289 this._stopSearch(); 290 }, 291 292 /** 293 * @param {?Event} event 294 */ 295 _onKeyDown: function(event) 296 { 297 switch (event.keyCode) { 298 case WebInspector.KeyboardShortcut.Keys.Enter.code: 299 this._onAction(); 300 break; 301 } 302 }, 303 304 _save: function() 305 { 306 WebInspector.settings.advancedSearchConfig.set(this._buildSearchConfig().toPlainObject()); 307 }, 308 309 _load: function() 310 { 311 var searchConfig = WebInspector.SearchConfig.fromPlainObject(WebInspector.settings.advancedSearchConfig.get()); 312 this._search.value = searchConfig.query(); 313 this._ignoreCaseCheckbox.checked = searchConfig.ignoreCase(); 314 this._regexCheckbox.checked = searchConfig.isRegex(); 315 }, 316 317 _onAction: function() 318 { 319 var searchConfig = this._buildSearchConfig(); 320 if (!searchConfig.query() || !searchConfig.query().length) 321 return; 322 323 this._save(); 324 this._startSearch(searchConfig); 325 }, 326 327 __proto__: WebInspector.VBox.prototype 328} 329 330/** 331 * @constructor 332 * @param {!WebInspector.ProjectSearchConfig} searchConfig 333 */ 334WebInspector.SearchResultsPane = function(searchConfig) 335{ 336 this._searchConfig = searchConfig; 337 this.element = document.createElement("div"); 338} 339 340WebInspector.SearchResultsPane.prototype = { 341 /** 342 * @return {!WebInspector.ProjectSearchConfig} 343 */ 344 get searchConfig() 345 { 346 return this._searchConfig; 347 }, 348 349 /** 350 * @param {!WebInspector.FileBasedSearchResult} searchResult 351 */ 352 addSearchResult: function(searchResult) { } 353} 354 355/** 356 * @constructor 357 * @implements {WebInspector.ActionDelegate} 358 */ 359WebInspector.AdvancedSearchView.ToggleDrawerViewActionDelegate = function() 360{ 361} 362 363WebInspector.AdvancedSearchView.ToggleDrawerViewActionDelegate.prototype = { 364 /** 365 * @return {boolean} 366 */ 367 handleAction: function() 368 { 369 var searchView = this._searchView(); 370 if (!searchView) 371 return false; 372 if (!searchView.isShowing() || searchView._search !== document.activeElement) { 373 WebInspector.inspectorView.showPanel("sources"); 374 searchView.toggle(); 375 } else { 376 WebInspector.inspectorView.closeDrawer(); 377 } 378 return true; 379 }, 380 381 /** 382 * @return {?WebInspector.AdvancedSearchView} 383 */ 384 _searchView: function() 385 { 386 if (!this._view) { 387 var extensions = WebInspector.moduleManager.extensions("drawer-view"); 388 for (var i = 0; i < extensions.length; ++i) { 389 if (extensions[i].descriptor()["name"] === "search") { 390 this._view = extensions[i].instance(); 391 break; 392 } 393 } 394 } 395 return this._view; 396 } 397} 398 399 400/** 401 * @constructor 402 * @param {!WebInspector.UISourceCode} uiSourceCode 403 * @param {!Array.<!Object>} searchMatches 404 */ 405WebInspector.FileBasedSearchResult = function(uiSourceCode, searchMatches) { 406 this.uiSourceCode = uiSourceCode; 407 this.searchMatches = searchMatches; 408} 409 410/** 411 * @interface 412 */ 413WebInspector.SearchScope = function() 414{ 415} 416 417WebInspector.SearchScope.prototype = { 418 /** 419 * @param {!WebInspector.SearchConfig} searchConfig 420 * @param {!WebInspector.Progress} progress 421 * @param {function(!WebInspector.FileBasedSearchResult)} searchResultCallback 422 * @param {function(boolean)} searchFinishedCallback 423 */ 424 performSearch: function(searchConfig, progress, searchResultCallback, searchFinishedCallback) { }, 425 426 /** 427 * @param {!WebInspector.Progress} progress 428 * @param {function(boolean)} callback 429 */ 430 performIndexing: function(progress, callback) { }, 431 432 stopSearch: function() { }, 433 434 /** 435 * @param {!WebInspector.ProjectSearchConfig} searchConfig 436 * @return {!WebInspector.SearchResultsPane} 437 */ 438 createSearchResultsPane: function(searchConfig) { } 439} 440 441importScript("FileBasedSearchResultsPane.js"); 442importScript("SourcesSearchScope.js"); 443