ONOS-1842 - GUI -- Tables have better resizing behavior -- column headers stay above their columns and table cell text is constrained to one size.

Change-Id: I89ca7d25d46d895c78c41b8250ce40408fbaba85
diff --git a/web/gui/src/main/webapp/app/fw/widget/table.js b/web/gui/src/main/webapp/app/fw/widget/table.js
index d9fddda..caacab4 100644
--- a/web/gui/src/main/webapp/app/fw/widget/table.js
+++ b/web/gui/src/main/webapp/app/fw/widget/table.js
@@ -37,86 +37,68 @@
         prevCol = {},
         sortIconAPI;
 
-    // Functions for creating a fixed header on a table (Angular Directive)
+    // Functions for creating a scrolling table body with fixed table header
 
-    function setElemWidth(elem, size) {
-        elem.style('width', size + 'px')
+    function _width(elem, width) {
+        elem.style('width', width);
     }
 
-    function setColWidth(th, td, size) {
-        setElemWidth(th, size);
-        setElemWidth(td, size);
+    function defaultSize(table, width) {
+        var thead = table.select('.table-header').select('table'),
+            tbody = table.select('.table-body').select('table'),
+            wpx = width + 'px';
+        _width(thead, wpx);
+        _width(tbody, wpx);
     }
 
-    // count number of headers of
-    //   - assigned width,
-    //   - icon width,
-    //   - and default width
-    // assumes assigned width is not given to icons
-    // returns the width of all columns that are not icons
-    // or have an assigned width
-    function getDefaultWidth(headers) {
-        var winWidth = fs.windowSize().width,
-            iconCols = 0,
-            regCols = 0,
-            cstmColWidth = 0;
+    function adjustTable(table, width, height) {
+        var thead = table.select('.table-header').select('table'),
+            tbodyDiv = table.select('.table-body'),
+            tbody = tbodyDiv.select('table'),
+            cstmWidths = {};
 
-        headers.each(function (d, i) {
-            var thElement = d3.select(this),
-                cstmWidth = thElement.attr(colWidth);
+        function findCstmWidths() {
+            var headers = thead.selectAll('td');
 
-            if (cstmWidth) {
-                cstmColWidth += fs.noPx(cstmWidth);
-            } else if (thElement.classed(tableIcon)) {
-                iconCols += 1;
-            } else {
-                regCols += 1;
-            }
-        });
+            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);
+                }
+            });
+            $log.debug('Headers with custom widths: ', cstmWidths);
+        }
 
-        return Math.floor((winWidth - cstmColWidth -
-                            (iconCols * tableIconTdSize)) / regCols);
-    }
+        function setTdWidths(elem) {
+            var tds = elem.selectAll('tr:not(.ignore-width)').selectAll('td');
+            _width(elem, width + 'px');
 
-    function setTableWidth(t) {
-        var tHeaders = t.selectAll('th'),
-            defaultColWidth = getDefaultWidth(tHeaders);
+            tds.each(function (d, i) {
+                var td = d3.select(this),
+                    index = i.toString();
+                if (cstmWidths.hasOwnProperty(index)) {
+                    _width(td, cstmWidths[index]);
+                }
+            });
+        }
 
-        tHeaders.each(function (d, i) {
-            var thElement = d3.select(this),
-                tr = t.select('tr:nth-of-type(2)'),
-                tdElement = tr.select('td:nth-of-type(' + (i + 1) + ')'),
-                custWidth = thElement.attr(colWidth);
+        function setHeight(body) {
+            var h = height - (mast.mastHeight() +
+                fs.noPxStyle(d3.select('.tabular-header'), 'height') +
+                fs.noPxStyle(thead, 'height') + pdg);
+            body.style('height', h + 'px');
+        }
 
-            if (custWidth) {
-                setColWidth(thElement, tdElement, fs.noPx(custWidth));
-            } else if (thElement.classed(tableIcon)) {
-                setColWidth(thElement, tdElement, tableIconTdSize);
-            } else {
-                setColWidth(thElement, tdElement, defaultColWidth);
-            }
-        });
-    }
+        findCstmWidths();
+        setTdWidths(thead);
+        setTdWidths(tbody);
+        setHeight(tbodyDiv);
 
-    // get the size of the window and then subtract the extra space at the top
-    // to get the height of the table
-    function setTableHeight(thead, tbody) {
-        var ttlHgt = fs.noPxStyle(d3.select('.tabular-header'), 'height'),
-            thHgt = fs.noPxStyle(thead, 'height'),
-            totalHgt = ttlHgt + thHgt + pdg,
-            tbleHgt = fs.windowSize(mast.mastHeight() + totalHgt).height;
-
-        thead.style('display', 'block');
-        tbody.style({
-            display: 'block',
-            height: tbleHgt + 'px',
-            overflow: 'auto'
-        });
-    }
-
-    function fixTable(t, th, tb) {
-        setTableWidth(t);
-        setTableHeight(th, tb);
+        cstmWidths = {};
     }
 
     // Functions for sorting table rows by header
@@ -163,40 +145,42 @@
     }
 
     angular.module('onosWidget')
-        .directive('onosFixedHeader', ['$window', 'FnService', 'MastService',
-            function (_$window_, _fs_, _mast_) {
+        .directive('onosFixedHeader', ['$log','$window',
+            'FnService', 'MastService',
+
+            function (_$log_, _$window_, _fs_, _mast_) {
             return function (scope, element) {
+                $log = _$log_;
                 $window = _$window_;
                 fs = _fs_;
                 mast = _mast_;
 
                 var w = angular.element($window),
                     table = d3.select(element[0]),
-                    thead = table.select('thead'),
-                    tbody = table.select('tbody'),
                     canAdjust = false;
 
                 scope.$watch(function () {
                     return {
-                        h: window.innerHeight,
-                        w: window.innerWidth
+                        h: $window.innerHeight,
+                        w: $window.innerWidth
                     };
-                }, function (newVal) {
-                    var wsz = fs.windowSize(0, 30);
-                    scope.windowHeight = newVal.h;
-                    scope.windowWidth = newVal.w;
+                }, function () {
+                    var wsz = fs.windowSize(0, 30),
+                        wWidth = wsz.width,
+                        wHeight = wsz.height;
 
-                    // default table size in case no data elements
-                    table.style('width', wsz.width + 'px');
+                    if (!scope.tableData.length) {
+                        defaultSize(table, wWidth);
+                    }
 
                     scope.$on('LastElement', function () {
                         // only adjust the table once it's completely loaded
-                        fixTable(table, thead, tbody);
+                        adjustTable(table, wWidth, wHeight);
                         canAdjust = true;
                     });
 
                     if (canAdjust) {
-                        fixTable(table, thead, tbody);
+                        adjustTable(table, wWidth, wHeight);
                     }
                 }, true);
 
@@ -215,16 +199,16 @@
                 link: function (scope, element) {
                     $log = _$log_;
                     is = _is_;
-                    var table = d3.select(element[0]);
+                    var header = d3.select(element[0]);
                         sortIconAPI = is.sortIcons();
 
-                    // when a header is clicked, change its icon tag
+                    // when a header is clicked, change its sort direction
                     // and get sorting order to send to the server.
-                    table.selectAll('th').on('click', function () {
-                        var thElem = d3.select(this);
+                    header.selectAll('td').on('click', function () {
+                        var col = d3.select(this);
 
-                        if (thElem.attr('sortable') === '') {
-                            updateSortDirection(thElem);
+                        if (col.attr('sortable') === '') {
+                            updateSortDirection(col);
                             scope.ctrlCallback({
                                 requestParams: sortRequestParams()
                             });
@@ -234,9 +218,9 @@
             };
         }])
 
-        .factory('TableService', ['$log', 'IconService',
+        .factory('TableService', ['IconService',
 
-            function ($log, is) {
+            function (is) {
                 sortIconAPI = is.sortIcons();
 
                 return {