Bri Prebilic Cole
Committed by Gerrit Code Review

ONOS-2325 - GUI -- Table View rows now flash yellow when their information updat…

…es. Minor device details panel bug fix.

Change-Id: I78eb0f90af00ce4484255d7e9e0c3c8a10a0eda7
......@@ -172,6 +172,32 @@
return true;
}
// returns true if the two objects have all the same properties
function sameObjProps(obj1, obj2) {
var key;
for (key in obj1) {
if (obj1.hasOwnProperty(key)) {
if (!(obj1[key] === obj2[key])) {
return false;
}
}
}
return true;
}
// returns true if the array contains the object
// does NOT use strict object reference equality,
// instead checks each property individually for equality
function containsObj(arr, obj) {
var i;
for (i = 0; i < arr.length; i++) {
if (sameObjProps(arr[i], obj)) {
return true;
}
}
return false;
}
// return the given string with the first character capitalized.
function cap(s) {
return s.toLowerCase().replace(/^[a-z]/, function (m) {
......@@ -227,6 +253,8 @@
inArray: inArray,
removeFromArray: removeFromArray,
isEmptyObject: isEmptyObject,
sameObjProps: sameObjProps,
containsObj: containsObj,
cap: cap,
noPx: noPx,
noPxStyle: noPxStyle,
......
......@@ -63,6 +63,17 @@ div.summary-list tr.no-data td {
background-color: #304860;
}
/* highlighting */
div.summary-list tr {
transition: background-color 500ms;
}
.light div.summary-list tr.data-change {
background-color: #FDFFDC;
}
.dark div.summary-list tr.data-change {
background-color: #5A5600;
}
div.summary-list td {
padding: 6px;
text-align: left;
......
......@@ -26,6 +26,7 @@
// constants
var tableIconTdSize = 33,
pdg = 22,
flashTime = 2000,
colWidth = 'col-width',
tableIcon = 'table-icon',
asc = 'asc',
......@@ -208,7 +209,46 @@
scope.$on('$destroy', function () {
resetSort();
});
}
};
}])
.directive('onosFlashChanges', ['$log', '$parse', '$timeout',
function ($log, $parse, $timeout) {
return function (scope, element, attrs) {
var rowData = $parse(attrs.row)(scope),
id = attrs.rowId,
tr = d3.select(element[0]),
multiRows = d3.selectAll('.multi-row'),
promise;
scope.$watchCollection('changedData', function (newData) {
angular.forEach(newData, function (item) {
function classMultiRows(b) {
if (!multiRows.empty()) {
multiRows.each(function () {
d3.select(this).classed('data-change', b);
});
}
}
if (rowData[id] === item[id]) {
tr.classed('data-change', true);
classMultiRows(true);
promise = $timeout(function () {
tr.classed('data-change', false);
classMultiRows(false);
}, flashTime);
}
});
});
scope.$on('$destroy', function () {
if (promise) {
$timeout.cancel(promise);
}
});
};
}]);
}());
......
......@@ -30,7 +30,8 @@
// {
// scope: $scope, <- controller scope
// tag: 'device', <- table identifier
// selCb: selCb <- row selection callback (optional)
// selCb: selCb, <- row selection callback (optional)
// respCb: respCb, <- websocket response callback (optional)
// query: params <- query parameters in URL (optional)
// }
// Note: selCb() is passed the row data model of the selected row,
......@@ -45,31 +46,54 @@
resp = o.tag + 'DataResponse',
onSel = fs.isF(o.selCb),
onResp = fs.isF(o.respCb),
oldTableData = [],
promise;
o.scope.tableData = [];
o.scope.changedData = [];
o.scope.sortParams = {};
o.scope.autoRefresh = true;
o.scope.autoRefreshTip = 'Toggle auto refresh';
// === websocket functions --------------------
// response
function respCb(data) {
o.scope.tableData = data[root];
onResp && onResp();
o.scope.$apply();
// checks if data changed for row flashing
if (!angular.equals(o.scope.tableData, oldTableData)) {
o.scope.changedData = [];
// only flash the row if the data already exists
if (oldTableData.length) {
angular.forEach(o.scope.tableData, function (item) {
if (!fs.containsObj(oldTableData, item)) {
o.scope.changedData.push(item);
}
});
}
angular.copy(o.scope.tableData, oldTableData);
}
}
handlers[resp] = respCb;
wss.bindHandlers(handlers);
// request
function sortCb(params) {
var p = angular.extend({}, params, o.query);
wss.sendEvent(req, p);
}
o.scope.sortCallback = sortCb;
// === selecting a row functions ----------------
function selCb($event, selRow) {
o.scope.selId = (o.scope.selId === selRow.id) ? null : selRow.id;
onSel && onSel($event, selRow);
}
o.scope.selectCallback = selCb;
// === autoRefresh functions ------------------
function startRefresh() {
promise = $interval(function () {
if (fs.debugOn('widget')) {
......@@ -92,10 +116,7 @@
}
o.scope.toggleRefresh = toggleRefresh;
handlers[resp] = respCb;
wss.bindHandlers(handlers);
// Cleanup on destroyed scope
// === Cleanup on destroyed scope -----------------
o.scope.$on('$destroy', function () {
wss.unbindHandlers(handlers);
stopRefresh();
......
......@@ -60,7 +60,8 @@
<tr ng-repeat="app in tableData track by $index"
ng-click="selectCallback($event, app)"
ng-class="{selected: app.id === selId}">
ng-class="{selected: app.id === selId}"
onos-flash-changes row="{{app}}" row-id="id">
<td class="table-icon">
<div icon icon-id="{{app._iconid_state}}"></div>
</td>
......
......@@ -48,7 +48,8 @@
</td>
</tr>
<tr ng-repeat="node in tableData track by $index">
<tr ng-repeat="node in tableData track by $index"
onos-flash-changes row="{{node}}" row-id="id">
<td class="table-icon">
<div icon icon-id="{{node._iconid_state}}"></div>
</td>
......
......@@ -54,7 +54,8 @@
<tr ng-repeat="dev in tableData track by $index"
ng-click="selectCallback($event, dev)"
ng-class="{selected: dev.id === selId}">
ng-class="{selected: dev.id === selId}"
onos-flash-changes row="{{dev}}" row-id="id">
<td class="table-icon">
<div icon icon-id="{{dev._iconid_available}}"></div>
</td>
......
......@@ -250,6 +250,7 @@
.directive('deviceDetailsPanel', ['$rootScope', '$window',
function ($rootScope, $window) {
return function (scope) {
var unbindWatch;
function heightCalc() {
pStartY = fs.noPxStyle(d3.select('.tabular-header'), 'height')
......@@ -268,7 +269,7 @@
}
});
$rootScope.$watchCollection(
unbindWatch = $rootScope.$watchCollection(
function () {
return {
h: $window.innerHeight,
......@@ -283,6 +284,7 @@
);
scope.$on('$destroy', function () {
unbindWatch();
ps.destroyPanel(pName);
});
};
......
......@@ -61,6 +61,24 @@
background-color: #333;
}
/* highlighted color */
.light #ov-flow tr:nth-child(6n + 1).data-change,
.light #ov-flow tr:nth-child(6n + 2).data-change,
.light #ov-flow tr:nth-child(6n + 3).data-change,
.light #ov-flow tr:nth-child(6n + 4).data-change,
.light #ov-flow tr:nth-child(6n + 5).data-change,
.light #ov-flow tr:nth-child(6n).data-change {
background-color: #FDFFDC;
}
.dark #ov-flow tr:nth-child(6n + 1).data-change,
.dark #ov-flow tr:nth-child(6n + 2).data-change,
.dark #ov-flow tr:nth-child(6n + 3).data-change,
.dark #ov-flow tr:nth-child(6n + 4).data-change,
.dark #ov-flow tr:nth-child(6n + 5).data-change,
.dark #ov-flow tr:nth-child(6n).data-change {
background-color: #5A5600;
}
#ov-flow td.selector,
#ov-flow td.treatment {
padding-left: 36px;
......
......@@ -55,7 +55,8 @@
</td>
</tr>
<tr ng-repeat-start="flow in tableData track by $index">
<tr ng-repeat-start="flow in tableData track by $index"
onos-flash-changes row="{{flow}}" row-id="id">
<td>{{flow.id}}</td>
<td>{{flow.appId}}</td>
<td>{{flow.groupId}}</td>
......@@ -67,10 +68,10 @@
<td>{{flow.packets}}</td>
<td>{{flow.bytes}}</td>
</tr>
<tr>
<tr class="multi-row">
<td class="selector" colspan="10">{{flow.selector}}</td>
</tr>
<tr ng-repeat-end>
<tr class="multi-row" ng-repeat-end>
<td class="treatment" colspan="10">{{flow.treatment}}</td>
</tr>
</table>
......
......@@ -57,6 +57,20 @@
background-color: #333;
}
/* highlighted color */
.light #ov-group tr:nth-child(4n + 1).data-change,
.light #ov-group tr:nth-child(4n + 2).data-change,
.light #ov-group tr:nth-child(4n + 3).data-change,
.light #ov-group tr:nth-child(4n).data-change {
background-color: #FDFFDC;
}
.dark #ov-group tr:nth-child(4n + 1).data-change,
.dark #ov-group tr:nth-child(4n + 2).data-change,
.dark #ov-group tr:nth-child(4n + 3).data-change,
.dark #ov-group tr:nth-child(4n).data-change {
background-color: #5A5600;
}
#ov-group td.buckets {
padding-left: 36px;
opacity: 0.65;
......
......@@ -67,7 +67,8 @@
</td>
</tr>
<tr ng-repeat-start="group in tableData track by $index">
<tr ng-repeat-start="group in tableData track by $index"
onos-flash-changes row="{{group}}" row-id="id">
<td>{{group.id}}</td>
<td>{{group.app_id}}</td>
<td>{{group.state}}</td>
......@@ -75,7 +76,7 @@
<td>{{group.packets}}</td>
<td>{{group.bytes}}</td>
</tr>
<tr ng-repeat-end>
<tr class="multi-row" ng-repeat-end>
<td class="buckets" colspan="6"
ng-bind-html="group.buckets"></td>
</tr>
......
......@@ -33,7 +33,8 @@
</td>
</tr>
<tr ng-repeat="host in tableData track by $index">
<tr ng-repeat="host in tableData track by $index"
onos-flash-changes row="{{host}}" row-id="id">
<td class="table-icon">
<div icon icon-id="{{host._iconid_type}}"></div>
</td>
......
......@@ -47,6 +47,23 @@
background-color: #333;
}
.light #ov-intent tr:nth-child(6n + 1).data-change,
.light #ov-intent tr:nth-child(6n + 2).data-change,
.light #ov-intent tr:nth-child(6n + 3).data-change,
.light #ov-intent tr:nth-child(6n + 4).data-change,
.light #ov-intent tr:nth-child(6n + 5).data-change,
.light #ov-intent tr:nth-child(6n).data-change {
background-color: #FDFFDC;
}
.dark #ov-intent tr:nth-child(6n + 1).data-change,
.dark #ov-intent tr:nth-child(6n + 2).data-change,
.dark #ov-intent tr:nth-child(6n + 3).data-change,
.dark #ov-intent tr:nth-child(6n + 4).data-change,
.dark #ov-intent tr:nth-child(6n + 5).data-change,
.dark #ov-intent tr:nth-child(6n).data-change {
background-color: #5A5600;
}
#ov-intent td.resources,
#ov-intent td.details {
padding-left: 36px;
......
......@@ -48,17 +48,18 @@
</td>
</tr>
<tr ng-repeat-start="intent in tableData track by $index">
<tr ng-repeat-start="intent in tableData track by $index"
onos-flash-changes row="{{intent}}" row-id="key">
<td>{{intent.appId}}</td>
<td>{{intent.key}}</td>
<td>{{intent.type}}</td>
<td>{{intent.priority}}</td>
<td>{{intent.state}}</td>
</tr>
<tr>
<tr class="multi-row">
<td class="resources" colspan="5">{{intent.resources}}</td>
</tr>
<tr ng-repeat-end>
<tr class="multi-row" ng-repeat-end>
<td class="details" colspan="5">{{intent.details}}</td>
</tr>
</table>
......
......@@ -49,7 +49,8 @@
</td>
</tr>
<tr ng-repeat="link in tableData track by $index">
<tr ng-repeat="link in tableData track by $index"
onos-flash-changes row="{{link}}" row-id="one">
<td class="table-icon">
<div icon icon-id="{{link._iconid_state}}"></div>
</td>
......
......@@ -69,7 +69,8 @@
</td>
</tr>
<tr ng-repeat="port in tableData track by $index">
<tr ng-repeat="port in tableData track by $index"
onos-flash-changes row="{{port}}" row-id="id">
<td>{{port.id}}</td>
<td>{{port.pkt_rx}}</td>
<td>{{port.pkt_tx}}</td>
......
......@@ -33,7 +33,8 @@
</td>
</tr>
<tr ng-repeat="prop in tableData track by $index">
<tr ng-repeat="prop in tableData track by $index"
onos-flash-changes row="{{prop}}" row-id="id">
<td>{{prop.component}}</td>
<td>{{prop.id}}</td>
<td>{{prop.type}}</td>
......