Index: netlog_viewer/source_row.js |
diff --git a/netlog_viewer/source_row.js b/netlog_viewer/source_row.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..be2fd2b80af04acb73522718c8c6628e76e56af9 |
--- /dev/null |
+++ b/netlog_viewer/source_row.js |
@@ -0,0 +1,269 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+var SourceRow = (function() { |
+ 'use strict'; |
+ |
+ /** |
+ * A SourceRow represents the row corresponding to a single SourceEntry |
+ * displayed by the EventsView. |
+ * |
+ * @constructor |
+ */ |
+ function SourceRow(parentView, sourceEntry) { |
+ this.parentView_ = parentView; |
+ |
+ this.sourceEntry_ = sourceEntry; |
+ this.isSelected_ = false; |
+ this.isMatchedByFilter_ = false; |
+ |
+ // Used to set CSS class for display. Must only be modified by calling |
+ // corresponding set functions. |
+ this.isSelected_ = false; |
+ this.isMouseOver_ = false; |
+ |
+ // Mirror sourceEntry's values, so we only update the DOM when necessary. |
+ this.isError_ = sourceEntry.isError(); |
+ this.isInactive_ = sourceEntry.isInactive(); |
+ this.description_ = sourceEntry.getDescription(); |
+ |
+ this.createRow_(); |
+ this.onSourceUpdated(); |
+ } |
+ |
+ SourceRow.prototype = { |
+ createRow_: function() { |
+ // Create a row. |
+ var tr = addNode(this.parentView_.tableBody_, 'tr'); |
+ tr._id = this.getSourceId(); |
+ tr.style.display = 'none'; |
+ this.row_ = tr; |
+ |
+ var selectionCol = addNode(tr, 'td'); |
+ var checkbox = addNode(selectionCol, 'input'); |
+ checkbox.title = this.getSourceId(); |
+ selectionCol.style.borderLeft = '0'; |
+ checkbox.type = 'checkbox'; |
+ |
+ var idCell = addNode(tr, 'td'); |
+ idCell.style.textAlign = 'right'; |
+ |
+ var typeCell = addNode(tr, 'td'); |
+ var descriptionCell = addNode(tr, 'td'); |
+ this.descriptionCell_ = descriptionCell; |
+ |
+ // Connect listeners. |
+ checkbox.onchange = this.onCheckboxToggled_.bind(this); |
+ |
+ var onclick = this.onClicked_.bind(this); |
+ idCell.onclick = onclick; |
+ typeCell.onclick = onclick; |
+ descriptionCell.onclick = onclick; |
+ |
+ tr.onmouseover = this.onMouseover_.bind(this); |
+ tr.onmouseout = this.onMouseout_.bind(this); |
+ |
+ // Set the cell values to match this source's data. |
+ if (this.getSourceId() >= 0) { |
+ addTextNode(idCell, this.getSourceId()); |
+ } else { |
+ addTextNode(idCell, '-'); |
+ } |
+ var sourceTypeString = this.sourceEntry_.getSourceTypeString(); |
+ addTextNode(typeCell, sourceTypeString); |
+ this.updateDescription_(); |
+ |
+ // Add a CSS classname specific to this source type (so CSS can specify |
+ // different stylings for different types). |
+ var sourceTypeClass = sourceTypeString.toLowerCase().replace(/_/g, '-'); |
+ this.row_.classList.add('source-' + sourceTypeClass); |
+ |
+ this.updateClass_(); |
+ }, |
+ |
+ onSourceUpdated: function() { |
+ if (this.sourceEntry_.isInactive() != this.isInactive_ || |
+ this.sourceEntry_.isError() != this.isError_) { |
+ this.updateClass_(); |
+ } |
+ |
+ if (this.description_ != this.sourceEntry_.getDescription()) |
+ this.updateDescription_(); |
+ |
+ // Update filters. |
+ var matchesFilter = this.parentView_.currentFilter_(this.sourceEntry_); |
+ this.setIsMatchedByFilter(matchesFilter); |
+ }, |
+ |
+ /** |
+ * Changes |row_|'s class based on currently set flags. Clears any previous |
+ * class set by this method. This method is needed so that some styles |
+ * override others. |
+ */ |
+ updateClass_: function() { |
+ this.isInactive_ = this.sourceEntry_.isInactive(); |
+ this.isError_ = this.sourceEntry_.isError(); |
+ |
+ // Each element of this list contains a property of |this| and the |
+ // corresponding class name to set if that property is true. Entries |
+ // earlier in the list take precedence. |
+ var propertyNames = [ |
+ ['isSelected_', 'selected'], |
+ ['isMouseOver_', 'mouseover'], |
+ ['isError_', 'error'], |
+ ['isInactive_', 'inactive'], |
+ ]; |
+ |
+ // Loop through |propertyNames| in order, checking if each property |
+ // is true. For the first such property found, if any, add the |
+ // corresponding class to the SourceEntry's row. Remove classes |
+ // that correspond to any other property. |
+ var noStyleSet = true; |
+ for (var i = 0; i < propertyNames.length; ++i) { |
+ var setStyle = noStyleSet && this[propertyNames[i][0]]; |
+ if (setStyle) { |
+ this.row_.classList.add(propertyNames[i][1]); |
+ noStyleSet = false; |
+ } else { |
+ this.row_.classList.remove(propertyNames[i][1]); |
+ } |
+ } |
+ }, |
+ |
+ getSourceEntry: function() { |
+ return this.sourceEntry_; |
+ }, |
+ |
+ setIsMatchedByFilter: function(isMatchedByFilter) { |
+ if (this.isMatchedByFilter() == isMatchedByFilter) |
+ return; // No change. |
+ |
+ this.isMatchedByFilter_ = isMatchedByFilter; |
+ |
+ this.setFilterStyles(isMatchedByFilter); |
+ |
+ if (isMatchedByFilter) { |
+ this.parentView_.incrementPostfilterCount(1); |
+ } else { |
+ this.parentView_.incrementPostfilterCount(-1); |
+ // If we are filtering an entry away, make sure it is no longer |
+ // part of the selection. |
+ this.setSelected(false); |
+ } |
+ }, |
+ |
+ isMatchedByFilter: function() { |
+ return this.isMatchedByFilter_; |
+ }, |
+ |
+ setFilterStyles: function(isMatchedByFilter) { |
+ // Hide rows which have been filtered away. |
+ if (isMatchedByFilter) { |
+ this.row_.style.display = ''; |
+ } else { |
+ this.row_.style.display = 'none'; |
+ } |
+ }, |
+ |
+ isSelected: function() { |
+ return this.isSelected_; |
+ }, |
+ |
+ setSelected: function(isSelected) { |
+ if (isSelected == this.isSelected()) |
+ return; |
+ |
+ this.isSelected_ = isSelected; |
+ |
+ this.setSelectedStyles(isSelected); |
+ this.parentView_.modifySelectionArray(this.getSourceId(), isSelected); |
+ this.parentView_.onSelectionChanged(); |
+ }, |
+ |
+ setSelectedStyles: function(isSelected) { |
+ this.isSelected_ = isSelected; |
+ this.getSelectionCheckbox().checked = isSelected; |
+ this.updateClass_(); |
+ }, |
+ |
+ setMouseoverStyle: function(isMouseOver) { |
+ this.isMouseOver_ = isMouseOver; |
+ this.updateClass_(); |
+ }, |
+ |
+ onClicked_: function() { |
+ this.parentView_.clearSelection(); |
+ this.setSelected(true); |
+ if (this.isSelected()) |
+ this.parentView_.scrollToSourceId(this.getSourceId()); |
+ }, |
+ |
+ onMouseover_: function() { |
+ this.setMouseoverStyle(true); |
+ }, |
+ |
+ onMouseout_: function() { |
+ this.setMouseoverStyle(false); |
+ }, |
+ |
+ updateDescription_: function() { |
+ this.description_ = this.sourceEntry_.getDescription(); |
+ this.descriptionCell_.innerHTML = ''; |
+ addTextNode(this.descriptionCell_, this.description_); |
+ }, |
+ |
+ onCheckboxToggled_: function() { |
+ this.setSelected(this.getSelectionCheckbox().checked); |
+ if (this.isSelected()) |
+ this.parentView_.scrollToSourceId(this.getSourceId()); |
+ }, |
+ |
+ getSelectionCheckbox: function() { |
+ return this.row_.childNodes[0].firstChild; |
+ }, |
+ |
+ getSourceId: function() { |
+ return this.sourceEntry_.getSourceId(); |
+ }, |
+ |
+ /** |
+ * Returns source ID of the entry whose row is currently above this one's. |
+ * Returns null if no such node exists. |
+ */ |
+ getPreviousNodeSourceId: function() { |
+ var prevNode = this.row_.previousSibling; |
+ if (prevNode == null) |
+ return null; |
+ return prevNode._id; |
+ }, |
+ |
+ /** |
+ * Returns source ID of the entry whose row is currently below this one's. |
+ * Returns null if no such node exists. |
+ */ |
+ getNextNodeSourceId: function() { |
+ var nextNode = this.row_.nextSibling; |
+ if (nextNode == null) |
+ return null; |
+ return nextNode._id; |
+ }, |
+ |
+ /** |
+ * Moves current object's row before |entry|'s row. |
+ */ |
+ moveBefore: function(entry) { |
+ this.row_.parentNode.insertBefore(this.row_, entry.row_); |
+ }, |
+ |
+ /** |
+ * Moves current object's row after |entry|'s row. |
+ */ |
+ moveAfter: function(entry) { |
+ this.row_.parentNode.insertBefore(this.row_, entry.row_.nextSibling); |
+ } |
+ }; |
+ |
+ return SourceRow; |
+})(); |
+ |