/* * 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 thingsboardTypes from '../../common/types.constant'; import thingsboardUtils from '../../common/utils.service'; import thingsboardEntityAliasSelect from '../entity-alias-select.directive'; import thingsboardDatasource from '../datasource.directive'; import thingsboardTimewindow from '../timewindow.directive'; import thingsboardLegendConfig from '../legend-config.directive'; import thingsboardJsonForm from '../json-form.directive'; import thingsboardManageWidgetActions from './action/manage-widget-actions.directive'; import 'angular-ui-ace'; import './widget-config.scss'; /* eslint-disable import/no-unresolved, import/default */ import widgetConfigTemplate from './widget-config.tpl.html'; /* eslint-enable import/no-unresolved, import/default */ /* eslint-disable angular/angularelement */ export default angular.module('thingsboard.directives.widgetConfig', [thingsboardTypes, thingsboardUtils, thingsboardJsonForm, thingsboardEntityAliasSelect, thingsboardDatasource, thingsboardTimewindow, thingsboardLegendConfig, thingsboardManageWidgetActions, 'ui.ace']) .directive('tbWidgetConfig', WidgetConfig) .name; /*@ngInject*/ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout, types, utils) { var linker = function (scope, element, attrs, ngModelCtrl) { var template = $templateCache.get(widgetConfigTemplate); element.html(template); scope.types = types; scope.widgetEditMode = $rootScope.widgetEditMode; scope.emptySettingsSchema = { type: "object", properties: {} }; scope.defaultSettingsForm = [ '*' ]; scope.styleEditorOptions = { useWrapMode: true, mode: 'json', advanced: { enableSnippets: true, enableBasicAutocompletion: true, enableLiveAutocompletion: true } }; if (angular.isUndefined(scope.forceExpandDatasources)) { scope.forceExpandDatasources = false; } if (angular.isUndefined(scope.isDataEnabled)) { scope.isDataEnabled = true; } scope.currentSettingsSchema = {}; scope.currentSettings = angular.copy(scope.emptySettingsSchema); scope.targetDeviceAlias = { value: null } scope.alarmSource = { value: null } ngModelCtrl.$render = function () { if (ngModelCtrl.$viewValue) { var config = ngModelCtrl.$viewValue.config; var layout = ngModelCtrl.$viewValue.layout; if (config) { scope.selectedTab = 0; scope.title = config.title; scope.showTitle = config.showTitle; scope.dropShadow = angular.isDefined(config.dropShadow) ? config.dropShadow : true; scope.enableFullscreen = angular.isDefined(config.enableFullscreen) ? config.enableFullscreen : true; scope.backgroundColor = config.backgroundColor; scope.color = config.color; scope.padding = config.padding; scope.margin = config.margin; scope.widgetStyle = angular.toJson(angular.isDefined(config.widgetStyle) ? config.widgetStyle : {}, true); scope.titleStyle = angular.toJson(angular.isDefined(config.titleStyle) ? config.titleStyle : { fontSize: '16px', fontWeight: 400 }, true); scope.units = config.units; scope.decimals = config.decimals; scope.useDashboardTimewindow = angular.isDefined(config.useDashboardTimewindow) ? config.useDashboardTimewindow : true; scope.timewindow = config.timewindow; scope.showLegend = angular.isDefined(config.showLegend) ? config.showLegend : scope.widgetType === types.widgetType.timeseries.value; scope.legendConfig = config.legendConfig; scope.actions = config.actions; if (!scope.actions) { scope.actions = {}; } if (scope.widgetType !== types.widgetType.rpc.value && scope.widgetType !== types.widgetType.alarm.value && scope.widgetType !== types.widgetType.static.value && scope.isDataEnabled) { if (scope.datasources) { scope.datasources.splice(0, scope.datasources.length); } else { scope.datasources = []; } if (config.datasources) { for (var i = 0; i < config.datasources.length; i++) { scope.datasources.push({value: config.datasources[i]}); } } } else if (scope.widgetType === types.widgetType.rpc.value && scope.isDataEnabled) { if (config.targetDeviceAliasIds && config.targetDeviceAliasIds.length > 0) { var aliasId = config.targetDeviceAliasIds[0]; var entityAliases = scope.aliasController.getEntityAliases(); if (entityAliases[aliasId]) { scope.targetDeviceAlias.value = entityAliases[aliasId]; } else { scope.targetDeviceAlias.value = null; } } else { scope.targetDeviceAlias.value = null; } } else if (scope.widgetType === types.widgetType.alarm.value && scope.isDataEnabled) { scope.alarmSearchStatus = angular.isDefined(config.alarmSearchStatus) ? config.alarmSearchStatus : types.alarmSearchStatus.any; scope.alarmsPollingInterval = angular.isDefined(config.alarmsPollingInterval) ? config.alarmsPollingInterval : 5; if (config.alarmSource) { scope.alarmSource.value = config.alarmSource; } else { scope.alarmSource.value = null; } } scope.settings = config.settings; scope.updateSchemaForm(); } if (layout) { scope.mobileOrder = layout.mobileOrder; scope.mobileHeight = layout.mobileHeight; } } }; scope.displayAdvanced = function() { return scope.widgetSettingsSchema && scope.widgetSettingsSchema.schema; } scope.updateSchemaForm = function() { if (scope.widgetSettingsSchema && scope.widgetSettingsSchema.schema) { scope.currentSettingsSchema = scope.widgetSettingsSchema.schema; scope.currentSettingsForm = scope.widgetSettingsSchema.form || angular.copy(scope.defaultSettingsForm); scope.currentSettings = scope.settings; } else { scope.currentSettingsForm = angular.copy(scope.defaultSettingsForm); scope.currentSettingsSchema = angular.copy(scope.emptySettingsSchema); scope.currentSettings = {}; } } scope.updateValidity = function () { if (ngModelCtrl.$viewValue) { var value = ngModelCtrl.$viewValue; var config = value.config; if (config) { var valid; if (scope.widgetType === types.widgetType.rpc.value && scope.isDataEnabled) { valid = config && config.targetDeviceAliasIds && config.targetDeviceAliasIds.length > 0; ngModelCtrl.$setValidity('targetDeviceAliasIds', valid); } else if (scope.widgetType === types.widgetType.alarm.value && scope.isDataEnabled) { valid = config && config.alarmSource; ngModelCtrl.$setValidity('alarmSource', valid); } else if (scope.widgetType !== types.widgetType.static.value && scope.isDataEnabled) { valid = config && config.datasources && config.datasources.length > 0; ngModelCtrl.$setValidity('datasources', valid); } try { angular.fromJson(scope.widgetStyle); ngModelCtrl.$setValidity('widgetStyle', true); } catch (e) { ngModelCtrl.$setValidity('widgetStyle', false); } try { angular.fromJson(scope.titleStyle); ngModelCtrl.$setValidity('titleStyle', true); } catch (e) { ngModelCtrl.$setValidity('titleStyle', false); } } } }; scope.$watch('title + showTitle + dropShadow + enableFullscreen + backgroundColor + color + ' + 'padding + margin + widgetStyle + titleStyle + mobileOrder + mobileHeight + units + decimals + useDashboardTimewindow + ' + 'alarmSearchStatus + alarmsPollingInterval + showLegend', function () { if (ngModelCtrl.$viewValue) { var value = ngModelCtrl.$viewValue; if (value.config) { var config = value.config; config.title = scope.title; config.showTitle = scope.showTitle; config.dropShadow = scope.dropShadow; config.enableFullscreen = scope.enableFullscreen; config.backgroundColor = scope.backgroundColor; config.color = scope.color; config.padding = scope.padding; config.margin = scope.margin; try { config.widgetStyle = angular.fromJson(scope.widgetStyle); } catch (e) { config.widgetStyle = {}; } try { config.titleStyle = angular.fromJson(scope.titleStyle); } catch (e) { config.titleStyle = {}; } config.units = scope.units; config.decimals = scope.decimals; config.useDashboardTimewindow = scope.useDashboardTimewindow; config.alarmSearchStatus = scope.alarmSearchStatus; config.alarmsPollingInterval = scope.alarmsPollingInterval; config.showLegend = scope.showLegend; } if (value.layout) { var layout = value.layout; layout.mobileOrder = angular.isNumber(scope.mobileOrder) ? scope.mobileOrder : undefined; layout.mobileHeight = scope.mobileHeight; } ngModelCtrl.$setViewValue(value); scope.updateValidity(); } }); scope.$watch('currentSettings', function () { if (ngModelCtrl.$viewValue) { var value = ngModelCtrl.$viewValue; if (value.config) { value.config.settings = scope.currentSettings; ngModelCtrl.$setViewValue(value); } } }, true); scope.$watch('timewindow', function () { if (ngModelCtrl.$viewValue) { var value = ngModelCtrl.$viewValue; if (value.config) { value.config.timewindow = scope.timewindow; ngModelCtrl.$setViewValue(value); } } }, true); scope.$watch('legendConfig', function () { if (ngModelCtrl.$viewValue) { var value = ngModelCtrl.$viewValue; if (value.config) { value.config.legendConfig = scope.legendConfig; ngModelCtrl.$setViewValue(value); } } }, true); scope.$watch('datasources', function () { if (ngModelCtrl.$viewValue && ngModelCtrl.$viewValue.config && scope.widgetType !== types.widgetType.rpc.value && scope.widgetType !== types.widgetType.alarm.value && scope.widgetType !== types.widgetType.static.value && scope.isDataEnabled) { var value = ngModelCtrl.$viewValue; var config = value.config; if (config.datasources) { config.datasources.splice(0, config.datasources.length); } else { config.datasources = []; } if (scope.datasources) { for (var i = 0; i < scope.datasources.length; i++) { config.datasources.push(scope.datasources[i].value); } } ngModelCtrl.$setViewValue(value); scope.updateValidity(); } }, true); scope.$watch('targetDeviceAlias.value', function () { if (ngModelCtrl.$viewValue && ngModelCtrl.$viewValue.config && scope.widgetType === types.widgetType.rpc.value && scope.isDataEnabled) { var value = ngModelCtrl.$viewValue; var config = value.config; if (scope.targetDeviceAlias.value) { config.targetDeviceAliasIds = [scope.targetDeviceAlias.value.id]; } else { config.targetDeviceAliasIds = []; } ngModelCtrl.$setViewValue(value); scope.updateValidity(); } }); scope.$watch('alarmSource.value', function () { if (ngModelCtrl.$viewValue && ngModelCtrl.$viewValue.config && scope.widgetType === types.widgetType.alarm.value && scope.isDataEnabled) { var value = ngModelCtrl.$viewValue; var config = value.config; if (scope.alarmSource.value) { config.alarmSource = scope.alarmSource.value; } else { config.alarmSource = null; } ngModelCtrl.$setViewValue(value); scope.updateValidity(); } }); scope.$watch('actions', function () { if (ngModelCtrl.$viewValue && ngModelCtrl.$viewValue.config) { var value = ngModelCtrl.$viewValue; var config = value.config; config.actions = scope.actions; ngModelCtrl.$setViewValue(value); scope.updateValidity(); /*if (scope.theForm) { scope.theForm.$setDirty(); }*/ } }, true); scope.addDatasource = function () { var newDatasource; if (scope.functionsOnly) { newDatasource = angular.copy(utils.getDefaultDatasource(scope.datakeySettingsSchema.schema)); newDatasource.dataKeys = [scope.generateDataKey('Sin', types.dataKeyType.function)]; } else { newDatasource = { type: types.datasourceType.entity, dataKeys: [] }; } var datasource = {value: newDatasource}; scope.datasources.push(datasource); if (scope.theForm) { scope.theForm.$setDirty(); } } scope.removeDatasource = function ($event, datasource) { var index = scope.datasources.indexOf(datasource); if (index > -1) { scope.datasources.splice(index, 1); if (scope.theForm) { scope.theForm.$setDirty(); } } }; scope.generateDataKey = function (chip, type) { if (angular.isObject(chip)) { chip._hash = Math.random(); return chip; } var label = chip; if (type === types.dataKeyType.alarm) { var alarmField = types.alarmFields[chip]; if (alarmField) { label = $translate.instant(alarmField.name)+''; } } label = scope.genNextLabel(label); var result = { name: chip, type: type, label: label, color: scope.genNextColor(), settings: {}, _hash: Math.random() }; if (type === types.dataKeyType.function) { result.name = 'f(x)'; result.funcBody = utils.getPredefinedFunctionBody(chip); if (!result.funcBody) { result.funcBody = "return prevValue + 1;"; } } if (angular.isDefined(scope.datakeySettingsSchema.schema)) { result.settings = utils.generateObjectFromJsonSchema(scope.datakeySettingsSchema.schema); } return result; }; scope.genNextLabel = function (name) { var label = name; var value = ngModelCtrl.$viewValue; var i = 1; var matches = false; do { matches = false; var datasources = scope.widgetType == types.widgetType.alarm.value ? [value.config.alarmSource] : value.config.datasources; if (datasources) { for (var d=0;d