timewindow.directive.js

282 lines | 11.208 kB Blame History Raw Download
/*
 * Copyright © 2016-2018 The Thingsboard Authors
 *
 * 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.
 */
import './timewindow.scss';

import $ from 'jquery';
import thingsboardTimeinterval from './timeinterval.directive';
import thingsboardDatetimePeriod from './datetime-period.directive';

/* eslint-disable import/no-unresolved, import/default */

import timewindowButtonTemplate from './timewindow-button.tpl.html';
import timewindowTemplate from './timewindow.tpl.html';
import timewindowPanelTemplate from './timewindow-panel.tpl.html';

/* eslint-enable import/no-unresolved, import/default */

import TimewindowPanelController from './timewindow-panel.controller';

export default angular.module('thingsboard.directives.timewindow', [thingsboardTimeinterval, thingsboardDatetimePeriod])
    .controller('TimewindowPanelController', TimewindowPanelController)
    .directive('tbTimewindow', Timewindow)
    .filter('milliSecondsToTimeString', MillisecondsToTimeString)
    .name;

/* eslint-disable angular/angularelement */
/*@ngInject*/
function Timewindow($compile, $templateCache, $filter, $mdPanel, $document, $mdMedia, $translate, timeService) {

    var linker = function (scope, element, attrs, ngModelCtrl) {

        /* tbTimewindow (ng-model)
         * {
         * 	  realtime: {
         * 	        interval: 0,
         * 			timewindowMs: 0
         * 	  },
         * 	  history: {
         * 	        interval: 0,
         * 			timewindowMs: 0,
         * 			fixedTimewindow: {
         * 				startTimeMs: 0,
         * 				endTimeMs: 0
         * 			}
         * 	  },
         * 	  aggregation: {
         * 	        type: types.aggregation.avg.value,
         * 	        limit: 200
         * 	  }
         * }
         */

        scope.historyOnly = angular.isDefined(attrs.historyOnly);

        scope.aggregation = scope.$eval(attrs.aggregation);

        scope.isToolbar = angular.isDefined(attrs.isToolbar);

        scope.hideLabel = function() {
            return scope.isToolbar && !$mdMedia('gt-md');
        }

        var translationPending = false;

        $translate.onReady(function() {
            if (translationPending) {
                scope.updateDisplayValue();
                translationPending = false;
            }
        });

        var template;
        if (scope.asButton) {
            template = $templateCache.get(timewindowButtonTemplate);
        } else {
            scope.direction = angular.isDefined(attrs.direction) ? attrs.direction : 'left';
            scope.tooltipDirection = angular.isDefined(attrs.tooltipDirection) ? attrs.tooltipDirection : 'top';
            template = $templateCache.get(timewindowTemplate);
        }
        element.html(template);

        scope.openEditMode = function (event) {
            if (scope.disabled) {
                return;
            }
            var position;
            var isGtSm = $mdMedia('gt-sm');
            if (isGtSm) {
                var panelHeight = 375;
                var panelWidth = 417;
                var offset = element[0].getBoundingClientRect();
                var bottomY = offset.bottom - $(window).scrollTop(); //eslint-disable-line
                var leftX = offset.left - $(window).scrollLeft(); //eslint-disable-line
                var yPosition;
                var xPosition;
                if (bottomY + panelHeight > $( window ).height()) { //eslint-disable-line
                    yPosition = $mdPanel.yPosition.ABOVE;
                } else {
                    yPosition = $mdPanel.yPosition.BELOW;
                }
                if (leftX + panelWidth > $( window ).width()) { //eslint-disable-line
                    xPosition = $mdPanel.xPosition.ALIGN_END;
                } else {
                    xPosition = $mdPanel.xPosition.ALIGN_START;
                }
                position = $mdPanel.newPanelPosition()
                    .relativeTo(element)
                    .addPanelPosition(xPosition, yPosition);
            } else {
                position = $mdPanel.newPanelPosition()
                    .absolute()
                    .top('0%')
                    .left('0%');
            }
            var config = {
                attachTo: angular.element($document[0].body),
                controller: 'TimewindowPanelController',
                controllerAs: 'vm',
                templateUrl: timewindowPanelTemplate,
                panelClass: 'tb-timewindow-panel',
                position: position,
                fullscreen: !isGtSm,
                locals: {
                    'timewindow': angular.copy(scope.model),
                    'historyOnly': scope.historyOnly,
                    'aggregation': scope.aggregation,
                    'onTimewindowUpdate': function (timewindow) {
                        scope.model = timewindow;
                        scope.updateView();
                    }
                },
                openFrom: event,
                clickOutsideToClose: true,
                escapeToClose: true,
                focusOnOpen: false
            };
            $mdPanel.open(config);
        }

        scope.updateView = function () {
            var value = {};
            var model = scope.model;
            if (model.selectedTab === 0) {
                value.realtime = {
                    interval: model.realtime.interval,
                    timewindowMs: model.realtime.timewindowMs
                };
            } else {
                if (model.history.historyType === 0) {
                    value.history = {
                        interval: model.history.interval,
                        timewindowMs: model.history.timewindowMs
                    };
                } else {
                    value.history = {
                        interval: model.history.interval,
                        fixedTimewindow: {
                            startTimeMs: model.history.fixedTimewindow.startTimeMs,
                            endTimeMs: model.history.fixedTimewindow.endTimeMs
                        }
                    };
                }
            }
            value.aggregation = {
                type: model.aggregation.type,
                limit: model.aggregation.limit
            };
            ngModelCtrl.$setViewValue(value);
            scope.updateDisplayValue();
        }

        scope.updateDisplayValue = function () {
            if ($translate.isReady()) {
                if (scope.model.selectedTab === 0 && !scope.historyOnly) {
                    scope.model.displayValue = $translate.instant('timewindow.realtime') + ' - ' +
                        $translate.instant('timewindow.last-prefix') + ' ' +
                        $filter('milliSecondsToTimeString')(scope.model.realtime.timewindowMs);
                } else {
                    scope.model.displayValue = !scope.historyOnly ? ($translate.instant('timewindow.history') + ' - ') : '';
                    if (scope.model.history.historyType === 0) {
                        scope.model.displayValue += $translate.instant('timewindow.last-prefix') + ' ' +
                            $filter('milliSecondsToTimeString')(scope.model.history.timewindowMs);
                    } else {
                        var startString = $filter('date')(scope.model.history.fixedTimewindow.startTimeMs, 'yyyy-MM-dd HH:mm:ss');
                        var endString = $filter('date')(scope.model.history.fixedTimewindow.endTimeMs, 'yyyy-MM-dd HH:mm:ss');
                        scope.model.displayValue += $translate.instant('timewindow.period', {startTime: startString, endTime: endString});
                    }
                }
            } else {
                translationPending = true;
            }
        }

        ngModelCtrl.$render = function () {
            scope.model = timeService.defaultTimewindow();
            if (ngModelCtrl.$viewValue) {
                var value = ngModelCtrl.$viewValue;
                var model = scope.model;
                if (angular.isDefined(value.realtime)) {
                    model.selectedTab = 0;
                    model.realtime.interval = value.realtime.interval;
                    model.realtime.timewindowMs = value.realtime.timewindowMs;
                } else {
                    model.selectedTab = 1;
                    model.history.interval = value.history.interval;
                    if (angular.isDefined(value.history.timewindowMs)) {
                        model.history.historyType = 0;
                        model.history.timewindowMs = value.history.timewindowMs;
                    } else {
                        model.history.historyType = 1;
                        model.history.fixedTimewindow.startTimeMs = value.history.fixedTimewindow.startTimeMs;
                        model.history.fixedTimewindow.endTimeMs = value.history.fixedTimewindow.endTimeMs;
                    }
                }
                if (angular.isDefined(value.aggregation)) {
                    if (angular.isDefined(value.aggregation.type) && value.aggregation.type.length > 0) {
                        model.aggregation.type = value.aggregation.type;
                    }
                    model.aggregation.limit = value.aggregation.limit || Math.floor(timeService.getMaxDatapointsLimit() / 2);
                }
            }
            scope.updateDisplayValue();
        };

        $compile(element.contents())(scope);
    }

    return {
        restrict: "E",
        require: "^ngModel",
        scope: {
            asButton: '=asButton',
            disabled:'=ngDisabled'
        },
        link: linker
    };
}

/*@ngInject*/
function MillisecondsToTimeString($translate) {
    return function (millseconds) {
        var seconds = Math.floor(millseconds / 1000);
        var days = Math.floor(seconds / 86400);
        var hours = Math.floor((seconds % 86400) / 3600);
        var minutes = Math.floor(((seconds % 86400) % 3600) / 60);
        seconds = seconds % 60;
        var timeString = '';
        if (days > 0) timeString += $translate.instant('timewindow.days', {days: days}, 'messageformat');
        if (hours > 0) {
            if (timeString.length === 0 && hours === 1) {
                hours = 0;
            }
            timeString += $translate.instant('timewindow.hours', {hours: hours}, 'messageformat');
        }
        if (minutes > 0) {
            if (timeString.length === 0 && minutes === 1) {
                minutes = 0;
            }
            timeString += $translate.instant('timewindow.minutes', {minutes: minutes}, 'messageformat');
        }
        if (seconds > 0) {
            if (timeString.length === 0 && seconds === 1) {
                seconds = 0;
            }
            timeString += $translate.instant('timewindow.seconds', {seconds: seconds}, 'messageformat');
        }
        return timeString;
    }
}
/* eslint-enable angular/angularelement */