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