Home | History | Annotate | Download | only in net_internals
      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 var SourceRow = (function() {
      6   'use strict';
      7 
      8   /**
      9    * A SourceRow represents the row corresponding to a single SourceEntry
     10    * displayed by the EventsView.
     11    *
     12    * @constructor
     13    */
     14   function SourceRow(parentView, sourceEntry) {
     15     this.parentView_ = parentView;
     16 
     17     this.sourceEntry_ = sourceEntry;
     18     this.isSelected_ = false;
     19     this.isMatchedByFilter_ = false;
     20 
     21     // Used to set CSS class for display.  Must only be modified by calling
     22     // corresponding set functions.
     23     this.isSelected_ = false;
     24     this.isMouseOver_ = false;
     25 
     26     // Mirror sourceEntry's values, so we only update the DOM when necessary.
     27     this.isError_ = sourceEntry.isError();
     28     this.isInactive_ = sourceEntry.isInactive();
     29     this.description_ = sourceEntry.getDescription();
     30 
     31     this.createRow_();
     32     this.onSourceUpdated();
     33   }
     34 
     35   SourceRow.prototype = {
     36     createRow_: function() {
     37       // Create a row.
     38       var tr = addNode(this.parentView_.tableBody_, 'tr');
     39       tr._id = this.getSourceId();
     40       tr.style.display = 'none';
     41       this.row_ = tr;
     42 
     43       var selectionCol = addNode(tr, 'td');
     44       var checkbox = addNode(selectionCol, 'input');
     45       selectionCol.style.borderLeft = '0';
     46       checkbox.type = 'checkbox';
     47 
     48       var idCell = addNode(tr, 'td');
     49       idCell.style.textAlign = 'right';
     50 
     51       var typeCell = addNode(tr, 'td');
     52       var descriptionCell = addNode(tr, 'td');
     53       this.descriptionCell_ = descriptionCell;
     54 
     55       // Connect listeners.
     56       checkbox.onchange = this.onCheckboxToggled_.bind(this);
     57 
     58       var onclick = this.onClicked_.bind(this);
     59       idCell.onclick = onclick;
     60       typeCell.onclick = onclick;
     61       descriptionCell.onclick = onclick;
     62 
     63       tr.onmouseover = this.onMouseover_.bind(this);
     64       tr.onmouseout = this.onMouseout_.bind(this);
     65 
     66       // Set the cell values to match this source's data.
     67       if (this.getSourceId() >= 0) {
     68         addTextNode(idCell, this.getSourceId());
     69       } else {
     70         addTextNode(idCell, '-');
     71       }
     72       var sourceTypeString = this.sourceEntry_.getSourceTypeString();
     73       addTextNode(typeCell, sourceTypeString);
     74       this.updateDescription_();
     75 
     76       // Add a CSS classname specific to this source type (so CSS can specify
     77       // different stylings for different types).
     78       var sourceTypeClass = sourceTypeString.toLowerCase().replace(/_/g, '-');
     79       this.row_.classList.add('source-' + sourceTypeClass);
     80 
     81       this.updateClass_();
     82     },
     83 
     84     onSourceUpdated: function() {
     85       if (this.sourceEntry_.isInactive() != this.isInactive_ ||
     86           this.sourceEntry_.isError() != this.isError_) {
     87         this.updateClass_();
     88       }
     89 
     90       if (this.description_ != this.sourceEntry_.getDescription())
     91         this.updateDescription_();
     92 
     93       // Update filters.
     94       var matchesFilter = this.parentView_.currentFilter_(this.sourceEntry_);
     95       this.setIsMatchedByFilter(matchesFilter);
     96     },
     97 
     98     /**
     99      * Changes |row_|'s class based on currently set flags.  Clears any previous
    100      * class set by this method.  This method is needed so that some styles
    101      * override others.
    102      */
    103     updateClass_: function() {
    104       this.isInactive_ = this.sourceEntry_.isInactive();
    105       this.isError_ = this.sourceEntry_.isError();
    106 
    107       // Each element of this list contains a property of |this| and the
    108       // corresponding class name to set if that property is true.  Entries
    109       // earlier in the list take precedence.
    110       var propertyNames = [
    111         ['isSelected_', 'selected'],
    112         ['isMouseOver_', 'mouseover'],
    113         ['isError_', 'error'],
    114         ['isInactive_', 'inactive'],
    115       ];
    116 
    117       // Loop through |propertyNames| in order, checking if each property
    118       // is true.  For the first such property found, if any, add the
    119       // corresponding class to the SourceEntry's row.  Remove classes
    120       // that correspond to any other property.
    121       var noStyleSet = true;
    122       for (var i = 0; i < propertyNames.length; ++i) {
    123         var setStyle = noStyleSet && this[propertyNames[i][0]];
    124         if (setStyle) {
    125           this.row_.classList.add(propertyNames[i][1]);
    126           noStyleSet = false;
    127         } else {
    128           this.row_.classList.remove(propertyNames[i][1]);
    129         }
    130       }
    131     },
    132 
    133     getSourceEntry: function() {
    134       return this.sourceEntry_;
    135     },
    136 
    137     setIsMatchedByFilter: function(isMatchedByFilter) {
    138       if (this.isMatchedByFilter() == isMatchedByFilter)
    139         return;  // No change.
    140 
    141       this.isMatchedByFilter_ = isMatchedByFilter;
    142 
    143       this.setFilterStyles(isMatchedByFilter);
    144 
    145       if (isMatchedByFilter) {
    146         this.parentView_.incrementPostfilterCount(1);
    147       } else {
    148         this.parentView_.incrementPostfilterCount(-1);
    149         // If we are filtering an entry away, make sure it is no longer
    150         // part of the selection.
    151         this.setSelected(false);
    152       }
    153     },
    154 
    155     isMatchedByFilter: function() {
    156       return this.isMatchedByFilter_;
    157     },
    158 
    159     setFilterStyles: function(isMatchedByFilter) {
    160       // Hide rows which have been filtered away.
    161       if (isMatchedByFilter) {
    162         this.row_.style.display = '';
    163       } else {
    164         this.row_.style.display = 'none';
    165       }
    166     },
    167 
    168     isSelected: function() {
    169       return this.isSelected_;
    170     },
    171 
    172     setSelected: function(isSelected) {
    173       if (isSelected == this.isSelected())
    174         return;
    175 
    176       this.isSelected_ = isSelected;
    177 
    178       this.setSelectedStyles(isSelected);
    179       this.parentView_.modifySelectionArray(this.getSourceId(), isSelected);
    180       this.parentView_.onSelectionChanged();
    181     },
    182 
    183     setSelectedStyles: function(isSelected) {
    184       this.isSelected_ = isSelected;
    185       this.getSelectionCheckbox().checked = isSelected;
    186       this.updateClass_();
    187     },
    188 
    189     setMouseoverStyle: function(isMouseOver) {
    190       this.isMouseOver_ = isMouseOver;
    191       this.updateClass_();
    192     },
    193 
    194     onClicked_: function() {
    195       this.parentView_.clearSelection();
    196       this.setSelected(true);
    197       if (this.isSelected())
    198         this.parentView_.scrollToSourceId(this.getSourceId());
    199     },
    200 
    201     onMouseover_: function() {
    202       this.setMouseoverStyle(true);
    203     },
    204 
    205     onMouseout_: function() {
    206       this.setMouseoverStyle(false);
    207     },
    208 
    209     updateDescription_: function() {
    210       this.description_ = this.sourceEntry_.getDescription();
    211       this.descriptionCell_.innerHTML = '';
    212       addTextNode(this.descriptionCell_, this.description_);
    213     },
    214 
    215     onCheckboxToggled_: function() {
    216       this.setSelected(this.getSelectionCheckbox().checked);
    217       if (this.isSelected())
    218         this.parentView_.scrollToSourceId(this.getSourceId());
    219     },
    220 
    221     getSelectionCheckbox: function() {
    222       return this.row_.childNodes[0].firstChild;
    223     },
    224 
    225     getSourceId: function() {
    226       return this.sourceEntry_.getSourceId();
    227     },
    228 
    229     /**
    230      * Returns source ID of the entry whose row is currently above this one's.
    231      * Returns null if no such node exists.
    232      */
    233     getPreviousNodeSourceId: function() {
    234       var prevNode = this.row_.previousSibling;
    235       if (prevNode == null)
    236         return null;
    237       return prevNode._id;
    238     },
    239 
    240     /**
    241      * Returns source ID of the entry whose row is currently below this one's.
    242      * Returns null if no such node exists.
    243      */
    244     getNextNodeSourceId: function() {
    245       var nextNode = this.row_.nextSibling;
    246       if (nextNode == null)
    247         return null;
    248       return nextNode._id;
    249     },
    250 
    251     /**
    252      * Moves current object's row before |entry|'s row.
    253      */
    254     moveBefore: function(entry) {
    255       this.row_.parentNode.insertBefore(this.row_, entry.row_);
    256     },
    257 
    258     /**
    259      * Moves current object's row after |entry|'s row.
    260      */
    261     moveAfter: function(entry) {
    262       this.row_.parentNode.insertBefore(this.row_, entry.row_.nextSibling);
    263     }
    264   };
    265 
    266   return SourceRow;
    267 })();
    268