| <!DOCTYPE html> |
| <!-- |
| Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| Use of this source code is governed by a BSD-style license that can be |
| found in the LICENSE file. |
| --> |
| |
| <link rel="import" href="/base/task.html"> |
| <link rel="import" href="/core/filter.html"> |
| <link rel="import" href="/core/selection.html"> |
| |
| <script> |
| 'use strict'; |
| |
| /** |
| * @fileoverview FindController. |
| */ |
| tr.exportTo('tr.c', function() { |
| var Task = tr.b.Task; |
| |
| function FindController(selectionController) { |
| this.selectionController_ = selectionController; |
| this.filterHits_ = new tr.c.Selection(); |
| this.currentHitIndex_ = -1; |
| this.activePromise_ = Promise.resolve(); |
| this.activeTask_ = undefined; |
| }; |
| |
| FindController.prototype = { |
| __proto__: Object.prototype, |
| |
| get model() { |
| return this.selectionController_.model; |
| }, |
| |
| get selectionController() { |
| return this.selectionController_; |
| }, |
| |
| enqueueOperation_: function(operation) { |
| var task; |
| if (operation instanceof tr.b.Task) |
| task = operation; |
| else |
| task = new tr.b.Task(operation, this); |
| if (this.activeTask_) { |
| this.activeTask_ = this.activeTask_.enqueue(task); |
| } else { |
| // We're enqueuing the first task, schedule it. |
| this.activeTask_ = task; |
| this.activePromise_ = Task.RunWhenIdle(this.activeTask_); |
| this.activePromise_.then(function() { |
| this.activePromise_ = undefined; |
| this.activeTask_ = undefined; |
| }.bind(this)); |
| } |
| }, |
| |
| /** |
| * Updates the filter hits based on the provided |filterText|. Returns a |
| * promise which resolves when |filterHits| has been refreshed. |
| */ |
| startFiltering: function(filterText) { |
| var sc = this.selectionController_; |
| if (!sc) |
| return; |
| |
| // TODO(beaudoin): Cancel anything left in the task queue, without |
| // invalidating the promise. |
| this.enqueueOperation_(function() { |
| this.filterHits_ = new tr.c.Selection(); |
| this.currentHitIndex_ = -1; |
| }.bind(this)); |
| |
| // Try constructing a UIState from the filterText. |
| // UIState.fromUserFriendlyString will throw an error only if the string |
| // is syntactically correct to a UI state string but with invalid values. |
| // It will return undefined if there is no syntactic match. |
| var stateFromString; |
| try { |
| stateFromString = sc.uiStateFromString(filterText); |
| } catch (e) { |
| this.enqueueOperation_(function() { |
| var overlay = new tr.b.ui.Overlay(); |
| overlay.textContent = e.message; |
| overlay.title = 'UI State Navigation Error'; |
| overlay.visible = true; |
| }); |
| return this.activePromise_; |
| } |
| |
| if (stateFromString !== undefined) { |
| this.enqueueOperation_( |
| sc.navToPosition.bind(this, stateFromString, true)); |
| } else { |
| // filterText is not a navString here -- proceed with find and filter. |
| if (filterText.length === 0) { |
| this.enqueueOperation_(sc.findTextCleared.bind(sc)); |
| } else { |
| var filter = new tr.c.TitleOrCategoryFilter(filterText); |
| var filterHits = new tr.c.Selection(); |
| this.enqueueOperation_(sc.addAllEventsMatchingFilterToSelectionAsTask( |
| filter, filterHits)); |
| this.enqueueOperation_(function() { |
| this.filterHits_ = filterHits; |
| sc.findTextChangedTo(filterHits); |
| }.bind(this)); |
| } |
| } |
| return this.activePromise_; |
| }, |
| |
| /** |
| * Returns the most recent filter hits as a tr.c.Selection. Call |
| * |startFiltering| to ensure this is up to date after the filter settings |
| * have been changed. |
| */ |
| get filterHits() { |
| return this.filterHits_; |
| }, |
| |
| get currentHitIndex() { |
| return this.currentHitIndex_; |
| }, |
| |
| find_: function(dir) { |
| var firstHit = this.currentHitIndex_ === -1; |
| if (firstHit && dir < 0) |
| this.currentHitIndex_ = 0; |
| |
| var N = this.filterHits.length; |
| this.currentHitIndex_ = (this.currentHitIndex_ + dir + N) % N; |
| |
| if (!this.selectionController_) |
| return; |
| |
| this.selectionController_.findFocusChangedTo( |
| this.filterHits.subSelection(this.currentHitIndex_, 1)); |
| }, |
| |
| findNext: function() { |
| this.find_(1); |
| }, |
| |
| findPrevious: function() { |
| this.find_(-1); |
| } |
| }; |
| |
| return { |
| FindController: FindController |
| }; |
| }); |
| </script> |