thingsboard-aplcache

Changes

ui/package.json 4(+2 -2)

Details

diff --git a/application/src/main/data/json/system/widget_bundles/cards.json b/application/src/main/data/json/system/widget_bundles/cards.json
index f48ef37..6550f58 100644
--- a/application/src/main/data/json/system/widget_bundles/cards.json
+++ b/application/src/main/data/json/system/widget_bundles/cards.json
@@ -15,7 +15,7 @@
         "resources": [],
         "templateHtml": "",
         "templateCss": "#container {\n    overflow: auto;\n}\n\n.tbDatasource-container {\n    margin: 5px;\n    padding: 8px;\n}\n\n.tbDatasource-title {\n    font-size: 1.200rem;\n    font-weight: 500;\n    padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n    width: 100%;\n    box-shadow: 0 0 10px #ccc;\n    border-collapse: collapse;\n    white-space: nowrap;\n    font-size: 1.000rem;\n    color: #757575;\n}\n\n.tbDatasource-table td {\n    position: relative;\n    border-top: 1px solid rgba(0, 0, 0, 0.12);\n    border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n    padding: 0px 18px;\n    box-sizing: border-box;\n}",
-        "controllerScript": "self.onInit = function() {\n    \n    self.ctx.datasourceTitleCells = [];\n    self.ctx.valueCells = [];\n    self.ctx.labelCells = [];\n    \n    for (var i=0; i < self.ctx.datasources.length; i++) {\n        var tbDatasource = self.ctx.datasources[i];\n\n        var datasourceId = 'tbDatasource' + i;\n        self.ctx.$container.append(\n            \"<div id='\" + datasourceId +\n            \"' class='tbDatasource-container'></div>\"\n        );\n\n        var datasourceContainer = $('#' + datasourceId,\n            self.ctx.$container);\n\n        datasourceContainer.append(\n            \"<div class='tbDatasource-title'>\" +\n            tbDatasource.name + \"</div>\"\n        );\n        \n        var datasourceTitleCell = $('.tbDatasource-title', datasourceContainer);\n        self.ctx.datasourceTitleCells.push(datasourceTitleCell);\n        \n        var tableId = 'table' + i;\n        datasourceContainer.append(\n            \"<table id='\" + tableId +\n            \"' class='tbDatasource-table'><col width='30%'><col width='70%'></table>\"\n        );\n        var table = $('#' + tableId, self.ctx.$container);\n\n        for (var a = 0; a < tbDatasource.dataKeys.length; a++) {\n            var dataKey = tbDatasource.dataKeys[a];\n            var labelCellId = 'labelCell' + a;\n            var cellId = 'cell' + a;\n            table.append(\"<tr><td id='\" + labelCellId + \"'>\" + dataKey.label +\n                \"</td><td id='\" + cellId +\n                \"'></td></tr>\");\n            var labelCell = $('#' + labelCellId, table);\n            self.ctx.labelCells.push(labelCell);\n            var valueCell = $('#' + cellId, table);\n            self.ctx.valueCells.push(valueCell);\n        }\n    }    \n    \n    self.onResize();\n}\n\nself.onDataUpdated = function() {\n    for (var i = 0; i < self.ctx.valueCells.length; i++) {\n        var cellData = self.ctx.data[i];\n        console.log(self.ctx); //del\n        if (cellData && cellData.data && cellData.data.length > 0) {\n            var tvPair = cellData.data[cellData.data.length -\n                1];\n            var value = tvPair[1];\n            var textValue;\n            //toDo -> + IsNumber\n            \n            if (isNumber(value)) {\n                var decimals = self.ctx.decimals;\n                var units = self.ctx.units;\n                if (cellData.dataKey.decimals || cellData.dataKey.decimals === 0) {\n                    decimals = cellData.dataKey.decimals;\n                }\n                if (cellData.dataKey.units) {\n                    units = cellData.dataKey.units;\n                }\n                txtValue = self.ctx.utils.formatValue(value, decimals, units, true);\n            } else {\n                txtValue = value;\n            }\n            self.ctx.valueCells[i].html(txtValue);\n        }\n    }\n    \n    function isNumber(n) {\n        return !isNaN(parseFloat(n)) && isFinite(n);\n    }\n}\n\nself.onResize = function() {\n    var datasoirceTitleFontSize = self.ctx.height/8;\n    if (self.ctx.width/self.ctx.height <= 1.5) {\n        datasoirceTitleFontSize = self.ctx.width/12;\n    }\n    datasoirceTitleFontSize = Math.min(datasoirceTitleFontSize, 20);\n    for (var i = 0; i < self.ctx.datasourceTitleCells.length; i++) {\n        self.ctx.datasourceTitleCells[i].css('font-size', datasoirceTitleFontSize+'px');\n    }\n    var valueFontSize = self.ctx.height/9;\n    var labelFontSize = self.ctx.height/9;\n    if (self.ctx.width/self.ctx.height <= 1.5) {\n        valueFontSize = self.ctx.width/15;\n        labelFontSize = self.ctx.width/15;\n    }\n    valueFontSize = Math.min(valueFontSize, 18);\n    labelFontSize = Math.min(labelFontSize, 18);\n\n    for (i = 0; i < self.ctx.valueCells; i++) {\n        self.ctx.valueCells[i].css('font-size', valueFontSize+'px');\n        self.ctx.valueCells[i].css('height', valueFontSize*2.5+'px');\n        self.ctx.valueCells[i].css('padding', '0px ' + valueFontSize + 'px');\n        self.ctx.labelCells[i].css('font-size', labelFontSize+'px');\n        self.ctx.labelCells[i].css('height', labelFontSize*2.5+'px');\n        self.ctx.labelCells[i].css('padding', '0px ' + labelFontSize + 'px');\n    }    \n}\n\nself.onDestroy = function() {\n}\n",
+        "controllerScript": "self.onInit = function() {\n    \n    self.ctx.datasourceTitleCells = [];\n    self.ctx.valueCells = [];\n    self.ctx.labelCells = [];\n    \n    for (var i=0; i < self.ctx.datasources.length; i++) {\n        var tbDatasource = self.ctx.datasources[i];\n\n        var datasourceId = 'tbDatasource' + i;\n        self.ctx.$container.append(\n            \"<div id='\" + datasourceId +\n            \"' class='tbDatasource-container'></div>\"\n        );\n\n        var datasourceContainer = $('#' + datasourceId,\n            self.ctx.$container);\n\n        datasourceContainer.append(\n            \"<div class='tbDatasource-title'>\" +\n            tbDatasource.name + \"</div>\"\n        );\n        \n        var datasourceTitleCell = $('.tbDatasource-title', datasourceContainer);\n        self.ctx.datasourceTitleCells.push(datasourceTitleCell);\n        \n        var tableId = 'table' + i;\n        datasourceContainer.append(\n            \"<table id='\" + tableId +\n            \"' class='tbDatasource-table'><col width='30%'><col width='70%'></table>\"\n        );\n        var table = $('#' + tableId, self.ctx.$container);\n\n        for (var a = 0; a < tbDatasource.dataKeys.length; a++) {\n            var dataKey = tbDatasource.dataKeys[a];\n            var labelCellId = 'labelCell' + a;\n            var cellId = 'cell' + a;\n            table.append(\"<tr><td id='\" + labelCellId + \"'>\" + dataKey.label +\n                \"</td><td id='\" + cellId +\n                \"'></td></tr>\");\n            var labelCell = $('#' + labelCellId, table);\n            self.ctx.labelCells.push(labelCell);\n            var valueCell = $('#' + cellId, table);\n            self.ctx.valueCells.push(valueCell);\n        }\n    }    \n    \n    self.onResize();\n}\n\nself.onDataUpdated = function() {\n    for (var i = 0; i < self.ctx.valueCells.length; i++) {\n        var cellData = self.ctx.data[i];\n        if (cellData && cellData.data && cellData.data.length > 0) {\n            var tvPair = cellData.data[cellData.data.length -\n                1];\n            var value = tvPair[1];\n            var textValue;\n            //toDo -> + IsNumber\n            \n            if (isNumber(value)) {\n                var decimals = self.ctx.decimals;\n                var units = self.ctx.units;\n                if (cellData.dataKey.decimals || cellData.dataKey.decimals === 0) {\n                    decimals = cellData.dataKey.decimals;\n                }\n                if (cellData.dataKey.units) {\n                    units = cellData.dataKey.units;\n                }\n                txtValue = self.ctx.utils.formatValue(value, decimals, units, true);\n            } else {\n                txtValue = value;\n            }\n            self.ctx.valueCells[i].html(txtValue);\n        }\n    }\n    \n    function isNumber(n) {\n        return !isNaN(parseFloat(n)) && isFinite(n);\n    }\n}\n\nself.onResize = function() {\n    var datasoirceTitleFontSize = self.ctx.height/8;\n    if (self.ctx.width/self.ctx.height <= 1.5) {\n        datasoirceTitleFontSize = self.ctx.width/12;\n    }\n    datasoirceTitleFontSize = Math.min(datasoirceTitleFontSize, 20);\n    for (var i = 0; i < self.ctx.datasourceTitleCells.length; i++) {\n        self.ctx.datasourceTitleCells[i].css('font-size', datasoirceTitleFontSize+'px');\n    }\n    var valueFontSize = self.ctx.height/9;\n    var labelFontSize = self.ctx.height/9;\n    if (self.ctx.width/self.ctx.height <= 1.5) {\n        valueFontSize = self.ctx.width/15;\n        labelFontSize = self.ctx.width/15;\n    }\n    valueFontSize = Math.min(valueFontSize, 18);\n    labelFontSize = Math.min(labelFontSize, 18);\n\n    for (i = 0; i < self.ctx.valueCells; i++) {\n        self.ctx.valueCells[i].css('font-size', valueFontSize+'px');\n        self.ctx.valueCells[i].css('height', valueFontSize*2.5+'px');\n        self.ctx.valueCells[i].css('padding', '0px ' + valueFontSize + 'px');\n        self.ctx.labelCells[i].css('font-size', labelFontSize+'px');\n        self.ctx.labelCells[i].css('height', labelFontSize*2.5+'px');\n        self.ctx.labelCells[i].css('padding', '0px ' + labelFontSize + 'px');\n    }    \n}\n\nself.onDestroy = function() {\n}\n",
         "settingsSchema": "{}",
         "dataKeySettingsSchema": "{}\n",
         "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Attributes card\"}"

ui/package.json 4(+2 -2)

diff --git a/ui/package.json b/ui/package.json
index 7874170..9fb0cc5 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -26,7 +26,7 @@
     "angular-gridster": "^0.13.14",
     "angular-hotkeys": "^1.7.0",
     "angular-jwt": "^0.1.6",
-    "angular-material": "1.1.1",
+    "angular-material": "1.1.9",
     "angular-material-data-table": "^0.10.9",
     "angular-material-icons": "^0.7.1",
     "angular-material-expansion-panel": "^0.7.2",
@@ -63,7 +63,7 @@
     "leaflet-providers": "^1.1.17",
     "material-ui": "^0.16.1",
     "material-ui-number-input": "^5.0.16",
-    "md-color-picker": "^0.2.6",
+    "md-color-picker": "0.2.6",
     "mdPickers": "git://github.com/alenaksu/mdPickers.git#0.7.5",
     "moment": "^2.15.0",
     "ngclipboard": "^1.1.1",
diff --git a/ui/src/app/alarm/alarm-row.directive.js b/ui/src/app/alarm/alarm-row.directive.js
index 37eec7c..681498f 100644
--- a/ui/src/app/alarm/alarm-row.directive.js
+++ b/ui/src/app/alarm/alarm-row.directive.js
@@ -50,7 +50,7 @@ export default function AlarmRowDirective($compile, $templateCache, types, $mdDi
                 parent: angular.element($document[0].body),
                 targetEvent: $event,
                 fullscreen: true,
-                skipHide: true,
+                multiple: true,
                 onShowing: function(scope, element) {
                     onShowingCallback.onShowing(scope, element);
                 }
diff --git a/ui/src/app/app.js b/ui/src/app/app.js
index 824dbb0..31b53a0 100644
--- a/ui/src/app/app.js
+++ b/ui/src/app/app.js
@@ -31,6 +31,7 @@ import 'angular-translate-interpolation-messageformat';
 import 'md-color-picker';
 import mdPickers from 'mdPickers';
 import ngSanitize from 'angular-sanitize';
+import FBAngular from 'angular-fullscreen';
 import vAccordion from 'v-accordion';
 import ngAnimate from 'angular-animate';
 import 'angular-websocket';
@@ -65,6 +66,7 @@ import 'angular-material-expansion-panel/dist/md-expansion-panel.min.css';
 import 'ngFlowchart/dist/flowchart.css';
 import '../scss/main.scss';
 
+import thingsboardThirdpartyFix from './common/thirdparty-fix';
 import thingsboardTranslateHandler from './locale/translate-handler';
 import thingsboardLogin from './login';
 import thingsboardDialogs from './components/datakey-config-dialog.controller';
@@ -105,6 +107,7 @@ angular.module('thingsboard', [
     'mdColorPicker',
     mdPickers,
     ngSanitize,
+    FBAngular.name,
     vAccordion,
     ngAnimate,
     'ngWebSocket',
@@ -118,6 +121,7 @@ angular.module('thingsboard', [
     react.name,
     'flow',
     'flowchart',
+    thingsboardThirdpartyFix,
     thingsboardTranslateHandler,
     thingsboardLogin,
     thingsboardDialogs,
diff --git a/ui/src/app/audit/audit-log-row.directive.js b/ui/src/app/audit/audit-log-row.directive.js
index 2c2e170..f13a0d2 100644
--- a/ui/src/app/audit/audit-log-row.directive.js
+++ b/ui/src/app/audit/audit-log-row.directive.js
@@ -48,7 +48,7 @@ export default function AuditLogRowDirective($compile, $templateCache, types, $m
                 parent: angular.element($document[0].body),
                 targetEvent: $event,
                 fullscreen: true,
-                skipHide: true,
+                multiple: true,
                 onShowing: function(scope, element) {
                     onShowingCallback.onShowing(scope, element);
                 }
diff --git a/ui/src/app/common/thirdparty-fix.js b/ui/src/app/common/thirdparty-fix.js
new file mode 100644
index 0000000..b00a782
--- /dev/null
+++ b/ui/src/app/common/thirdparty-fix.js
@@ -0,0 +1,195 @@
+/*
+ * 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 tinycolor from 'tinycolor2';
+
+export default angular.module('thingsboard.thirdpartyFix', [])
+    .factory('Fullscreen', Fullscreen)
+    .factory('$mdColorPicker', mdColorPicker)
+    .name;
+
+/*@ngInject*/
+function Fullscreen($document, $rootScope) {
+
+    /* eslint-disable */
+
+    var document = $document[0];
+
+    // ensure ALLOW_KEYBOARD_INPUT is available and enabled
+    var isKeyboardAvailbleOnFullScreen = (typeof Element !== 'undefined' && 'ALLOW_KEYBOARD_INPUT' in Element) && Element.ALLOW_KEYBOARD_INPUT;
+
+    var emitter = $rootScope.$new();
+
+    // listen event on document instead of element to avoid firefox limitation
+    // see https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Using_full_screen_mode
+    $document.on('fullscreenchange webkitfullscreenchange mozfullscreenchange MSFullscreenChange', function(){
+        emitter.$emit('FBFullscreen.change', serviceInstance.isEnabled());
+    });
+
+    var serviceInstance = {
+        $on: angular.bind(emitter, emitter.$on),
+        all: function() {
+            serviceInstance.enable( document.documentElement );
+        },
+        enable: function(element) {
+            if(element.requestFullScreen) {
+                element.requestFullScreen();
+            } else if(element.mozRequestFullScreen) {
+                element.mozRequestFullScreen();
+            } else if(element.webkitRequestFullscreen) {
+                // Safari temporary fix
+                //if (/Version\/[\d]{1,2}(\.[\d]{1,2}){1}(\.(\d){1,2}){0,1} Safari/.test(navigator.userAgent)) {
+                if (/Safari/.test(navigator.userAgent)) {
+                    element.webkitRequestFullscreen();
+                } else {
+                    element.webkitRequestFullscreen(isKeyboardAvailbleOnFullScreen);
+                }
+            } else if (element.msRequestFullscreen) {
+                element.msRequestFullscreen();
+            }
+        },
+        cancel: function() {
+            if(document.cancelFullScreen) {
+                document.cancelFullScreen();
+            } else if(document.mozCancelFullScreen) {
+                document.mozCancelFullScreen();
+            } else if(document.webkitExitFullscreen) {
+                document.webkitExitFullscreen();
+            } else if (document.msExitFullscreen) {
+                document.msExitFullscreen();
+            }
+        },
+        isEnabled: function(){
+            var fullscreenElement = document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement;
+            return fullscreenElement ? true : false;
+        },
+        toggleAll: function(){
+            serviceInstance.isEnabled() ? serviceInstance.cancel() : serviceInstance.all();
+        },
+        isSupported: function(){
+            var docElm = document.documentElement;
+            var requestFullscreen = docElm.requestFullScreen || docElm.mozRequestFullScreen || docElm.webkitRequestFullscreen || docElm.msRequestFullscreen;
+            return requestFullscreen ? true : false;
+        }
+    };
+
+    /* eslint-enable */
+
+    return serviceInstance;
+}
+
+/*@ngInject*/
+function mdColorPicker($q, $mdDialog, mdColorPickerHistory) {
+    var dialog;
+
+    /* eslint-disable angular/definedundefined */
+
+    return {
+        show: function (options)
+        {
+            if ( options === undefined ) {
+                options = {};
+            }
+            //console.log( 'DIALOG OPTIONS', options );
+            // Defaults
+            // Dialog Properties
+            options.hasBackdrop = options.hasBackdrop === undefined ? true : options.hasBackdrop;
+            options.clickOutsideToClose = options.clickOutsideToClose === undefined ? true : options.clickOutsideToClose;
+            options.defaultValue = options.defaultValue === undefined ? '#FFFFFF' : options.defaultValue;
+            options.focusOnOpen = options.focusOnOpen === undefined ? false : options.focusOnOpen;
+            options.preserveScope = options.preserveScope === undefined ? true : options.preserveScope;
+            if (options.skipHide !== undefined) {
+                options.multiple = options.skipHide;
+            }
+            if (options.multiple === undefined) {
+                options.multiple = true;
+            }
+
+            // mdColorPicker Properties
+            options.mdColorAlphaChannel = options.mdColorAlphaChannel === undefined ? false : options.mdColorAlphaChannel;
+            options.mdColorSpectrum = options.mdColorSpectrum === undefined ? true : options.mdColorSpectrum;
+            options.mdColorSliders = options.mdColorSliders === undefined ? true : options.mdColorSliders;
+            options.mdColorGenericPalette = options.mdColorGenericPalette === undefined ? true : options.mdColorGenericPalette;
+            options.mdColorMaterialPalette = options.mdColorMaterialPalette === undefined ? true : options.mdColorMaterialPalette;
+            options.mdColorHistory = options.mdColorHistory === undefined ? true : options.mdColorHistory;
+
+
+            dialog = $mdDialog.show({
+                templateUrl: 'mdColorPickerDialog.tpl.html',
+                hasBackdrop: options.hasBackdrop,
+                clickOutsideToClose: options.clickOutsideToClose,
+
+                controller: ['$scope', 'options', function( $scope, options ) {
+                    //console.log( "DIALOG CONTROLLER OPEN", Date.now() - dateClick );
+                    $scope.close = function close()
+                    {
+                        $mdDialog.cancel();
+                    };
+                    $scope.ok = function ok()
+                    {
+                        $mdDialog.hide( $scope.value );
+                    };
+                    $scope.hide = $scope.ok;
+
+
+
+                    $scope.value = options.value;
+                    $scope.default = options.defaultValue;
+                    $scope.random = options.random;
+
+                    $scope.mdColorAlphaChannel = options.mdColorAlphaChannel;
+                    $scope.mdColorSpectrum = options.mdColorSpectrum;
+                    $scope.mdColorSliders = options.mdColorSliders;
+                    $scope.mdColorGenericPalette = options.mdColorGenericPalette;
+                    $scope.mdColorMaterialPalette = options.mdColorMaterialPalette;
+                    $scope.mdColorHistory = options.mdColorHistory;
+                    $scope.mdColorDefaultTab = options.mdColorDefaultTab;
+
+                }],
+
+                locals: {
+                    options: options,
+                },
+                preserveScope: options.preserveScope,
+                multiple: options.multiple,
+
+                targetEvent: options.$event,
+                focusOnOpen: options.focusOnOpen,
+                autoWrap: false,
+                onShowing: function() {
+                    //		console.log( "DIALOG OPEN START", Date.now() - dateClick );
+                },
+                onComplete: function() {
+                    //		console.log( "DIALOG OPEN COMPLETE", Date.now() - dateClick );
+                }
+            });
+
+            dialog.then(function (value) {
+                mdColorPickerHistory.add(new tinycolor(value));
+            }, function () { });
+
+            return dialog;
+        },
+        hide: function() {
+            return dialog.hide();
+        },
+        cancel: function() {
+            return dialog.cancel();
+        }
+    };
+
+    /* eslint-enable angular/definedundefined */
+}
diff --git a/ui/src/app/components/datasource-entity.directive.js b/ui/src/app/components/datasource-entity.directive.js
index b02a30d..6bc1932 100644
--- a/ui/src/app/components/datasource-entity.directive.js
+++ b/ui/src/app/components/datasource-entity.directive.js
@@ -186,7 +186,7 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
                 random: tinycolor.random(),
                 clickOutsideToClose: false,
                 hasBackdrop: false,
-                skipHide: true,
+                multiple: true,
                 preserveScope: false,
 
                 mdColorAlphaChannel: true,
@@ -220,7 +220,7 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
                 parent: angular.element($document[0].body),
                 fullscreen: true,
                 targetEvent: event,
-                skipHide: true,
+                multiple: true,
                 onComplete: function () {
                     var w = angular.element($window);
                     w.triggerHandler('resize');
diff --git a/ui/src/app/components/datasource-entity.tpl.html b/ui/src/app/components/datasource-entity.tpl.html
index db6fd3b..8f25787 100644
--- a/ui/src/app/components/datasource-entity.tpl.html
+++ b/ui/src/app/components/datasource-entity.tpl.html
@@ -26,7 +26,6 @@
 		   <section flex layout='column' layout-align="center" style="padding-left: 4px;">
 			   <md-chips flex ng-if="widgetType != types.widgetType.alarm.value"
 						 id="timeseries_datakey_chips"
-						 ng-required="true"
 						 ng-model="timeseriesDataKeys" md-autocomplete-snap
 						 md-transform-chip="transformTimeseriesDataKeyChip($chip)"
 						 md-require-match="false">
@@ -78,7 +77,6 @@
 			   </md-chips>
 			   <md-chips flex ng-if="widgetType === types.widgetType.latest.value"
                          id="attribute_datakey_chips"
-                         ng-required="true"
                          ng-model="attributeDataKeys" md-autocomplete-snap
                          md-transform-chip="transformAttributeDataKeyChip($chip)"
                          md-require-match="false">
diff --git a/ui/src/app/components/datasource-func.directive.js b/ui/src/app/components/datasource-func.directive.js
index 7515b61..982685f 100644
--- a/ui/src/app/components/datasource-func.directive.js
+++ b/ui/src/app/components/datasource-func.directive.js
@@ -139,7 +139,7 @@ function DatasourceFunc($compile, $templateCache, $mdDialog, $window, $document,
                 random: tinycolor.random(),
                 clickOutsideToClose: false,
                 hasBackdrop: false,
-                skipHide: true,
+                multiple: true,
                 preserveScope: false,
 
                 mdColorAlphaChannel: true,
@@ -173,7 +173,7 @@ function DatasourceFunc($compile, $templateCache, $mdDialog, $window, $document,
                 parent: angular.element($document[0].body),
                 fullscreen: true,
                 targetEvent: event,
-                skipHide: true,
+                multiple: true,
                 onComplete: function () {
                     var w = angular.element($window);
                     w.triggerHandler('resize');
diff --git a/ui/src/app/components/json-form.directive.js b/ui/src/app/components/json-form.directive.js
index b016f57..97b7bfb 100644
--- a/ui/src/app/components/json-form.directive.js
+++ b/ui/src/app/components/json-form.directive.js
@@ -96,7 +96,7 @@ function JsonForm($compile, $templateCache, $mdColorPicker) {
                 random: tinycolor.random(),
                 clickOutsideToClose: false,
                 hasBackdrop: false,
-                skipHide: true,
+                multiple: true,
                 preserveScope: false,
 
                 mdColorAlphaChannel: true,
diff --git a/ui/src/app/components/material-icon-select.directive.js b/ui/src/app/components/material-icon-select.directive.js
index 67caff2..f3efe63 100644
--- a/ui/src/app/components/material-icon-select.directive.js
+++ b/ui/src/app/components/material-icon-select.directive.js
@@ -67,7 +67,7 @@ function MaterialIconSelect($compile, $templateCache, $document, $mdDialog) {
                 templateUrl: materialIconsDialogTemplate,
                 parent: angular.element($document[0].body),
                 locals: {icon: scope.icon},
-                skipHide: true,
+                multiple: true,
                 fullscreen: true,
                 targetEvent: $event
             }).then(function (icon) {
diff --git a/ui/src/app/components/widget/action/manage-widget-actions.directive.js b/ui/src/app/components/widget/action/manage-widget-actions.directive.js
index 8110322..9597204 100644
--- a/ui/src/app/components/widget/action/manage-widget-actions.directive.js
+++ b/ui/src/app/components/widget/action/manage-widget-actions.directive.js
@@ -164,7 +164,7 @@ function ManageWidgetActionsController($rootScope, $scope, $document, $mdDialog,
                 .cancel($translate.instant('action.no'))
                 .ok($translate.instant('action.yes'));
 
-            confirm._options.skipHide = true;
+            confirm._options.multiple = true;
             confirm._options.fullscreen = true;
 
             $mdDialog.show(confirm).then(function () {
@@ -212,7 +212,7 @@ function ManageWidgetActionsController($rootScope, $scope, $document, $mdDialog,
             locals: {isAdd: isAdd, fetchDashboardStates: vm.fetchDashboardStates,
                 actionSources: availableActionSources, widgetActions: vm.widgetActions,
                 action: angular.copy(action)},
-            skipHide: true,
+            multiple: true,
             fullscreen: true,
             targetEvent: $event
         }).then(function (action) {
diff --git a/ui/src/app/dashboard/add-widget.controller.js b/ui/src/app/dashboard/add-widget.controller.js
index caa69d6..fd665fd 100644
--- a/ui/src/app/dashboard/add-widget.controller.js
+++ b/ui/src/app/dashboard/add-widget.controller.js
@@ -166,7 +166,7 @@ export default function AddWidgetController($scope, widgetService, entityService
             },
             parent: angular.element($document[0].body),
             fullscreen: true,
-            skipHide: true,
+            multiple: true,
             targetEvent: event
         }).then(function (singleEntityAlias) {
             vm.dashboard.configuration.entityAliases[singleEntityAlias.id] = singleEntityAlias;
diff --git a/ui/src/app/dashboard/dashboard.controller.js b/ui/src/app/dashboard/dashboard.controller.js
index 6df48df..3f32e1b 100644
--- a/ui/src/app/dashboard/dashboard.controller.js
+++ b/ui/src/app/dashboard/dashboard.controller.js
@@ -463,7 +463,7 @@ export default function DashboardController(types, utils, dashboardUtils, widget
                 }
             },
             parent: angular.element($document[0].body),
-            skipHide: true,
+            multiple: true,
             fullscreen: true,
             targetEvent: $event
         }).then(function (entityAliases) {
@@ -488,7 +488,7 @@ export default function DashboardController(types, utils, dashboardUtils, widget
                 gridSettings: gridSettings
             },
             parent: angular.element($document[0].body),
-            skipHide: true,
+            multiple: true,
             fullscreen: true,
             targetEvent: $event
         }).then(function (data) {
@@ -510,7 +510,7 @@ export default function DashboardController(types, utils, dashboardUtils, widget
                 layouts: angular.copy(vm.dashboard.configuration.states[vm.dashboardCtx.state].layouts)
             },
             parent: angular.element($document[0].body),
-            skipHide: true,
+            multiple: true,
             fullscreen: true,
             targetEvent: $event
         }).then(function (layouts) {
@@ -531,7 +531,7 @@ export default function DashboardController(types, utils, dashboardUtils, widget
                 states: states
             },
             parent: angular.element($document[0].body),
-            skipHide: true,
+            multiple: true,
             fullscreen: true,
             targetEvent: $event
         }).then(function (states) {
@@ -873,7 +873,7 @@ export default function DashboardController(types, utils, dashboardUtils, widget
                 templateUrl: selectTargetLayoutTemplate,
                 parent: angular.element($document[0].body),
                 fullscreen: true,
-                skipHide: true,
+                multiple: true,
                 targetEvent: $event
             }).then(
                 function success(layoutId) {
@@ -941,7 +941,7 @@ export default function DashboardController(types, utils, dashboardUtils, widget
                         },
                         parent: angular.element($document[0].body),
                         fullscreen: true,
-                        skipHide: true,
+                        multiple: true,
                         targetEvent: event,
                         onComplete: function () {
                             var w = angular.element($window);
diff --git a/ui/src/app/dashboard/edit-widget.directive.js b/ui/src/app/dashboard/edit-widget.directive.js
index e6858b4..f8aee69 100644
--- a/ui/src/app/dashboard/edit-widget.directive.js
+++ b/ui/src/app/dashboard/edit-widget.directive.js
@@ -131,7 +131,7 @@ export default function EditWidgetDirective($compile, $templateCache, types, wid
                 },
                 parent: angular.element($document[0].body),
                 fullscreen: true,
-                skipHide: true,
+                multiple: true,
                 targetEvent: event
             }).then(function (singleEntityAlias) {
                 scope.dashboard.configuration.entityAliases[singleEntityAlias.id] = singleEntityAlias;
diff --git a/ui/src/app/dashboard/layouts/manage-dashboard-layouts.controller.js b/ui/src/app/dashboard/layouts/manage-dashboard-layouts.controller.js
index 5ce8a6c..828bd6f 100644
--- a/ui/src/app/dashboard/layouts/manage-dashboard-layouts.controller.js
+++ b/ui/src/app/dashboard/layouts/manage-dashboard-layouts.controller.js
@@ -51,7 +51,7 @@ export default function ManageDashboardLayoutsController($scope, $mdDialog, $doc
                 gridSettings: gridSettings
             },
             parent: angular.element($document[0].body),
-            skipHide: true,
+            multiple: true,
             fullscreen: true,
             targetEvent: $event
         }).then(function (data) {
diff --git a/ui/src/app/dashboard/states/manage-dashboard-states.controller.js b/ui/src/app/dashboard/states/manage-dashboard-states.controller.js
index 98df466..298b2ab 100644
--- a/ui/src/app/dashboard/states/manage-dashboard-states.controller.js
+++ b/ui/src/app/dashboard/states/manage-dashboard-states.controller.js
@@ -111,7 +111,7 @@ export default function ManageDashboardStatesController($scope, $mdDialog, $filt
             templateUrl: dashboardStateDialogTemplate,
             parent: angular.element($document[0].body),
             locals: {isAdd: isAdd, allStates: vm.allStates, state: angular.copy(state)},
-            skipHide: true,
+            multiple: true,
             fullscreen: true,
             targetEvent: $event
         }).then(function (state) {
@@ -163,7 +163,7 @@ export default function ManageDashboardStatesController($scope, $mdDialog, $filt
                 .cancel($translate.instant('action.no'))
                 .ok($translate.instant('action.yes'));
 
-            confirm._options.skipHide = true;
+            confirm._options.multiple = true;
             confirm._options.fullscreen = true;
 
             $mdDialog.show(confirm).then(function () {
diff --git a/ui/src/app/entity/alias/entity-aliases.controller.js b/ui/src/app/entity/alias/entity-aliases.controller.js
index 732f10f..49f28c9 100644
--- a/ui/src/app/entity/alias/entity-aliases.controller.js
+++ b/ui/src/app/entity/alias/entity-aliases.controller.js
@@ -126,7 +126,7 @@ export default function EntityAliasesController(utils, entityService, toast, $sc
             },
             parent: angular.element($document[0].body),
             fullscreen: true,
-            skipHide: true,
+            multiple: true,
             targetEvent: $event
         }).then(function (alias) {
             if (isAdd) {
@@ -158,7 +158,7 @@ export default function EntityAliasesController(utils, entityService, toast, $sc
                     .ariaLabel($translate.instant('entity.unable-delete-entity-alias-title'))
                     .ok($translate.instant('action.close'))
                     .targetEvent($event);
-                alert._options.skipHide = true;
+                alert._options.multiple = true;
                 alert._options.fullscreen = true;
 
                 $mdDialog.show(alert);
diff --git a/ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js b/ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js
index 5346e03..f39593c 100644
--- a/ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js
+++ b/ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js
@@ -53,7 +53,7 @@ export default function AddWidgetToDashboardDialogController($scope, $mdDialog, 
                     states: states
                 },
                 fullscreen: true,
-                skipHide: true,
+                multiple: true,
                 targetEvent: $event
             }).then(
                 function success(stateId) {
@@ -81,7 +81,7 @@ export default function AddWidgetToDashboardDialogController($scope, $mdDialog, 
                 templateUrl: selectTargetLayoutTemplate,
                 parent: angular.element($document[0].body),
                 fullscreen: true,
-                skipHide: true,
+                multiple: true,
                 targetEvent: $event
             }).then(
                 function success(layoutId) {
diff --git a/ui/src/app/entity/relation/relation-table.directive.js b/ui/src/app/entity/relation/relation-table.directive.js
index 872042c..d2586c4 100644
--- a/ui/src/app/entity/relation/relation-table.directive.js
+++ b/ui/src/app/entity/relation/relation-table.directive.js
@@ -160,7 +160,7 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $
                       showingCallback: onShowingCallback},
             targetEvent: $event,
             fullscreen: true,
-            skipHide: true,
+            multiple: true,
             onShowing: function(scope, element) {
                 onShowingCallback.onShowing(scope, element);
             }
diff --git a/ui/src/app/event/event-row.directive.js b/ui/src/app/event/event-row.directive.js
index b808fb8..5f84187 100644
--- a/ui/src/app/event/event-row.directive.js
+++ b/ui/src/app/event/event-row.directive.js
@@ -79,7 +79,7 @@ export default function EventRowDirective($compile, $templateCache, $mdDialog, $
                 parent: angular.element($document[0].body),
                 fullscreen: true,
                 targetEvent: $event,
-                skipHide: true,
+                multiple: true,
                 onShowing: function(scope, element) {
                     onShowingCallback.onShowing(scope, element);
                 }
diff --git a/ui/src/app/extension/extension-table.directive.js b/ui/src/app/extension/extension-table.directive.js
index 18d281c..73c5d93 100644
--- a/ui/src/app/extension/extension-table.directive.js
+++ b/ui/src/app/extension/extension-table.directive.js
@@ -208,7 +208,7 @@ function ExtensionTableController($scope, $filter, $document, $translate, $timeo
             bindToController: true,
             targetEvent: $event,
             fullscreen: true,
-            skipHide: true
+            multiple: true
         }).then(function() {
             reloadExtensions();
         }, function () {
diff --git a/ui/src/app/import-export/import-export.service.js b/ui/src/app/import-export/import-export.service.js
index d64441f..e08fd24 100644
--- a/ui/src/app/import-export/import-export.service.js
+++ b/ui/src/app/import-export/import-export.service.js
@@ -721,7 +721,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
                 }
             },
             parent: angular.element($document[0].body),
-            skipHide: true,
+            multiple: true,
             fullscreen: true,
             targetEvent: $event
         }).then(function (updatedEntityAliases) {
@@ -796,7 +796,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
                 importFileLabel: importFileLabel
             },
             parent: angular.element($document[0].body),
-            skipHide: true,
+            multiple: true,
             fullscreen: true,
             targetEvent: $event
         }).then(function (importData) {
diff --git a/ui/src/app/layout/index.js b/ui/src/app/layout/index.js
index 192400a..7c7e870 100644
--- a/ui/src/app/layout/index.js
+++ b/ui/src/app/layout/index.js
@@ -17,7 +17,6 @@ import './home.scss';
 
 import uiRouter from 'angular-ui-router';
 import ngSanitize from 'angular-sanitize';
-import FBAngular from 'angular-fullscreen';
 import 'angular-breadcrumb';
 
 import thingsboardMenu from '../services/menu.service';
@@ -63,7 +62,6 @@ import BreadcrumbIcon from './breadcrumb-icon.filter';
 export default angular.module('thingsboard.home', [
     uiRouter,
     ngSanitize,
-    FBAngular.name,
     'ncy-angular-breadcrumb',
     thingsboardMenu,
     thingsboardHomeLinks,
diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json
index 9f326c7..b3c89c7 100644
--- a/ui/src/app/locale/locale.constant-en_US.json
+++ b/ui/src/app/locale/locale.constant-en_US.json
@@ -1573,7 +1573,7 @@
             "ru_RU": "Russian",
             "es_ES": "Spanish",
             "ja_JA": "Japanese",
-            "TR": "Turkish"
+            "tr_TR": "Turkish"
         }
     }
 }
diff --git a/ui/src/app/locale/locale.constant-es_ES.json b/ui/src/app/locale/locale.constant-es_ES.json
index 81cc52e..7b02141 100644
--- a/ui/src/app/locale/locale.constant-es_ES.json
+++ b/ui/src/app/locale/locale.constant-es_ES.json
@@ -1552,7 +1552,7 @@
             "ru_RU": "Ruso",
             "es_ES": "Español",
             "ja_JA": "Japonés",
-            "TR": "Turco"
+            "tr_TR": "Turco"
         }
     }
 }
diff --git a/ui/src/app/locale/locale.constant-fr_FR.json b/ui/src/app/locale/locale.constant-fr_FR.json
index 8b270aa..dd2fa0d 100644
--- a/ui/src/app/locale/locale.constant-fr_FR.json
+++ b/ui/src/app/locale/locale.constant-fr_FR.json
@@ -1010,7 +1010,7 @@
             "ko_KR": "Coréen",
             "ru_RU": "Russe",
             "zh_CN": "Chinois",
-            "TR": "Turc"
+            "tr_TR": "Turc"
         }
     },
     "layout": {
diff --git a/ui/src/app/locale/locale.constant-it_IT.json b/ui/src/app/locale/locale.constant-it_IT.json
index 79843ab..49f9b48 100644
--- a/ui/src/app/locale/locale.constant-it_IT.json
+++ b/ui/src/app/locale/locale.constant-it_IT.json
@@ -1442,7 +1442,7 @@
             "ru_RU": "Russo",
             "es_ES": "Spagnolo",
             "ja_JA": "Giapponese",
-            "TR": "Turco"
+            "tr_TR": "Turco"
         }
     }
 }
diff --git a/ui/src/app/locale/locale.constant-ja_JA.json b/ui/src/app/locale/locale.constant-ja_JA.json
index 6c48894..2ab2bcb 100644
--- a/ui/src/app/locale/locale.constant-ja_JA.json
+++ b/ui/src/app/locale/locale.constant-ja_JA.json
@@ -1458,7 +1458,7 @@
 			"ru_RU": "ロシア",
 			"es_ES": "スペイン語",
 			"ja_JA": "日本語",
-			"TR": "トルコ語"
+			"tr_TR": "トルコ語"
 		}
 	}
 }
diff --git a/ui/src/app/locale/locale.constant-ko_KR.json b/ui/src/app/locale/locale.constant-ko_KR.json
index 07e7dd6..62c0054 100644
--- a/ui/src/app/locale/locale.constant-ko_KR.json
+++ b/ui/src/app/locale/locale.constant-ko_KR.json
@@ -1336,7 +1336,7 @@
             "es_ES": "스페인어",
             "it_IT": "이탈리아 사람",
             "ja_JA": "일본어",
-            "TR": "터키어"
+            "tr_TR": "터키어"
         }
     }
 }
diff --git a/ui/src/app/locale/locale.constant-ru_RU.json b/ui/src/app/locale/locale.constant-ru_RU.json
index 893bbcd..eb9996d 100644
--- a/ui/src/app/locale/locale.constant-ru_RU.json
+++ b/ui/src/app/locale/locale.constant-ru_RU.json
@@ -1361,7 +1361,7 @@
             "it_IT": "Итальянский",
             "ru_RU": "Русский",
             "ja_JA": "Японский",
-            "TR": "Турецкий"
+            "tr_TR": "Турецкий"
         }
 
     }
diff --git a/ui/src/app/locale/locale.constant-zh_CN.json b/ui/src/app/locale/locale.constant-zh_CN.json
index c952f9c..bac1706 100644
--- a/ui/src/app/locale/locale.constant-zh_CN.json
+++ b/ui/src/app/locale/locale.constant-zh_CN.json
@@ -1445,7 +1445,7 @@
             "es_ES": "西班牙语",
             "it_IT": "意大利",
             "ja_JA": "日本",
-            "TR": "土耳其"
+            "tr_TR": "土耳其"
         }
     }
 }
diff --git a/ui/src/app/rulechain/script/node-script-test.service.js b/ui/src/app/rulechain/script/node-script-test.service.js
index 81d81c1..d4e6df8 100644
--- a/ui/src/app/rulechain/script/node-script-test.service.js
+++ b/ui/src/app/rulechain/script/node-script-test.service.js
@@ -121,7 +121,7 @@ export default function NodeScriptTest($q, $mdDialog, $document, ruleChainServic
                 onShowingCallback: onShowingCallback
             },
             fullscreen: true,
-            skipHide: true,
+            multiple: true,
             targetEvent: $event,
             onComplete: () => {
                 onShowingCallback.onShowed();
diff --git a/ui/src/app/user/add-user.controller.js b/ui/src/app/user/add-user.controller.js
index 20ad0be..c81cd67 100644
--- a/ui/src/app/user/add-user.controller.js
+++ b/ui/src/app/user/add-user.controller.js
@@ -100,7 +100,7 @@ export default function AddUserController($scope, $mdDialog, $state, $stateParam
             },
             parent: angular.element($document[0].body),
             fullscreen: true,
-            skipHide: true,
+            multiple: true,
             targetEvent: $event
         }).then(function () {
             deferred.resolve();
diff --git a/ui/src/app/user/user.controller.js b/ui/src/app/user/user.controller.js
index 8d7f214..ef7948d 100644
--- a/ui/src/app/user/user.controller.js
+++ b/ui/src/app/user/user.controller.js
@@ -186,7 +186,7 @@ export default function UserController(userService, toast, $scope, $mdDialog, $d
             },
             parent: angular.element($document[0].body),
             fullscreen: true,
-            skipHide: true,
+            multiple: true,
             targetEvent: event
         });
     }
diff --git a/ui/src/app/widget/lib/alarms-table-widget.js b/ui/src/app/widget/lib/alarms-table-widget.js
index 6ae17e0..3d91e9a 100644
--- a/ui/src/app/widget/lib/alarms-table-widget.js
+++ b/ui/src/app/widget/lib/alarms-table-widget.js
@@ -402,7 +402,7 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
                 parent: angular.element($document[0].body),
                 targetEvent: $event,
                 fullscreen: true,
-                skipHide: true,
+                multiple: true,
                 onShowing: function(scope, element) {
                     onShowingCallback.onShowing(scope, element);
                 }
diff --git a/ui/src/app/widget/lib/canvas-digital-gauge.js b/ui/src/app/widget/lib/canvas-digital-gauge.js
index 283a426..274cc97 100644
--- a/ui/src/app/widget/lib/canvas-digital-gauge.js
+++ b/ui/src/app/widget/lib/canvas-digital-gauge.js
@@ -209,8 +209,8 @@ export default class TbCanvasDigitalGauge {
                 }
                 var value = tvPair[1];
                 if(value !== this.gauge.value) {
-                    this.gauge.value = value;
                     this.gauge._value = value;
+                    this.gauge.value = value;
                 } else if (this.localSettings.showTimestamp && this.gauge.timestamp != timestamp) {
                     this.gauge.timestamp = timestamp;
                 }