• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (c) 2012 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'use strict';
6
7/**
8 * @fileoverview FindControl and FindController.
9 */
10base.require('tracing.timeline_track_view');
11base.require('tracing.filter');
12base.require('ui.overlay');
13base.exportTo('tracing', function() {
14
15  /**
16   * FindControl
17   * @constructor
18   * @extends {ui.Overlay}
19   */
20  var FindControl = ui.define('div');
21
22  FindControl.prototype = {
23    __proto__: ui.Overlay.prototype,
24
25    decorate: function() {
26      ui.Overlay.prototype.decorate.call(this);
27
28      this.className = 'find-control';
29
30      this.hitCountEl_ = document.createElement('div');
31      this.hitCountEl_.className = 'hit-count-label';
32      this.hitCountEl_.textContent = '1 of 7';
33
34      var findPreviousBn = document.createElement('div');
35      findPreviousBn.className = 'button find-previous';
36      findPreviousBn.textContent = '\u2190';
37      findPreviousBn.addEventListener('click', this.findPrevious_.bind(this));
38
39      var findNextBn = document.createElement('div');
40      findNextBn.className = 'button find-next';
41      findNextBn.textContent = '\u2192';
42      findNextBn.addEventListener('click', this.findNext_.bind(this));
43
44      // Filter input element.
45      this.filterEl_ = document.createElement('input');
46      this.filterEl_.type = 'input';
47
48      this.filterEl_.addEventListener('input',
49          this.filterTextChanged_.bind(this));
50
51      this.filterEl_.addEventListener('keydown', function(e) {
52        if (e.keyCode == 13) {
53          if (e.shiftKey)
54            this.findPrevious_();
55          else
56            this.findNext_();
57        } else if (e.keyCode == 27) {
58          this.filterEl_.blur();
59          this.updateHitCountEl_();
60        }
61      }.bind(this));
62
63      this.filterEl_.addEventListener('blur', function(e) {
64        this.updateHitCountEl_();
65      }.bind(this));
66
67      this.filterEl_.addEventListener('focus', function(e) {
68        this.controller.reset();
69        this.filterTextChanged_();
70        this.filterEl_.select();
71      }.bind(this));
72
73      // Prevent that the input text is deselected after focusing the find
74      // control with the mouse.
75      this.filterEl_.addEventListener('mouseup', function(e) {
76        e.preventDefault();
77      });
78
79      // Attach everything.
80      this.appendChild(this.filterEl_);
81
82      this.appendChild(findPreviousBn);
83      this.appendChild(findNextBn);
84      this.appendChild(this.hitCountEl_);
85
86      this.updateHitCountEl_();
87    },
88
89    get controller() {
90      return this.controller_;
91    },
92
93    set controller(c) {
94      this.controller_ = c;
95      this.updateHitCountEl_();
96    },
97
98    focus: function() {
99      this.filterEl_.focus();
100    },
101
102    filterTextChanged_: function() {
103      this.controller.filterText = this.filterEl_.value;
104      this.updateHitCountEl_();
105    },
106
107    findNext_: function() {
108      if (this.controller)
109        this.controller.findNext();
110      this.updateHitCountEl_();
111    },
112
113    findPrevious_: function() {
114      if (this.controller)
115        this.controller.findPrevious();
116      this.updateHitCountEl_();
117    },
118
119    updateHitCountEl_: function() {
120      if (!this.controller || document.activeElement != this.filterEl_) {
121        this.hitCountEl_.textContent = '';
122        return;
123      }
124      var i = this.controller.currentHitIndex;
125      var n = this.controller.filterHits.length;
126      if (n == 0)
127        this.hitCountEl_.textContent = '0 of 0';
128      else
129        this.hitCountEl_.textContent = (i + 1) + ' of ' + n;
130    }
131  };
132
133  function FindController() {
134    this.timeline_ = undefined;
135    this.model_ = undefined;
136    this.filterText_ = '';
137    this.filterHits_ = new tracing.Selection();
138    this.filterHitsDirty_ = true;
139    this.currentHitIndex_ = -1;
140  };
141
142  FindController.prototype = {
143    __proto__: Object.prototype,
144
145    get timeline() {
146      return this.timeline_;
147    },
148
149    set timeline(t) {
150      this.timeline_ = t;
151      this.filterHitsDirty_ = true;
152    },
153
154    get filterText() {
155      return this.filterText_;
156    },
157
158    set filterText(f) {
159      if (f == this.filterText_)
160        return;
161      this.filterText_ = f;
162      this.filterHitsDirty_ = true;
163      this.showHits_(this.filterHits);
164    },
165
166    get filterHits() {
167      if (this.filterHitsDirty_) {
168        this.filterHitsDirty_ = false;
169        this.filterHits_.clear();
170        this.currentHitIndex_ = -1;
171
172        if (this.timeline_ && this.filterText.length) {
173          var filter = new tracing.TitleFilter(this.filterText);
174          this.timeline.addAllObjectsMatchingFilterToSelection(
175              filter, this.filterHits_);
176        }
177      }
178      return this.filterHits_;
179    },
180
181    get currentHitIndex() {
182      return this.currentHitIndex_;
183    },
184
185    showHits_: function(selection, zoom, pan) {
186      if (!this.timeline)
187        return;
188
189      this.timeline.selection = selection;
190
191      if (zoom)
192        this.timeline.zoomToSelection();
193      else if (pan)
194        this.timeline.panToSelection();
195    },
196
197    find_: function(dir) {
198      var firstHit = this.currentHitIndex_ === -1;
199      if (firstHit && dir < 0)
200        this.currentHitIndex_ = 0;
201
202      var N = this.filterHits.length;
203      this.currentHitIndex_ = (this.currentHitIndex_ + dir + N) % N;
204
205      // We allow the zoom level to change only on the first hit. But, when
206      // then cycling through subsequent changes, restrict it to panning.
207      var zoom = firstHit;
208      var pan = true;
209      var subSelection = this.filterHits.subSelection(this.currentHitIndex_);
210      this.showHits_(subSelection, zoom, pan);
211    },
212
213    findNext: function() {
214      this.find_(1);
215    },
216
217    findPrevious: function() {
218      this.find_(-1);
219    },
220
221    reset: function() {
222      this.filterText_ = '';
223      this.filterHitsDirty_ = true;
224    }
225  };
226
227  return {
228    FindControl: FindControl,
229    FindController: FindController
230  };
231});
232