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