• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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