blob: 327aedb9673f9fbdd91d913c77caee5c2e17a476 [file] [log] [blame]
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
ONOS GUI -- Widget -- Table Service
*/
(function () {
'use strict';
// injected refs
var $log, $window, fs, mast, is;
// constants
var tableIconTdSize = 33,
pdg = 22,
flashTime = 1500,
colWidth = 'col-width',
tableIcon = 'table-icon',
asc = 'asc',
desc = 'desc',
none = 'none';
// internal state
var currCol = {},
prevCol = {},
cstmWidths = {},
sortIconAPI;
// Functions for resizing a tabular view to the window
function _width(elem, width) {
elem.style('width', width);
}
function findCstmWidths(table) {
var headers = table.select('.table-header').selectAll('td');
headers.each(function (d, i) {
var h = d3.select(this),
index = i.toString();
if (h.classed(tableIcon)) {
cstmWidths[index] = tableIconTdSize + 'px';
}
if (h.attr(colWidth)) {
cstmWidths[index] = h.attr(colWidth);
}
});
if (fs.debugOn('widget')) {
$log.debug('Headers with custom widths: ', cstmWidths);
}
}
function setTdWidths(elem, width) {
var tds = elem.select('tr:first-child').selectAll('td');
_width(elem, width + 'px');
tds.each(function (d, i) {
var td = d3.select(this),
index = i.toString();
if (cstmWidths.hasOwnProperty(index)) {
_width(td, cstmWidths[index]);
}
});
}
function setHeight(thead, body, height) {
var h = height - (mast.mastHeight() +
fs.noPxStyle(d3.select('.tabular-header'), 'height') +
fs.noPxStyle(thead, 'height') + pdg);
body.style('height', h + 'px');
}
function adjustTable(haveItems, tableElems, width, height) {
if (haveItems) {
setTdWidths(tableElems.thead, width);
setTdWidths(tableElems.tbody, width);
setHeight(tableElems.thead, tableElems.table.select('.table-body'), height);
} else {
setTdWidths(tableElems.thead, width);
_width(tableElems.tbody, width + 'px');
}
}
// Functions for sorting table rows by header
function updateSortDirection(thElem) {
sortIconAPI.sortNone(thElem.select('div'));
currCol.div = thElem.append('div');
currCol.colId = thElem.attr('colId');
if (currCol.colId === prevCol.colId) {
(currCol.dir === desc) ? currCol.dir = asc : currCol.dir = desc;
prevCol.dir = currCol.dir;
} else {
currCol.dir = asc;
prevCol.dir = none;
}
(currCol.dir === asc) ?
sortIconAPI.sortAsc(currCol.div) : sortIconAPI.sortDesc(currCol.div);
if (prevCol.colId && prevCol.dir === none) {
sortIconAPI.sortNone(prevCol.div);
}
prevCol.colId = currCol.colId;
prevCol.div = currCol.div;
}
function sortRequestParams() {
return {
sortCol: currCol.colId,
sortDir: currCol.dir
};
}
function resetSort() {
if (currCol.div) {
sortIconAPI.sortNone(currCol.div);
}
if (prevCol.div) {
sortIconAPI.sortNone(prevCol.div);
}
currCol = {};
prevCol = {};
}
angular.module('onosWidget')
.directive('onosTableResize', ['$log','$window',
'FnService', 'MastService',
function (_$log_, _$window_, _fs_, _mast_) {
return function (scope, element) {
$log = _$log_;
$window = _$window_;
fs = _fs_;
mast = _mast_;
var table = d3.select(element[0]),
tableElems = {
table: table,
thead: table.select('.table-header').select('table'),
tbody: table.select('.table-body').select('table')
},
wsz;
findCstmWidths(table);
// adjust table on window resize
scope.$watchCollection(function () {
return {
h: $window.innerHeight,
w: $window.innerWidth
};
}, function () {
wsz = fs.windowSize(0, 30);
adjustTable(
scope.tableData.length,
tableElems,
wsz.width, wsz.height
);
});
// adjust table when data changes
scope.$watchCollection('tableData', function () {
adjustTable(
scope.tableData.length,
tableElems,
wsz.width, wsz.height
);
});
scope.$on('$destroy', function () {
cstmWidths = {};
});
};
}])
.directive('onosSortableHeader', ['$log', 'IconService',
function (_$log_, _is_) {
return function (scope, element) {
$log = _$log_;
is = _is_;
var header = d3.select(element[0]);
sortIconAPI = is.sortIcons();
header.selectAll('td').on('click', function () {
var col = d3.select(this);
if (col.attr('sortable') === '') {
updateSortDirection(col);
scope.sortParams = sortRequestParams();
scope.sortCallback(scope.sortParams);
}
});
scope.$on('$destroy', function () {
resetSort();
});
};
}])
.directive('onosFlashChanges',
['$log', '$parse', '$timeout', 'FnService',
function ($log, $parse, $timeout, fs) {
return function (scope, element, attrs) {
var idProp = attrs.idProp,
table = d3.select(element[0]),
trs, promise;
function highlightRows() {
var changedRows = [];
function classRows(b) {
if (changedRows.length) {
angular.forEach(changedRows, function (tr) {
tr.classed('data-change', b);
});
}
}
// timeout because 'row-id' was the un-interpolated value
// "{{link.one}}" for example, instead of link.one evaluated
// timeout executes on the next digest -- after evaluation
$timeout(function () {
if (scope.tableData.length) {
trs = table.selectAll('tr');
}
if (trs && !trs.empty()) {
trs.each(function () {
var tr = d3.select(this);
if (fs.find(tr.attr('row-id'),
scope.changedData,
idProp) > -1) {
changedRows.push(tr);
}
});
classRows(true);
promise = $timeout(function () {
classRows(false);
}, flashTime);
trs = undefined;
}
});
}
// new items added:
scope.$on('ngRepeatComplete', highlightRows);
// items changed in existing set:
scope.$watchCollection('changedData', highlightRows);
scope.$on('$destroy', function () {
if (promise) {
$timeout.cancel(promise);
}
});
};
}]);
}());