table.js 6.97 KB
/*
 * 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 = 12,
        colWidth = 'col-width',
        tableIcon = 'table-icon',
        asc = 'asc',
        desc = 'desc',
        none = 'none';

    // internal state
    var currCol = {},
        prevCol = {},
        sortIconAPI;

    // Functions for creating a scrolling table body with fixed table header

    function _width(elem, width) {
        elem.style('width', width);
    }

    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);
    }

    function adjustTable(table, width, height) {
        var thead = table.select('.table-header').select('table'),
            tbodyDiv = table.select('.table-body'),
            tbody = tbodyDiv.select('table'),
            cstmWidths = {};

        function findCstmWidths() {
            var headers = thead.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);
                }
            });
            $log.debug('Headers with custom widths: ', cstmWidths);
        }

        function setTdWidths(elem) {
            var tds = elem.selectAll('tr:not(.ignore-width)').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(body) {
            var h = height - (mast.mastHeight() +
                fs.noPxStyle(d3.select('.tabular-header'), 'height') +
                fs.noPxStyle(thead, 'height') + pdg);
            body.style('height', h + 'px');
        }

        findCstmWidths();
        setTdWidths(thead);
        setTdWidths(tbody);
        setHeight(tbodyDiv);

        cstmWidths = {};
    }

    // 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 resetSortIcons() {
        if (currCol.div) {
            sortIconAPI.sortNone(currCol.div);
        }
        if (prevCol.div) {
            sortIconAPI.sortNone(prevCol.div);
        }
        currCol = {};
        prevCol = {};
    }

    angular.module('onosWidget')
        .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]),
                    canAdjust = false;

                scope.$watch(function () {
                    return {
                        h: $window.innerHeight,
                        w: $window.innerWidth
                    };
                }, function () {
                    var wsz = fs.windowSize(0, 30),
                        wWidth = wsz.width,
                        wHeight = wsz.height;

                    if (!scope.tableData.length) {
                        defaultSize(table, wWidth);
                    }

                    scope.$on('LastElement', function () {
                        // only adjust the table once it's completely loaded
                        adjustTable(table, wWidth, wHeight);
                        canAdjust = true;
                    });

                    if (canAdjust) {
                        adjustTable(table, wWidth, wHeight);
                    }
                }, true);

                w.bind('onos-fixed-header', function () {
                    scope.$apply();
                });
            };
        }])

        .directive('onosSortableHeader', ['$log', 'IconService',
            function (_$log_, _is_) {
            return {
                scope: {
                    ctrlCallback: '&sortCallback'
                },
                link: function (scope, element) {
                    $log = _$log_;
                    is = _is_;
                    var header = d3.select(element[0]);
                        sortIconAPI = is.sortIcons();

                    // when a header is clicked, change its sort direction
                    // and get sorting order to send to the server.
                    header.selectAll('td').on('click', function () {
                        var col = d3.select(this);

                        if (col.attr('sortable') === '') {
                            updateSortDirection(col);
                            scope.ctrlCallback({
                                requestParams: sortRequestParams()
                            });
                        }
                    });
                }
            };
        }])

        .factory('TableService', ['IconService',

            function (is) {
                sortIconAPI = is.sortIcons();

                return {
                    resetSortIcons: resetSortIcons
                };
        }]);

}());