blob: 637c8aa94e41332c8fe9aeb1461c93d3fb1c847f [file] [log] [blame]
<!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>