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