• 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('timeline_track_view');
11base.require('filter');
12base.require('overlay');
13base.exportTo('tracing', function() {
14
15  /**
16   * FindControl
17   * @constructor
18   * @extends {tracing.ui.Overlay}
19   */
20  var FindControl = tracing.ui.define('div');
21
22  FindControl.prototype = {
23    __proto__: tracing.ui.Overlay.prototype,
24
25    decorate: function() {
26      tracing.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', function() {
38        this.controller.findPrevious();
39        this.updateHitCountEl_();
40      }.bind(this));
41
42      var findNextBn = document.createElement('div');
43      findNextBn.className = 'button find-next';
44      findNextBn.textContent = '\u2192';
45      findNextBn.addEventListener('click', function() {
46        this.controller.findNext();
47        this.updateHitCountEl_();
48      }.bind(this));
49
50      // Filter input element.
51      this.filterEl_ = document.createElement('input');
52      this.filterEl_.type = 'input';
53
54      this.filterEl_.addEventListener('input', function(e) {
55        this.controller.filterText = this.filterEl_.value;
56        this.updateHitCountEl_();
57      }.bind(this));
58
59      this.filterEl_.addEventListener('keydown', function(e) {
60        if (e.keyCode == 13) {
61          findNextBn.click();
62        } else if (e.keyCode == 27) {
63          this.filterEl_.blur();
64          this.updateHitCountEl_();
65        }
66      }.bind(this));
67
68      this.filterEl_.addEventListener('blur', function(e) {
69        this.updateHitCountEl_();
70      }.bind(this));
71
72      this.filterEl_.addEventListener('focus', function(e) {
73        this.updateHitCountEl_();
74      }.bind(this));
75
76      // Attach everything.
77      this.appendChild(this.filterEl_);
78
79      this.appendChild(findPreviousBn);
80      this.appendChild(findNextBn);
81      this.appendChild(this.hitCountEl_);
82
83      this.updateHitCountEl_();
84    },
85
86    get controller() {
87      return this.controller_;
88    },
89
90    set controller(c) {
91      this.controller_ = c;
92      this.updateHitCountEl_();
93    },
94
95    focus: function() {
96      this.filterEl_.selectionStart = 0;
97      this.filterEl_.selectionEnd = this.filterEl_.value.length;
98      this.filterEl_.focus();
99    },
100
101    updateHitCountEl_: function() {
102      if (!this.controller || document.activeElement != this.filterEl_) {
103        this.hitCountEl_.textContent = '';
104        return;
105      }
106      var i = this.controller.currentHitIndex;
107      var n = this.controller.filterHits.length;
108      if (n == 0)
109        this.hitCountEl_.textContent = '0 of 0';
110      else
111        this.hitCountEl_.textContent = (i + 1) + ' of ' + n;
112    }
113  };
114
115  function FindController() {
116    this.timeline_ = undefined;
117    this.model_ = undefined;
118    this.filterText_ = '';
119    this.filterHits_ = new tracing.Selection();
120    this.filterHitsDirty_ = true;
121    this.currentHitIndex_ = 0;
122  };
123
124  FindController.prototype = {
125    __proto__: Object.prototype,
126
127    get timeline() {
128      return this.timeline_;
129    },
130
131    set timeline(t) {
132      this.timeline_ = t;
133      this.filterHitsDirty_ = true;
134    },
135
136    get filterText() {
137      return this.filterText_;
138    },
139
140    set filterText(f) {
141      if (f == this.filterText_)
142        return;
143      this.filterText_ = f;
144      this.filterHitsDirty_ = true;
145      this.findNext();
146    },
147
148    get filterHits() {
149      if (this.filterHitsDirty_) {
150        this.filterHitsDirty_ = false;
151        if (this.timeline_) {
152          var filter = new tracing.TitleFilter(this.filterText);
153          this.filterHits_.clear();
154          this.timeline.addAllObjectsMatchingFilterToSelection(
155              filter, this.filterHits_);
156          this.currentHitIndex_ = this.filterHits_.length - 1;
157        } else {
158          this.filterHits_.clear();
159          this.currentHitIndex_ = 0;
160        }
161      }
162      return this.filterHits_;
163    },
164
165    get currentHitIndex() {
166      return this.currentHitIndex_;
167    },
168
169    find_: function(dir) {
170      if (!this.timeline)
171        return;
172
173      var N = this.filterHits.length;
174      this.currentHitIndex_ = this.currentHitIndex_ + dir;
175
176      if (this.currentHitIndex_ < 0) this.currentHitIndex_ = N - 1;
177      if (this.currentHitIndex_ >= N) this.currentHitIndex_ = 0;
178
179      if (this.currentHitIndex_ < 0 || this.currentHitIndex_ >= N) {
180        this.timeline.selection = new tracing.Selection();
181        return;
182      }
183
184      // We allow the zoom level to change on the first hit level. But, when
185      // then cycling through subsequent changes, restrict it to panning.
186      var zoomAllowed = this.currentHitIndex_ == 0;
187      var subSelection = this.filterHits.subSelection(this.currentHitIndex_);
188      this.timeline.setSelectionAndMakeVisible(subSelection, zoomAllowed);
189    },
190
191    findNext: function() {
192      this.find_(1);
193    },
194
195    findPrevious: function() {
196      this.find_(-1);
197    }
198  };
199
200  return {
201    FindControl: FindControl,
202    FindController: FindController
203  };
204});
205