Home | History | Annotate | Download | only in sources
      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