Home | History | Annotate | Download | only in tracing
      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  */
     10 base.require('tracing.timeline_track_view');
     11 base.require('tracing.filter');
     12 base.require('ui.overlay');
     13 base.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