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 803f1d3953..1ccfdc9071 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 \"
\"\n );\n\n var datasourceContainer = $('#' + datasourceId,\n self.ctx.$container);\n\n datasourceContainer.append(\n \"
\" +\n tbDatasource.name + \"
\"\n );\n \n var datasourceTitleCell = $('.tbDatasource-title', datasourceContainer);\n self.ctx.datasourceTitleCells.push(datasourceTitleCell);\n \n var tableId = 'table' + i;\n datasourceContainer.append(\n \"
\"\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(\"\" + dataKey.label +\n \"\");\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 self.ctx.valueCells[i].html(value);\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 \"
\"\n );\n\n var datasourceContainer = $('#' + datasourceId,\n self.ctx.$container);\n\n datasourceContainer.append(\n \"
\" +\n tbDatasource.name + \"
\"\n );\n \n var datasourceTitleCell = $('.tbDatasource-title', datasourceContainer);\n self.ctx.datasourceTitleCells.push(datasourceTitleCell);\n \n var tableId = 'table' + i;\n datasourceContainer.append(\n \"
\"\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(\"\" + dataKey.label +\n \"\");\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", "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\"}" @@ -95,7 +95,7 @@ "resources": [], "templateHtml": "", "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n width: 100%;\n height: 100%;\n overflow: hidden;\n}\n\n.tbDatasource-table {\n width: 100%;\n height: 100%;\n border-collapse: collapse;\n white-space: nowrap;\n font-weight: 100;\n text-align: right;\n}\n\n.tbDatasource-table td {\n padding: 12px;\n position: relative;\n box-sizing: border-box;\n}\n\n.tbDatasource-data-key {\n opacity: 0.7;\n font-weight: 400;\n font-size: 3.500rem;\n}\n\n.tbDatasource-value {\n font-size: 5.000rem;\n}", - "controllerScript": "self.onInit = function() {\n\n self.ctx.labelPosition = self.ctx.settings.labelPosition || 'left';\n \n if (self.ctx.datasources.length > 0) {\n var tbDatasource = self.ctx.datasources[0];\n var datasourceId = 'tbDatasource' + 0;\n self.ctx.$container.append(\n \"
\"\n );\n \n self.ctx.datasourceContainer = $('#' + datasourceId,\n self.ctx.$container);\n \n var tableId = 'table' + 0;\n self.ctx.datasourceContainer.append(\n \"
\"\n );\n var table = $('#' + tableId, self.ctx.$container);\n if (self.ctx.labelPosition === 'top') {\n table.css('text-align', 'left');\n }\n \n if (tbDatasource.dataKeys.length > 0) {\n var dataKey = tbDatasource.dataKeys[0];\n var labelCellId = 'labelCell' + 0;\n var cellId = 'cell' + 0;\n if (self.ctx.labelPosition === 'left') {\n table.append(\n \"\" +\n dataKey.label +\n \"\");\n } else {\n table.append(\n \"\" +\n dataKey.label +\n \"\");\n }\n self.ctx.labelCell = $('#' + labelCellId, table);\n self.ctx.valueCell = $('#' + cellId, table);\n self.ctx.valueCell.html(0 + ' ' + self.ctx.units);\n }\n }\n \n $.fn.textWidth = function(){\n var html_org = $(this).html();\n var html_calc = '' + html_org + '';\n $(this).html(html_calc);\n var width = $(this).find('span:first').width();\n $(this).html(html_org);\n return width;\n }; \n \n self.onResize();\n};\n\nself.onDataUpdated = function() {\n \n function isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n }\n\n if (self.ctx.valueCell && self.ctx.data.length > 0) {\n var cellData = self.ctx.data[0];\n if (cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length -\n 1];\n var value = tvPair[1];\n var txtValue;\n if (isNumber(value)) {\n txtValue = self.ctx.utils.formatValue(value, self.ctx.decimals, self.ctx.units);\n } else {\n txtValue = value;\n }\n self.ctx.valueCell.html(txtValue);\n var targetWidth;\n var minDelta;\n if (self.ctx.labelPosition === 'left') {\n targetWidth = self.ctx.datasourceContainer.width() - self.ctx.labelCell.width();\n minDelta = self.ctx.width/16 + self.ctx.padding;\n } else {\n targetWidth = self.ctx.datasourceContainer.width();\n minDelta = self.ctx.padding;\n }\n var delta = targetWidth - self.ctx.valueCell.textWidth();\n var fontSize = self.ctx.valueFontSize;\n if (targetWidth > minDelta) {\n while (delta < minDelta && fontSize > 6) {\n fontSize--;\n self.ctx.valueCell.css('font-size', fontSize+'px');\n delta = targetWidth - self.ctx.valueCell.textWidth();\n }\n }\n }\n } \n \n};\n\nself.onResize = function() {\n var labelFontSize;\n if (self.ctx.labelPosition === 'top') {\n self.ctx.padding = self.ctx.height/20;\n labelFontSize = self.ctx.height/4;\n self.ctx.valueFontSize = self.ctx.height/2;\n } else {\n self.ctx.padding = self.ctx.width/50;\n labelFontSize = self.ctx.height/2.5;\n self.ctx.valueFontSize = self.ctx.height/2;\n if (self.ctx.width/self.ctx.height <= 2.7) {\n labelFontSize = self.ctx.width/7;\n self.ctx.valueFontSize = self.ctx.width/6;\n }\n }\n self.ctx.padding = Math.min(12, self.ctx.padding);\n \n if (self.ctx.labelCell) {\n self.ctx.labelCell.css('font-size', labelFontSize+'px');\n self.ctx.labelCell.css('padding', self.ctx.padding+'px');\n }\n if (self.ctx.valueCell) {\n self.ctx.valueCell.css('font-size', self.ctx.valueFontSize+'px');\n self.ctx.valueCell.css('padding', self.ctx.padding+'px');\n } \n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "controllerScript": "self.onInit = function() {\n\n self.ctx.labelPosition = self.ctx.settings.labelPosition || 'left';\n \n if (self.ctx.datasources.length > 0) {\n var tbDatasource = self.ctx.datasources[0];\n var datasourceId = 'tbDatasource' + 0;\n self.ctx.$container.append(\n \"
\"\n );\n \n self.ctx.datasourceContainer = $('#' + datasourceId,\n self.ctx.$container);\n \n var tableId = 'table' + 0;\n self.ctx.datasourceContainer.append(\n \"
\"\n );\n var table = $('#' + tableId, self.ctx.$container);\n if (self.ctx.labelPosition === 'top') {\n table.css('text-align', 'left');\n }\n \n if (tbDatasource.dataKeys.length > 0) {\n var dataKey = tbDatasource.dataKeys[0];\n var labelCellId = 'labelCell' + 0;\n var cellId = 'cell' + 0;\n if (self.ctx.labelPosition === 'left') {\n table.append(\n \"\" +\n dataKey.label +\n \"\");\n } else {\n table.append(\n \"\" +\n dataKey.label +\n \"\");\n }\n self.ctx.labelCell = $('#' + labelCellId, table);\n self.ctx.valueCell = $('#' + cellId, table);\n self.ctx.valueCell.html(0 + ' ' + self.ctx.units);\n }\n }\n \n $.fn.textWidth = function(){\n var html_org = $(this).html();\n var html_calc = '' + html_org + '';\n $(this).html(html_calc);\n var width = $(this).find('span:first').width();\n $(this).html(html_org);\n return width;\n }; \n \n self.onResize();\n};\n\nself.onDataUpdated = function() {\n \n function isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n }\n\n if (self.ctx.valueCell && self.ctx.data.length > 0) {\n var cellData = self.ctx.data[0];\n if (cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length -\n 1];\n var value = tvPair[1];\n var txtValue;\n if (isNumber(value)) {\n var decimals = self.ctx.decimals;\n var units = self.ctx.units;\n if (self.ctx.datasources.length > 0 && self.ctx.datasources[0].dataKeys.length > 0) {\n dataKey = self.ctx.datasources[0].dataKeys[0];\n if (dataKey.decimals || dataKey.decimals === 0) {\n decimals = dataKey.decimals;\n }\n if (dataKey.units) {\n units = dataKey.units;\n }\n }\n txtValue = self.ctx.utils.formatValue(value, decimals, units, true);\n } else {\n txtValue = value;\n }\n self.ctx.valueCell.html(txtValue);\n var targetWidth;\n var minDelta;\n if (self.ctx.labelPosition === 'left') {\n targetWidth = self.ctx.datasourceContainer.width() - self.ctx.labelCell.width();\n minDelta = self.ctx.width/16 + self.ctx.padding;\n } else {\n targetWidth = self.ctx.datasourceContainer.width();\n minDelta = self.ctx.padding;\n }\n var delta = targetWidth - self.ctx.valueCell.textWidth();\n var fontSize = self.ctx.valueFontSize;\n if (targetWidth > minDelta) {\n while (delta < minDelta && fontSize > 6) {\n fontSize--;\n self.ctx.valueCell.css('font-size', fontSize+'px');\n delta = targetWidth - self.ctx.valueCell.textWidth();\n }\n }\n }\n } \n \n};\n\nself.onResize = function() {\n var labelFontSize;\n if (self.ctx.labelPosition === 'top') {\n self.ctx.padding = self.ctx.height/20;\n labelFontSize = self.ctx.height/4;\n self.ctx.valueFontSize = self.ctx.height/2;\n } else {\n self.ctx.padding = self.ctx.width/50;\n labelFontSize = self.ctx.height/2.5;\n self.ctx.valueFontSize = self.ctx.height/2;\n if (self.ctx.width/self.ctx.height <= 2.7) {\n labelFontSize = self.ctx.width/7;\n self.ctx.valueFontSize = self.ctx.width/6;\n }\n }\n self.ctx.padding = Math.min(12, self.ctx.padding);\n \n if (self.ctx.labelCell) {\n self.ctx.labelCell.css('font-size', labelFontSize+'px');\n self.ctx.labelCell.css('padding', self.ctx.padding+'px');\n }\n if (self.ctx.valueCell) {\n self.ctx.valueCell.css('font-size', self.ctx.valueFontSize+'px');\n self.ctx.valueCell.css('padding', self.ctx.padding+'px');\n } \n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n };\n};\n\n\nself.onDestroy = function() {\n};\n", "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"labelPosition\": {\n \"title\": \"Label position\",\n \"type\": \"string\",\n \"default\": \"left\"\n }\n },\n \"required\": []\n },\n \"form\": [\n {\n \"key\": \"labelPosition\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"left\",\n \"label\": \"Left\"\n },\n {\n \"value\": \"top\",\n \"label\": \"Top\"\n }\n ]\n }\n ]\n}", "dataKeySettingsSchema": "{}\n", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ff5722\",\"color\":\"rgba(255, 255, 255, 0.87)\",\"padding\":\"16px\",\"settings\":{\"labelPosition\":\"top\"},\"title\":\"Simple card\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" diff --git a/application/src/main/data/json/system/widget_bundles/charts.json b/application/src/main/data/json/system/widget_bundles/charts.json index a264ba6515..71e9fa7ace 100644 --- a/application/src/main/data/json/system/widget_bundles/charts.json +++ b/application/src/main/data/json/system/widget_bundles/charts.json @@ -35,7 +35,7 @@ "resources": [], "templateHtml": "", "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", - "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema;\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true);\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('graph');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true);\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n", "settingsSchema": "{}", "dataKeySettingsSchema": "{}", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"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;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"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\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"legend\":{\"show\":true,\"position\":\"nw\",\"backgroundColor\":\"#f0f0f0\",\"backgroundOpacity\":0.85,\"labelBoxBorderColor\":\"rgba(1, 1, 1, 0.45)\"},\"decimals\":1,\"stack\":false,\"tooltipIndividual\":false},\"title\":\"Timeseries - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null}" @@ -147,10 +147,10 @@ "resources": [], "templateHtml": "", "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", - "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'bar'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema;\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(false);\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'bar'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('bar');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(false);\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n", "settingsSchema": "{}", "dataKeySettingsSchema": "{}", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000},\"aggregation\":{\"limit\":200,\"type\":\"AVG\"}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"legend\":{\"show\":true,\"position\":\"nw\",\"backgroundColor\":\"#f0f0f0\",\"backgroundOpacity\":0.85,\"labelBoxBorderColor\":\"rgba(1, 1, 1, 0.45)\"},\"decimals\":1,\"stack\":true,\"tooltipIndividual\":false},\"title\":\"Timeseries Bars - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null}" + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000},\"aggregation\":{\"limit\":200,\"type\":\"AVG\"}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"stack\":true,\"tooltipIndividual\":false,\"defaultBarWidth\":600},\"title\":\"Timeseries Bars - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{}}" } }, { @@ -163,7 +163,7 @@ "resources": [], "templateHtml": "", "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", - "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'state'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.typeParameters = function() {\n return {\n stateData: true\n };\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema;\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true);\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'state'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.typeParameters = function() {\n return {\n stateData: true\n };\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('graph');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true);\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n", "settingsSchema": "{}", "dataKeySettingsSchema": "{}", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 1\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false,\"axisPosition\":\"left\",\"showSeparateAxis\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"return Math.random() > 0.5 ? 1 : 0;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 2\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false,\"axisPosition\":\"left\"},\"_hash\":0.12775350966079668,\"funcBody\":\"return Math.random() <= 0.5 ? 1 : 0;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\",\"ticksFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"stack\":false,\"tooltipIndividual\":false,\"tooltipValueFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\",\"smoothLines\":false},\"title\":\"State Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{},\"legendConfig\":{\"position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}" diff --git a/application/src/main/data/upgrade/2.1.1/schema_update.cql b/application/src/main/data/upgrade/2.1.1/schema_update.cql index c477e8a02a..36ac8e4d38 100644 --- a/application/src/main/data/upgrade/2.1.1/schema_update.cql +++ b/application/src/main/data/upgrade/2.1.1/schema_update.cql @@ -20,7 +20,7 @@ DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_customer; DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_entity_id; DROP TABLE IF EXISTS thingsboard.entity_views; -ControllerSqlTestSuite + CREATE TABLE IF NOT EXISTS thingsboard.entity_views ( id timeuuid, entity_id timeuuid, diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 720bb9f490..f35df67265 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -391,6 +391,7 @@ audit_log: "user": "${AUDIT_LOG_MASK_USER:W}" "rule_chain": "${AUDIT_LOG_MASK_RULE_CHAIN:W}" "alarm": "${AUDIT_LOG_MASK_ALARM:W}" + "entity_view": "${AUDIT_LOG_MASK_RULE_CHAIN:W}" sink: # Type of external sink. possible options: none, elasticsearch type: "${AUDIT_LOG_SINK_TYPE:none}" diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java index f0fb51e6e8..2e7b412c31 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java @@ -73,4 +73,6 @@ public abstract class DeviceAwareSessionContext implements SessionContext { public Device getDevice() { return device; } + + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java index f35b89069c..ebd056048b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java @@ -54,6 +54,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import static org.thingsboard.server.common.data.CacheConstants.ASSET_CACHE; @@ -141,7 +142,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ if (entityViews != null && !entityViews.isEmpty()) { throw new DataValidationException("Can't delete asset that is assigned to entity views!"); } - } catch (Exception e) { + } catch (ExecutionException | InterruptedException e) { log.error("Exception while finding entity views for assetId [{}]", assetId, e); throw new RuntimeException("Exception while finding entity views for assetId [" + assetId + "]", e); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index 6f9ea62244..2be5ba4f41 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -58,6 +58,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Optional; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import static org.thingsboard.server.common.data.CacheConstants.DEVICE_CACHE; @@ -158,7 +159,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe if (entityViews != null && !entityViews.isEmpty()) { throw new DataValidationException("Can't delete device that is assigned to entity views!"); } - } catch (Exception e) { + } catch (ExecutionException | InterruptedException e) { log.error("Exception while finding entity views for deviceId [{}]", deviceId, e); throw new RuntimeException("Exception while finding entity views for deviceId [" + deviceId + "]", e); } diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index 185b7a82c3..ed0c8c642c 100644 --- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -51,6 +51,9 @@ import javax.security.cert.X509Certificate; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.*; import static io.netty.handler.codec.mqtt.MqttMessageType.*; @@ -75,6 +78,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement private final RelationService relationService; private final QuotaService quotaService; private final SslHandler sslHandler; + private final ConcurrentMap mqttQoSMap; + private volatile boolean connected; private volatile InetSocketAddress address; private volatile GatewaySessionCtx gatewaySessionCtx; @@ -86,7 +91,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement this.relationService = relationService; this.authService = authService; this.adaptor = adaptor; - this.deviceSessionCtx = new DeviceSessionCtx(processor, authService, adaptor); + this.mqttQoSMap = new ConcurrentHashMap<>(); + this.deviceSessionCtx = new DeviceSessionCtx(processor, authService, adaptor, mqttQoSMap); this.sessionId = deviceSessionCtx.getSessionId().toUidStr(); this.sslHandler = sslHandler; this.quotaService = quotaService; @@ -166,18 +172,25 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement private void handleMqttPublishMsg(String topicName, int msgId, MqttPublishMessage mqttMsg) { try { - if (topicName.equals(GATEWAY_TELEMETRY_TOPIC)) { - gatewaySessionCtx.onDeviceTelemetry(mqttMsg); - } else if (topicName.equals(GATEWAY_ATTRIBUTES_TOPIC)) { - gatewaySessionCtx.onDeviceAttributes(mqttMsg); - } else if (topicName.equals(GATEWAY_ATTRIBUTES_REQUEST_TOPIC)) { - gatewaySessionCtx.onDeviceAttributesRequest(mqttMsg); - } else if (topicName.equals(GATEWAY_RPC_TOPIC)) { - gatewaySessionCtx.onDeviceRpcResponse(mqttMsg); - } else if (topicName.equals(GATEWAY_CONNECT_TOPIC)) { - gatewaySessionCtx.onDeviceConnect(mqttMsg); - } else if (topicName.equals(GATEWAY_DISCONNECT_TOPIC)) { - gatewaySessionCtx.onDeviceDisconnect(mqttMsg); + switch (topicName) { + case GATEWAY_TELEMETRY_TOPIC: + gatewaySessionCtx.onDeviceTelemetry(mqttMsg); + break; + case GATEWAY_ATTRIBUTES_TOPIC: + gatewaySessionCtx.onDeviceAttributes(mqttMsg); + break; + case GATEWAY_ATTRIBUTES_REQUEST_TOPIC: + gatewaySessionCtx.onDeviceAttributesRequest(mqttMsg); + break; + case GATEWAY_RPC_TOPIC: + gatewaySessionCtx.onDeviceRpcResponse(mqttMsg); + break; + case GATEWAY_CONNECT_TOPIC: + gatewaySessionCtx.onDeviceConnect(mqttMsg); + break; + case GATEWAY_DISCONNECT_TOPIC: + gatewaySessionCtx.onDeviceDisconnect(mqttMsg); + break; } } catch (RuntimeException | AdaptorException e) { log.warn("[{}] Failed to process publish msg [{}][{}]", sessionId, topicName, msgId, e); @@ -225,52 +238,71 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId()); List grantedQoSList = new ArrayList<>(); for (MqttTopicSubscription subscription : mqttMsg.payload().topicSubscriptions()) { - String topicName = subscription.topicName(); - //TODO: handle this qos level. + String topic = subscription.topicName(); MqttQoS reqQoS = subscription.qualityOfService(); try { - if (topicName.equals(DEVICE_ATTRIBUTES_TOPIC)) { - AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg); - processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); - grantedQoSList.add(getMinSupportedQos(reqQoS)); - } else if (topicName.equals(DEVICE_RPC_REQUESTS_SUB_TOPIC)) { - AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_RPC_COMMANDS_REQUEST, mqttMsg); - processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); - grantedQoSList.add(getMinSupportedQos(reqQoS)); - } else if (topicName.equals(DEVICE_RPC_RESPONSE_SUB_TOPIC)) { - grantedQoSList.add(getMinSupportedQos(reqQoS)); - } else if (topicName.equals(DEVICE_ATTRIBUTES_RESPONSES_TOPIC)) { - deviceSessionCtx.setAllowAttributeResponses(); - grantedQoSList.add(getMinSupportedQos(reqQoS)); - } else if (topicName.equals(GATEWAY_ATTRIBUTES_TOPIC)) { - grantedQoSList.add(getMinSupportedQos(reqQoS)); - } else { - log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topicName, reqQoS); - grantedQoSList.add(FAILURE.value()); + switch (topic) { + case DEVICE_ATTRIBUTES_TOPIC: { + AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg); + processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); + registerSubQoS(topic, grantedQoSList, reqQoS); + break; + } + case DEVICE_RPC_REQUESTS_SUB_TOPIC: { + AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_RPC_COMMANDS_REQUEST, mqttMsg); + processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); + registerSubQoS(topic, grantedQoSList, reqQoS); + break; + } + case DEVICE_RPC_RESPONSE_SUB_TOPIC: + case GATEWAY_ATTRIBUTES_TOPIC: + case GATEWAY_RPC_TOPIC: + registerSubQoS(topic, grantedQoSList, reqQoS); + break; + case DEVICE_ATTRIBUTES_RESPONSES_TOPIC: + deviceSessionCtx.setAllowAttributeResponses(); + registerSubQoS(topic, grantedQoSList, reqQoS); + break; + default: + log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topic, reqQoS); + grantedQoSList.add(FAILURE.value()); + break; } } catch (AdaptorException e) { - log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topicName, reqQoS); + log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topic, reqQoS); grantedQoSList.add(FAILURE.value()); } } ctx.writeAndFlush(createSubAckMessage(mqttMsg.variableHeader().messageId(), grantedQoSList)); } + private void registerSubQoS(String topic, List grantedQoSList, MqttQoS reqQoS) { + grantedQoSList.add(getMinSupportedQos(reqQoS)); + mqttQoSMap.put(topic, getMinSupportedQos(reqQoS)); + } + private void processUnsubscribe(ChannelHandlerContext ctx, MqttUnsubscribeMessage mqttMsg) { if (!checkConnected(ctx)) { return; } log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId()); for (String topicName : mqttMsg.payload().topics()) { + mqttQoSMap.remove(topicName); try { - if (topicName.equals(DEVICE_ATTRIBUTES_TOPIC)) { - AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg); - processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); - } else if (topicName.equals(DEVICE_RPC_REQUESTS_SUB_TOPIC)) { - AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_RPC_COMMANDS_REQUEST, mqttMsg); - processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); - } else if (topicName.equals(DEVICE_ATTRIBUTES_RESPONSES_TOPIC)) { - deviceSessionCtx.setDisallowAttributeResponses(); + switch (topicName) { + case DEVICE_ATTRIBUTES_TOPIC: { + AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg); + processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); + break; + } + case DEVICE_RPC_REQUESTS_SUB_TOPIC: { + AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_RPC_COMMANDS_REQUEST, mqttMsg); + processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); + break; + } + case DEVICE_ATTRIBUTES_RESPONSES_TOPIC: + deviceSessionCtx.setDisallowAttributeResponses(); + break; } } catch (AdaptorException e) { log.warn("[{}] Failed to process unsubscription [{}] to [{}]", sessionId, mqttMsg.variableHeader().messageId(), topicName); diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java index f0b29cbd17..c8baaf7e53 100644 --- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java +++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java @@ -170,7 +170,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { private MqttPublishMessage createMqttPublishMsg(DeviceSessionCtx ctx, String topic, JsonElement json) { MqttFixedHeader mqttFixedHeader = - new MqttFixedHeader(MqttMessageType.PUBLISH, false, MqttQoS.AT_LEAST_ONCE, false, 0); + new MqttFixedHeader(MqttMessageType.PUBLISH, false, ctx.getQoSForTopic(topic), false, 0); MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, ctx.nextMsgId()); ByteBuf payload = ALLOCATOR.buffer(); payload.writeBytes(GSON.toJson(json).getBytes(UTF8)); diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java index 9367a04407..3dbb3efe0e 100644 --- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java +++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java @@ -30,13 +30,15 @@ import org.thingsboard.server.common.transport.auth.DeviceAuthService; import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; +import java.util.Map; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; /** * @author Andrew Shvayka */ @Slf4j -public class DeviceSessionCtx extends DeviceAwareSessionContext { +public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { private final MqttTransportAdaptor adaptor; private final MqttSessionId sessionId; @@ -44,8 +46,8 @@ public class DeviceSessionCtx extends DeviceAwareSessionContext { private volatile boolean allowAttributeResponses; private AtomicInteger msgIdSeq = new AtomicInteger(0); - public DeviceSessionCtx(SessionMsgProcessor processor, DeviceAuthService authService, MqttTransportAdaptor adaptor) { - super(processor, authService); + public DeviceSessionCtx(SessionMsgProcessor processor, DeviceAuthService authService, MqttTransportAdaptor adaptor, ConcurrentMap mqttQoSMap) { + super(processor, authService, mqttQoSMap); this.adaptor = adaptor; this.sessionId = new MqttSessionId(); } diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java index dd9c9215a6..e5be5c71a1 100644 --- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java +++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java @@ -38,13 +38,15 @@ import org.thingsboard.server.transport.mqtt.MqttTransportHandler; import java.nio.charset.Charset; import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; /** * Created by ashvayka on 19.01.17. */ -public class GatewayDeviceSessionCtx extends DeviceAwareSessionContext { +public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext { private static final Gson GSON = new Gson(); private static final Charset UTF8 = Charset.forName("UTF-8"); @@ -56,8 +58,8 @@ public class GatewayDeviceSessionCtx extends DeviceAwareSessionContext { private volatile boolean closed; private AtomicInteger msgIdSeq = new AtomicInteger(0); - public GatewayDeviceSessionCtx(GatewaySessionCtx parent, Device device) { - super(parent.getProcessor(), parent.getAuthService(), device); + public GatewayDeviceSessionCtx(GatewaySessionCtx parent, Device device, ConcurrentMap mqttQoSMap) { + super(parent.getProcessor(), parent.getAuthService(), device, mqttQoSMap); this.parent = parent; this.sessionId = new MqttSessionId(); } @@ -195,7 +197,7 @@ public class GatewayDeviceSessionCtx extends DeviceAwareSessionContext { private MqttPublishMessage createMqttPublishMsg(String topic, JsonElement json) { MqttFixedHeader mqttFixedHeader = - new MqttFixedHeader(MqttMessageType.PUBLISH, false, MqttQoS.AT_LEAST_ONCE, false, 0); + new MqttFixedHeader(MqttMessageType.PUBLISH, false, getQoSForTopic(topic), false, 0); MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, msgIdSeq.incrementAndGet()); ByteBuf payload = ALLOCATOR.buffer(); payload.writeBytes(GSON.toJson(json).getBytes(UTF8)); diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java index 69ed17f3b0..98ad6d2c2c 100644 --- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java +++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java @@ -43,6 +43,7 @@ import org.thingsboard.server.transport.mqtt.MqttTransportHandler; import org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor; import java.util.*; +import java.util.concurrent.ConcurrentMap; import java.util.stream.Collectors; import static org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor.validateJsonPayload; @@ -63,6 +64,7 @@ public class GatewaySessionCtx { private final DeviceAuthService authService; private final RelationService relationService; private final Map devices; + private final ConcurrentMap mqttQoSMap; private ChannelHandlerContext channel; public GatewaySessionCtx(SessionMsgProcessor processor, DeviceService deviceService, DeviceAuthService authService, RelationService relationService, DeviceSessionCtx gatewaySessionCtx) { @@ -73,6 +75,7 @@ public class GatewaySessionCtx { this.gateway = gatewaySessionCtx.getDevice(); this.gatewaySessionId = gatewaySessionCtx.getSessionId(); this.devices = new HashMap<>(); + this.mqttQoSMap = gatewaySessionCtx.getMqttQoSMap(); } public void onDeviceConnect(MqttPublishMessage msg) throws AdaptorException { @@ -96,7 +99,7 @@ public class GatewaySessionCtx { relationService.saveRelationAsync(new EntityRelation(gateway.getId(), device.getId(), "Created")); processor.onDeviceAdded(device); } - GatewayDeviceSessionCtx ctx = new GatewayDeviceSessionCtx(this, device); + GatewayDeviceSessionCtx ctx = new GatewayDeviceSessionCtx(this, device, mqttQoSMap); devices.put(deviceName, ctx); log.debug("[{}] Added device [{}] to the gateway session", gatewaySessionId, deviceName); processor.process(new BasicTransportToDeviceSessionActorMsg(device, new BasicAdaptorToSessionActorMsg(ctx, new AttributesSubscribeMsg()))); diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java new file mode 100644 index 0000000000..f085064016 --- /dev/null +++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java @@ -0,0 +1,57 @@ +/** + * 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. + */ +package org.thingsboard.server.transport.mqtt.session; + +import io.netty.handler.codec.mqtt.MqttQoS; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.transport.SessionMsgProcessor; +import org.thingsboard.server.common.transport.auth.DeviceAuthService; +import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; + +import java.util.Map; +import java.util.concurrent.ConcurrentMap; + +/** + * Created by ashvayka on 30.08.18. + */ +public abstract class MqttDeviceAwareSessionContext extends DeviceAwareSessionContext { + + private final ConcurrentMap mqttQoSMap; + + public MqttDeviceAwareSessionContext(SessionMsgProcessor processor, DeviceAuthService authService, ConcurrentMap mqttQoSMap) { + super(processor, authService); + this.mqttQoSMap = mqttQoSMap; + } + + public MqttDeviceAwareSessionContext(SessionMsgProcessor processor, DeviceAuthService authService, Device device, ConcurrentMap mqttQoSMap) { + super(processor, authService, device); + this.mqttQoSMap = mqttQoSMap; + } + + public ConcurrentMap getMqttQoSMap() { + return mqttQoSMap; + } + + public MqttQoS getQoSForTopic(String topic) { + Integer qos = mqttQoSMap.get(topic); + if (qos != null) { + return MqttQoS.valueOf(qos); + } else { + return MqttQoS.AT_LEAST_ONCE; + } + } + +} diff --git a/ui/.stylelintrc b/ui/.stylelintrc index c145c8b0cb..e878729b53 100644 --- a/ui/.stylelintrc +++ b/ui/.stylelintrc @@ -251,7 +251,7 @@ "fill", "stroke" ], - "property-no-vendor-prefix": null, + "property-no-vendor-prefix": true, "rule-empty-line-before": ["always", { "except": ["first-nested"], "ignore": ["after-comment"] @@ -272,7 +272,7 @@ "selector-max-type": 5, "selector-max-universal": 1, "selector-no-qualifying-type": null, - "selector-no-vendor-prefix": null, + "selector-no-vendor-prefix": true, "selector-type-no-unknown": [true, { "ignoreTypes": [ "/^md-/", @@ -287,6 +287,6 @@ "value-list-comma-newline-after": "always-multi-line", "value-list-comma-newline-before": "never-multi-line", "value-list-comma-space-after": "always-single-line", - "value-no-vendor-prefix": null + "value-no-vendor-prefix": true } } diff --git a/ui/package.json b/ui/package.json index 417948e1c9..7874170159 100644 --- a/ui/package.json +++ b/ui/package.json @@ -119,7 +119,7 @@ "ng-annotate-loader": "^0.1.1", "ngtemplate-loader": "^1.3.1", "node-sass": "^4.5.3", - "postcss-loader": "^0.13.0", + "postcss-loader": "^3.0.0", "raw-loader": "^0.5.1", "react-hot-loader": "^3.0.0-beta.6", "sass-loader": "^4.0.2", @@ -145,5 +145,19 @@ "node_modules", "target" ] + }, + "browserslist": [ + "> 0.5%", + "last 2 versions", + "Firefox ESR", + "not ie <= 10", + "not ie_mob <= 10", + "not bb <= 10", + "not op_mob <= 12.1" + ], + "postcss": { + "plugins": { + "autoprefixer": true + } } } diff --git a/ui/src/app/components/dashboard.scss b/ui/src/app/components/dashboard.scss index e0a9afd54e..b4a696a85c 100644 --- a/ui/src/app/components/dashboard.scss +++ b/ui/src/app/components/dashboard.scss @@ -22,7 +22,7 @@ div.tb-widget { overflow: hidden; outline: none; - @include transition(all .2s ease-in-out); + transition: all .2s ease-in-out; .tb-widget-title { max-height: 60px; @@ -99,7 +99,7 @@ md-content.tb-dashboard-content { outline: none; .gridster-item { - @include transition(none); + transition: none; } } diff --git a/ui/src/app/components/grid.scss b/ui/src/app/components/grid.scss index 43138481a9..50e110bb0c 100644 --- a/ui/src/app/components/grid.scss +++ b/ui/src/app/components/grid.scss @@ -20,7 +20,7 @@ } .tb-card-item { - @include transition(all .2s ease-in-out); + transition: all .2s ease-in-out; md-card-content { max-height: 53px; @@ -46,7 +46,7 @@ .tb-current-item { opacity: .5; - @include transform(scale(1.05)); + transform: scale(1.05); } #tb-vertical-container { diff --git a/ui/src/app/components/menu-link.scss b/ui/src/app/components/menu-link.scss index 299797ce6b..be274d30df 100644 --- a/ui/src/app/components/menu-link.scss +++ b/ui/src/app/components/menu-link.scss @@ -16,7 +16,7 @@ @import "~compass-sass-mixins/lib/compass"; .md-button-toggle .md-toggle-icon.tb-toggled { - @include transform(rotateZ(180deg)); + transform: rotateZ(180deg); } .tb-menu-toggle-list.ng-hide { @@ -28,7 +28,7 @@ z-index: 1; overflow: hidden; - @include transition(.75s cubic-bezier(.35, 0, .25, 1)); + transition: .75s cubic-bezier(.35, 0, .25, 1); - @include transition-property(height); + transition-property: height; } diff --git a/ui/src/app/components/react/json-form.scss b/ui/src/app/components/react/json-form.scss index cca9d07a07..d324abef93 100644 --- a/ui/src/app/components/react/json-form.scss +++ b/ui/src/app/components/react/json-form.scss @@ -28,7 +28,7 @@ $input-label-float-scale: .75 !default; line-height: 12px; color: rgb(244, 67, 54); - @include transition(all 450ms cubic-bezier(.23, 1, .32, 1) 0ms); + transition: all 450ms cubic-bezier(.23, 1, .32, 1) 0ms; } .tb-container { @@ -77,14 +77,13 @@ label.tb-label { bottom: 100%; left: 0; color: rgba(0, 0, 0, .54); + + transition: transform $swift-ease-out-timing-function $swift-ease-out-duration, width $swift-ease-out-timing-function $swift-ease-out-duration; + + transform: translate3d(0, $input-label-float-offset, 0) scale($input-label-float-scale); transform-origin: left top; -webkit-font-smoothing: antialiased; - @include transform(translate3d(0, $input-label-float-offset, 0) scale($input-label-float-scale)); - - @include transition(transform $swift-ease-out-timing-function $swift-ease-out-duration, - width $swift-ease-out-timing-function $swift-ease-out-duration); - &.tb-focused { color: rgb(96, 125, 139); } diff --git a/ui/src/app/components/side-menu.scss b/ui/src/app/components/side-menu.scss index 46da8db448..f6efcb963b 100644 --- a/ui/src/app/components/side-menu.scss +++ b/ui/src/app/components/side-menu.scss @@ -53,7 +53,7 @@ margin: auto 0 auto auto; background-size: 100% auto; - @include transition(transform .3s, ease-in-out); + transition: transform .3s, ease-in-out; } .tb-side-menu .md-button { 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 88a37a876e..81103227ba 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 @@ -244,13 +244,18 @@ function ManageWidgetActionsController($rootScope, $scope, $document, $mdDialog, vm.widgetActions[actionSourceId] = targetActions; } if (prevActionId) { - var index = getActionIndex(prevActionId, vm.allActions); - if (index > -1) { - vm.allActions[index] = action; + const indexInTarget = getActionIndex(prevActionId, targetActions); + const indexInAllActions = getActionIndex(prevActionId, vm.allActions); + if (indexInTarget > -1) { + targetActions[indexInTarget] = widgetAction; + } else if (indexInAllActions > -1) { + const prevActionSourceId = vm.allActions[indexInAllActions].actionSourceId; + const index = getActionIndex(prevActionId,vm.widgetActions[prevActionSourceId]); + vm.widgetActions[prevActionSourceId].splice(index,1); + targetActions.push(widgetAction); } - index = getActionIndex(prevActionId, targetActions); - if (index > -1) { - targetActions[index] = widgetAction; + if (indexInAllActions > -1) { + vm.allActions[indexInAllActions] = action; } } else { vm.allActions.push(action); diff --git a/ui/src/app/dashboard/dashboard-card.scss b/ui/src/app/dashboard/dashboard-card.scss index 8e6626c728..111d67a4de 100644 --- a/ui/src/app/dashboard/dashboard-card.scss +++ b/ui/src/app/dashboard/dashboard-card.scss @@ -16,12 +16,12 @@ .tb-dashboard-assigned-customers { display: block; - display: -webkit-box; + display: -webkit-box; /* stylelint-disable-line value-no-vendor-prefix */ height: 34px; margin-bottom: 4px; overflow: hidden; text-overflow: ellipsis; -webkit-line-clamp: 2; - -webkit-box-orient: vertical; + -webkit-box-orient: vertical; /* stylelint-disable-line property-no-vendor-prefix */ } diff --git a/ui/src/app/dashboard/dashboard-toolbar.scss b/ui/src/app/dashboard/dashboard-toolbar.scss index 80fd876ad3..c2476341e8 100644 --- a/ui/src/app/dashboard/dashboard-toolbar.scss +++ b/ui/src/app/dashboard/dashboard-toolbar.scss @@ -31,7 +31,7 @@ tb-dashboard-toolbar { &.md-fab { opacity: 1; - @include transition(opacity .3s cubic-bezier(.55,0,.55,.2)); + transition: opacity .3s cubic-bezier(.55, 0, .55, .2); .md-fab-toolbar-background { background-color: $primary-default !important; @@ -50,7 +50,7 @@ tb-dashboard-toolbar { line-height: 36px; opacity: .5; - @include transition(opacity .3s cubic-bezier(.55,0,.55,.2) .2s); + transition: opacity .3s cubic-bezier(.55, 0, .55, .2) .2s; md-icon { position: absolute; diff --git a/ui/src/app/dashboard/dashboard.scss b/ui/src/app/dashboard/dashboard.scss index 00a0653de7..715c4daafe 100644 --- a/ui/src/app/dashboard/dashboard.scss +++ b/ui/src/app/dashboard/dashboard.scss @@ -75,13 +75,13 @@ section.tb-dashboard-toolbar { &.tb-dashboard-toolbar-opened { right: 0; - // @include transition(right .3s cubic-bezier(.55,0,.55,.2)); + // transition: right .3s cubic-bezier(.55, 0, .55, .2); } &.tb-dashboard-toolbar-closed { right: 18px; - @include transition(right .3s cubic-bezier(.55,0,.55,.2) .2s); + transition: right .3s cubic-bezier(.55, 0, .55, .2) .2s; } } @@ -102,14 +102,14 @@ section.tb-dashboard-toolbar { margin-top: $toolbar-height; } - @include transition(margin-top .3s cubic-bezier(.55,0,.55,.2)); + transition: margin-top .3s cubic-bezier(.55, 0, .55, .2); } } &.tb-dashboard-toolbar-closed { margin-top: 0; - @include transition(margin-top .3s cubic-bezier(.55,0,.55,.2) .2s); + transition: margin-top .3s cubic-bezier(.55, 0, .55, .2) .2s; } .tb-dashboard-layouts { @@ -133,7 +133,7 @@ section.tb-powered-by-footer { position: absolute; right: 25px; bottom: 5px; - z-index: 3; + z-index: 30; pointer-events: none; span { diff --git a/ui/src/app/dashboard/dashboard.tpl.html b/ui/src/app/dashboard/dashboard.tpl.html index 829f174fe7..79653a42c5 100644 --- a/ui/src/app/dashboard/dashboard.tpl.html +++ b/ui/src/app/dashboard/dashboard.tpl.html @@ -146,7 +146,7 @@ ng-style="{minWidth: vm.rightLayoutWidth(), maxWidth: vm.rightLayoutWidth(), height: vm.rightLayoutHeight(), - zIndex: 12}" + zIndex: 25}" md-component-id="right-dashboard-layout" aria-label="Right dashboard layout" md-is-open="vm.rightLayoutOpened" diff --git a/ui/src/app/layout/home.scss b/ui/src/app/layout/home.scss index 48c1553877..43b83297a9 100644 --- a/ui/src/app/layout/home.scss +++ b/ui/src/app/layout/home.scss @@ -42,7 +42,7 @@ border: none; opacity: .75; - @include transition(opacity .35s); + transition: opacity .35s; } a:hover, diff --git a/ui/src/app/rulechain/rulechain.scss b/ui/src/app/rulechain/rulechain.scss index 8f773005fa..d6d264d5ef 100644 --- a/ui/src/app/rulechain/rulechain.scss +++ b/ui/src/app/rulechain/rulechain.scss @@ -84,9 +84,6 @@ .tb-panel-title { min-width: 150px; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; user-select: none; } @@ -163,10 +160,6 @@ .fc-canvas { min-width: 100%; min-height: 100%; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; user-select: none; outline: none; -webkit-touch-callout: none; @@ -441,13 +434,7 @@ } .fc-noselect { - -webkit-touch-callout: none; /* iOS Safari */ - -webkit-user-select: none; /* Safari */ - -khtml-user-select: none; /* Konqueror HTML */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* Internet Explorer/Edge */ - user-select: none; /* Non-prefixed version, currently - supported by Chrome and Opera */ + user-select: none; } .fc-edge-label { @@ -495,7 +482,6 @@ font-weight: 600; text-align: center; white-space: nowrap; - -webkit-transform: translate(-50%, -50%); transform: translate(-50%, -50%); span { diff --git a/ui/src/app/rulechain/script/node-script-test.scss b/ui/src/app/rulechain/script/node-script-test.scss index 1dd174dd87..e68691d062 100644 --- a/ui/src/app/rulechain/script/node-script-test.scss +++ b/ui/src/app/rulechain/script/node-script-test.scss @@ -29,7 +29,7 @@ md-dialog.tb-node-script-test-dialog { } .tb-split { - @include box-sizing(border-box); + box-sizing: border-box; overflow-x: hidden; overflow-y: auto; } diff --git a/ui/src/app/widget/lib/entities-table-widget.js b/ui/src/app/widget/lib/entities-table-widget.js index 620f313749..6cfe7a03fc 100644 --- a/ui/src/app/widget/lib/entities-table-widget.js +++ b/ui/src/app/widget/lib/entities-table-widget.js @@ -371,7 +371,9 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $mdP content = strContent; } } else { - content = defaultContent(key, value); + var decimals = (contentInfo.decimals || contentInfo.decimals === 0) ? contentInfo.decimals : vm.widgetConfig.decimals; + var units = contentInfo.units || vm.widgetConfig.units; + content = vm.ctx.utils.formatValue(value, decimals, units, true); } return content; } else { @@ -379,14 +381,6 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $mdP } } - function defaultContent(key, value) { - if (angular.isDefined(value)) { - return value; - } else { - return ''; - } - } - function defaultStyle(/*key, value*/) { return {}; } @@ -542,7 +536,9 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $mdP vm.contentsInfo[dataKey.label] = { useCellContentFunction: useCellContentFunction, - cellContentFunction: cellContentFunction + cellContentFunction: cellContentFunction, + units: dataKey.units, + decimals: dataKey.decimals }; var columnWidth = angular.isDefined(keySettings.columnWidth) ? keySettings.columnWidth : '0px'; diff --git a/ui/src/app/widget/lib/flot-widget.js b/ui/src/app/widget/lib/flot-widget.js index 1f363d627c..acaaf10791 100644 --- a/ui/src/app/widget/lib/flot-widget.js +++ b/ui/src/app/widget/lib/flot-widget.js @@ -333,6 +333,7 @@ export default class TbFlot { lineWidth: 0, fill: 0.9 } + ctx.defaultBarWidth = settings.defaultBarWidth || 600; } if (this.chartType === 'state') { @@ -476,7 +477,11 @@ export default class TbFlot { this.options.yaxes = angular.copy(this.yaxes); if (this.chartType === 'line' || this.chartType === 'bar' || this.chartType === 'state') { if (this.chartType === 'bar') { - this.options.series.bars.barWidth = this.subscription.timeWindow.interval * 0.6; + if (this.subscription.timeWindowConfig.aggregation && this.subscription.timeWindowConfig.aggregation.type === 'NONE') { + this.options.series.bars.barWidth = this.ctx.defaultBarWidth; + } else { + this.options.series.bars.barWidth = this.subscription.timeWindow.interval * 0.6; + } } this.options.xaxis.min = this.subscription.timeWindow.minTime; this.options.xaxis.max = this.subscription.timeWindow.maxTime; @@ -594,7 +599,11 @@ export default class TbFlot { this.options.xaxis.min = this.subscription.timeWindow.minTime; this.options.xaxis.max = this.subscription.timeWindow.maxTime; if (this.chartType === 'bar') { - this.options.series.bars.barWidth = this.subscription.timeWindow.interval * 0.6; + if (this.subscription.timeWindowConfig.aggregation && this.subscription.timeWindowConfig.aggregation.type === 'NONE') { + this.options.series.bars.barWidth = this.ctx.defaultBarWidth; + } else { + this.options.series.bars.barWidth = this.subscription.timeWindow.interval * 0.6; + } } if (axisVisibilityChanged) { @@ -603,7 +612,11 @@ export default class TbFlot { this.ctx.plot.getOptions().xaxes[0].min = this.subscription.timeWindow.minTime; this.ctx.plot.getOptions().xaxes[0].max = this.subscription.timeWindow.maxTime; if (this.chartType === 'bar') { - this.ctx.plot.getOptions().series.bars.barWidth = this.subscription.timeWindow.interval * 0.6; + if (this.subscription.timeWindowConfig.aggregation && this.subscription.timeWindowConfig.aggregation.type === 'NONE') { + this.ctx.plot.getOptions().series.bars.barWidth = this.ctx.defaultBarWidth; + } else { + this.ctx.plot.getOptions().series.bars.barWidth = this.subscription.timeWindow.interval * 0.6; + } } this.updateData(); } @@ -810,238 +823,257 @@ export default class TbFlot { } } - static get settingsSchema() { - return { + static settingsSchema(chartType) { + + var schema = { "schema": { "type": "object", "title": "Settings", "properties": { - "stack": { - "title": "Stacking", - "type": "boolean", - "default": false - }, - "smoothLines": { - "title": "Display smooth (curved) lines", - "type": "boolean", - "default": false - }, - "shadowSize": { - "title": "Shadow size", - "type": "number", - "default": 4 - }, - "fontColor": { - "title": "Font color", + } + } + }; + + var properties = schema["schema"]["properties"]; + properties["stack"] = { + "title": "Stacking", + "type": "boolean", + "default": false + }; + if (chartType === 'graph') { + properties["smoothLines"] = { + "title": "Display smooth (curved) lines", + "type": "boolean", + "default": false + }; + } + if (chartType === 'bar') { + properties["defaultBarWidth"] = { + "title": "Default bar width for non-aggregated data (milliseconds)", + "type": "number", + "default": 600 + }; + } + properties["shadowSize"] = { + "title": "Shadow size", + "type": "number", + "default": 4 + }; + properties["fontColor"] = { + "title": "Font color", + "type": "string", + "default": "#545454" + }; + properties["fontSize"] = { + "title": "Font size", + "type": "number", + "default": 10 + }; + properties["tooltipIndividual"] = { + "title": "Hover individual points", + "type": "boolean", + "default": false + }; + properties["tooltipCumulative"] = { + "title": "Show cumulative values in stacking mode", + "type": "boolean", + "default": false + }; + properties["tooltipValueFormatter"] = { + "title": "Tooltip value format function, f(value)", + "type": "string", + "default": "" + }; + + properties["grid"] = { + "title": "Grid settings", + "type": "object", + "properties": { + "color": { + "title": "Primary color", "type": "string", "default": "#545454" - }, - "fontSize": { - "title": "Font size", - "type": "number", - "default": 10 - }, - "tooltipIndividual": { - "title": "Hover individual points", - "type": "boolean", - "default": false - }, - "tooltipCumulative": { - "title": "Show cumulative values in stacking mode", - "type": "boolean", - "default": false - }, - "tooltipValueFormatter": { - "title": "Tooltip value format function, f(value)", - "type": "string", - "default": "" - }, - "grid": { - "title": "Grid settings", - "type": "object", - "properties": { - "color": { - "title": "Primary color", - "type": "string", - "default": "#545454" - }, - "backgroundColor": { - "title": "Background color", - "type": "string", - "default": null - }, - "tickColor": { - "title": "Ticks color", - "type": "string", - "default": "#DDDDDD" - }, - "outlineWidth": { - "title": "Grid outline/border width (px)", - "type": "number", - "default": 1 - }, - "verticalLines": { - "title": "Show vertical lines", - "type": "boolean", - "default": true - }, - "horizontalLines": { - "title": "Show horizontal lines", - "type": "boolean", - "default": true - } - } - }, - "xaxis": { - "title": "X axis settings", - "type": "object", - "properties": { - "showLabels": { - "title": "Show labels", - "type": "boolean", - "default": true - }, - "title": { - "title": "Axis title", - "type": "string", - "default": null - }, - "titleAngle": { - "title": "Axis title's angle in degrees", - "type": "number", - "default": 0 - }, - "color": { - "title": "Ticks color", - "type": "string", - "default": null - } - } - }, - "yaxis": { - "title": "Y axis settings", - "type": "object", - "properties": { - "min": { - "title": "Minimum value on the scale", - "type": "number", - "default": null - }, - "max": { - "title": "Maximum value on the scale", - "type": "number", - "default": null - }, - "showLabels": { - "title": "Show labels", - "type": "boolean", - "default": true - }, - "title": { - "title": "Axis title", - "type": "string", - "default": null - }, - "titleAngle": { - "title": "Axis title's angle in degrees", - "type": "number", - "default": 0 - }, - "color": { - "title": "Ticks color", - "type": "string", - "default": null - }, - "ticksFormatter": { - "title": "Ticks formatter function, f(value)", - "type": "string", - "default": "" - }, - "tickDecimals": { - "title": "The number of decimals to display", - "type": "number", - "default": 0 - }, - "tickSize": { - "title": "Step size between ticks", - "type": "number", - "default": null - } - } - } }, - "required": [] - }, - "form": [ - "stack", - "smoothLines", - "shadowSize", + "backgroundColor": { + "title": "Background color", + "type": "string", + "default": null + }, + "tickColor": { + "title": "Ticks color", + "type": "string", + "default": "#DDDDDD" + }, + "outlineWidth": { + "title": "Grid outline/border width (px)", + "type": "number", + "default": 1 + }, + "verticalLines": { + "title": "Show vertical lines", + "type": "boolean", + "default": true + }, + "horizontalLines": { + "title": "Show horizontal lines", + "type": "boolean", + "default": true + } + } + }; + + properties["xaxis"] = { + "title": "X axis settings", + "type": "object", + "properties": { + "showLabels": { + "title": "Show labels", + "type": "boolean", + "default": true + }, + "title": { + "title": "Axis title", + "type": "string", + "default": null + }, + "titleAngle": { + "title": "Axis title's angle in degrees", + "type": "number", + "default": 0 + }, + "color": { + "title": "Ticks color", + "type": "string", + "default": null + } + } + }; + + properties["yaxis"] = { + "title": "Y axis settings", + "type": "object", + "properties": { + "min": { + "title": "Minimum value on the scale", + "type": "number", + "default": null + }, + "max": { + "title": "Maximum value on the scale", + "type": "number", + "default": null + }, + "showLabels": { + "title": "Show labels", + "type": "boolean", + "default": true + }, + "title": { + "title": "Axis title", + "type": "string", + "default": null + }, + "titleAngle": { + "title": "Axis title's angle in degrees", + "type": "number", + "default": 0 + }, + "color": { + "title": "Ticks color", + "type": "string", + "default": null + }, + "ticksFormatter": { + "title": "Ticks formatter function, f(value)", + "type": "string", + "default": "" + }, + "tickDecimals": { + "title": "The number of decimals to display", + "type": "number", + "default": 0 + }, + "tickSize": { + "title": "Step size between ticks", + "type": "number", + "default": null + } + } + }; + + schema["schema"]["required"] = []; + schema["form"] = ["stack"]; + if (chartType === 'graph') { + schema["form"].push("smoothLines"); + } + if (chartType === 'bar') { + schema["form"].push("defaultBarWidth"); + } + schema["form"].push("shadowSize"); + schema["form"].push({ + "key": "fontColor", + "type": "color" + }); + schema["form"].push("fontSize"); + schema["form"].push("tooltipIndividual"); + schema["form"].push("tooltipCumulative"); + schema["form"].push({ + "key": "tooltipValueFormatter", + "type": "javascript" + }); + schema["form"].push({ + "key": "grid", + "items": [ { - "key": "fontColor", + "key": "grid.color", "type": "color" }, - "fontSize", - "tooltipIndividual", - "tooltipCumulative", { - "key": "tooltipValueFormatter", - "type": "javascript" + "key": "grid.backgroundColor", + "type": "color" }, { - "key": "grid", - "items": [ - { - "key": "grid.color", - "type": "color" - }, - { - "key": "grid.backgroundColor", - "type": "color" - }, - { - "key": "grid.tickColor", - "type": "color" - }, - "grid.outlineWidth", - "grid.verticalLines", - "grid.horizontalLines" - ] + "key": "grid.tickColor", + "type": "color" }, - { - "key": "xaxis", - "items": [ - "xaxis.showLabels", - "xaxis.title", - "xaxis.titleAngle", - { - "key": "xaxis.color", - "type": "color" - } - ] - }, - { - "key": "yaxis", - "items": [ - "yaxis.min", - "yaxis.max", - "yaxis.tickDecimals", - "yaxis.tickSize", - "yaxis.showLabels", - "yaxis.title", - "yaxis.titleAngle", - { - "key": "yaxis.color", - "type": "color" - }, - { - "key": "yaxis.ticksFormatter", - "type": "javascript" - } - ] - } - + "grid.outlineWidth", + "grid.verticalLines", + "grid.horizontalLines" ] - } + }); + schema["form"].push({ + "key": "xaxis", + "items": [ + "xaxis.showLabels", + "xaxis.title", + "xaxis.titleAngle", + { + "key": "xaxis.color", + "type": "color" + } + ] + }); + schema["form"].push({ + "key": "yaxis", + "items": [ + "yaxis.min", + "yaxis.max", + "yaxis.tickDecimals", + "yaxis.tickSize", + "yaxis.showLabels", + "yaxis.title", + "yaxis.titleAngle", + { + "key": "yaxis.color", + "type": "color" + }, + { + "key": "yaxis.ticksFormatter", + "type": "javascript" + } + ] + }); + return schema; } static get pieDatakeySettingsSchema() { diff --git a/ui/src/app/widget/lib/rpc/knob.scss b/ui/src/app/widget/lib/rpc/knob.scss index bf2d3e019f..722275fc00 100644 --- a/ui/src/app/widget/lib/rpc/knob.scss +++ b/ui/src/app/widget/lib/rpc/knob.scss @@ -37,8 +37,6 @@ $background-color: #e6e7e8 !default; position: relative; &[draggable] { - -moz-user-select: none; - -webkit-user-select: none; user-select: none; } diff --git a/ui/src/app/widget/lib/rpc/led-indicator.scss b/ui/src/app/widget/lib/rpc/led-indicator.scss index b1da5c6a40..3083e7274c 100644 --- a/ui/src/app/widget/lib/rpc/led-indicator.scss +++ b/ui/src/app/widget/lib/rpc/led-indicator.scss @@ -60,19 +60,11 @@ $background-color: #e6e7e8 !default; .led { position: relative; cursor: pointer; - background-image: -owg-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, .25)); - background-image: -webkit-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, .25)); - background-image: -moz-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, .25)); - background-image: -o-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, .25)); background-image: radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, .25)); border-radius: 50%; transition: background-color .5s, box-shadow .5s; &.disabled { - background-image: -owg-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, .5), rgba(0, 0, 0, .1)); - background-image: -webkit-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, .5), rgba(0, 0, 0, .1)); - background-image: -moz-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, .5), rgba(0, 0, 0, .1)); - background-image: -o-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, .5), rgba(0, 0, 0, .1)); background-image: radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, .5), rgba(0, 0, 0, .1)); } } diff --git a/ui/src/app/widget/lib/rpc/round-switch.scss b/ui/src/app/widget/lib/rpc/round-switch.scss index 2e37f632a8..c89b26bd45 100644 --- a/ui/src/app/widget/lib/rpc/round-switch.scss +++ b/ui/src/app/widget/lib/rpc/round-switch.scss @@ -59,6 +59,8 @@ $background-color: #e6e7e8 !default; .switch { position: relative; + + box-sizing: border-box; width: 260px; min-width: 260px; height: 260px; @@ -69,21 +71,14 @@ $background-color: #e6e7e8 !default; color: #424242; cursor: pointer; - -pie-background: -pie-linear-gradient(270deg, #bbb, #ddd); background: #ddd; - background: -owg-linear-gradient(270deg, #bbb, #ddd); - background: -webkit-linear-gradient(270deg, #bbb, #ddd); - background: -moz-linear-gradient(270deg, #bbb, #ddd); - background: -o-linear-gradient(270deg, #bbb, #ddd); background: linear-gradient(180deg, #bbb, #ddd); border-radius: 130px; - @include box-sizing(border-box); - - @include box-shadow( - 0 0 0 8px rgba(0,0,0,.1) - ,0 0 3px 1px rgba(0,0,0,.1) - ,inset 0 8px 3px -8px rgba(255,255,255,.4)); + box-shadow: + 0 0 0 8px rgba(0, 0, 0, .1), + 0 0 3px 1px rgba(0, 0, 0, .1), + inset 0 8px 3px -8px rgba(255, 255, 255, .4); input { display: none; @@ -95,7 +90,7 @@ $background-color: #e6e7e8 !default; width: 100%; text-align: center; - @include text-shadow(1px 1px 4px #4a4a4a); + text-shadow: 1px 1px 4px #4a4a4a; } .on { @@ -103,15 +98,15 @@ $background-color: #e6e7e8 !default; font-family: sans-serif; color: #444; - @include transition(all .1s); + transition: all .1s; } .off { bottom: 5px; - @include transition(all .1s); + transition: all .1s; - @include transform(scaleY(.85)); + transform: scaleY(.85); } .but { @@ -125,90 +120,82 @@ $background-color: #e6e7e8 !default; border-bottom-width: 0; border-radius: 400px 400px 400px 400px / 400px 400px 300px 300px; - @include box-shadow(inset 8px 6px 5px -7px #a2a2a2, - inset -8px 6px 5px -7px #a2a2a2, - inset 0 -3px 2px -2px rgba(200, 200, 200, .5), - 0 3px 3px -2px #fff, - inset 0 -230px 60px -200px rgba(255, 255, 255, .2), - inset 0 220px 40px -200px rgba(0, 0, 0, .3)); + box-shadow: + inset 8px 6px 5px -7px #a2a2a2, + inset -8px 6px 5px -7px #a2a2a2, + inset 0 -3px 2px -2px rgba(200, 200, 200, .5), + 0 3px 3px -2px #fff, + inset 0 -230px 60px -200px rgba(255, 255, 255, .2), + inset 0 220px 40px -200px rgba(0, 0, 0, .3); - @include transition(all .2s); + transition: all .2s; } .back { + + box-sizing: border-box; width: 210px; height: 210px; padding: 4px 4px; cursor: pointer; background-color: #888787; - background-image: -owg-linear-gradient(0deg, transparent 30%, transparent 70%), -owg-linear-gradient(90deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, .2) 50%, rgba(150, 150, 150, 0) 70%); - background-image: -webkit-linear-gradient(0deg, transparent 30%, transparent 70%), -webkit-linear-gradient(90deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, .2) 50%, rgba(150, 150, 150, 0) 70%); - background-image: -moz-linear-gradient(0deg, transparent 30%, transparent 70%), -moz-linear-gradient(90deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, .2) 50%, rgba(150, 150, 150, 0) 70%); - background-image: -o-linear-gradient(0deg, transparent 30%, transparent 70%), -o-linear-gradient(90deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, .2) 50%, rgba(150, 150, 150, 0) 70%); background-image: linear-gradient(-90deg, transparent 30%, transparent 70%), linear-gradient(0deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, .2) 50%, rgba(150, 150, 150, 0) 70%); border-radius: 105px; - @include box-shadow(30px 30px 30px -20px rgba(58, 58, 58, .3), - -30px 30px 30px -20px rgba(58, 58, 58, .3), - 0 30px 30px 0 rgba(16, 16, 16, .3), - inset 0 -1px 0 0 #484848); + box-shadow: + 30px 30px 30px -20px rgba(58, 58, 58, .3), + -30px 30px 30px -20px rgba(58, 58, 58, .3), + 0 30px 30px 0 rgba(16, 16, 16, .3), + inset 0 -1px 0 0 #484848; - @include box-sizing(border-box); - - @include transition(all .2s); + transition: all .2s; } input:checked + .back .on, input:checked + .back .off{ - @include text-shadow(1px 1px 4px #4a4a4a); + text-shadow: 1px 1px 4px #4a4a4a; } input:checked + .back .on{ top: 10px; color: #4c4c4c; - @include transform(scaleY(.85)); + transform: scaleY(.85); } input:checked + .back .off{ bottom: 5px; color: #444; - @include transform(scaleY(1)); + transform: scaleY(1); } input:checked + .back .but{ margin-top: 20px; background: #dcdcdc; - background-image: -owg-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, .3), transparent); - background-image: -webkit-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, .3), transparent); - background-image: -moz-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, .3), transparent); - background-image: -o-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, .3), transparent); background-image: radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, .3), transparent); border-radius: 400px 400px 400px 400px / 300px 300px 400px 400px; - @include box-shadow(inset 8px -4px 5px -7px #a9a9a9, - inset -8px -4px 5px -7px #808080, - 0 -3px 8px -4px rgba(50, 50, 50, .4), - inset 0 3px 4px -2px #9c9c9c, - inset 0 280px 40px -200px rgba(0, 0, 0, .2), - inset 0 -200px 40px -200px rgba(180, 180, 180, .2)); + box-shadow: + inset 8px -4px 5px -7px #a9a9a9, + inset -8px -4px 5px -7px #808080, + 0 -3px 8px -4px rgba(50, 50, 50, .4), + inset 0 3px 4px -2px #9c9c9c, + inset 0 280px 40px -200px rgba(0, 0, 0, .2), + inset 0 -200px 40px -200px rgba(180, 180, 180, .2); } input:checked + .back{ padding: 2px 4px; - background-image: -owg-linear-gradient(90deg, #868686 30%, transparent 70%), -owg-linear-gradient(180deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, .74) 50%, rgba(105, 105, 105, 0) 100%); - background-image: -webkit-linear-gradient(90deg, #868686 30%, transparent 70%), -webkit-linear-gradient(180deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, .74) 50%, rgba(105, 105, 105, 0) 100%); - background-image: -moz-linear-gradient(90deg, #868686 30%, transparent 70%), -moz-linear-gradient(180deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, .74) 50%, rgba(105, 105, 105, 0) 100%); - background-image: -o-linear-gradient(90deg, #868686 30%, transparent 70%), -o-linear-gradient(180deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, .74) 50%, rgba(105, 105, 105, 0) 100%); background-image: linear-gradient(0deg, #868686 30%, transparent 70%), linear-gradient(90deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, .74) 50%, rgba(105, 105, 105, 0) 100%); - @include box-shadow(30px 30px 30px -20px rgba(49, 49, 49, .1), - -30px 30px 30px -20px rgba(111, 111, 111, .1), - 0 30px 30px 0 rgba(0, 0, 0, .2), - inset 0 1px 2px 0 rgba(167, 167, 167, .6)); + box-shadow: + 30px 30px 30px -20px rgba(49, 49, 49, .1), + -30px 30px 30px -20px rgba(111, 111, 111, .1), + 0 30px 30px 0 rgba(0, 0, 0, .2), + inset 0 1px 2px 0 rgba(167, 167, 167, .6); } } } diff --git a/ui/src/app/widget/lib/timeseries-table-widget.js b/ui/src/app/widget/lib/timeseries-table-widget.js index bca79565af..d025c1e397 100644 --- a/ui/src/app/widget/lib/timeseries-table-widget.js +++ b/ui/src/app/widget/lib/timeseries-table-widget.js @@ -217,7 +217,9 @@ function TimeseriesTableWidgetController($element, $scope, $filter, $timeout) { content = strContent; } } else { - content = vm.ctx.utils.formatValue(value, contentInfo.decimals, contentInfo.units); + var decimals = (contentInfo.decimals || contentInfo.decimals === 0) ? contentInfo.decimals : vm.widgetConfig.decimals; + var units = contentInfo.units || vm.widgetConfig.units; + content = vm.ctx.utils.formatValue(value, decimals, units, true); } return content; } diff --git a/ui/src/app/widget/widget-editor.scss b/ui/src/app/widget/widget-editor.scss index cd374e37aa..0eb37afee0 100644 --- a/ui/src/app/widget/widget-editor.scss +++ b/ui/src/app/widget/widget-editor.scss @@ -19,7 +19,7 @@ $edit-toolbar-height: 40px !default; .tb-editor { .tb-split { - @include box-sizing(border-box); + box-sizing: border-box; overflow-x: hidden; overflow-y: auto; } diff --git a/ui/src/scss/animations.scss b/ui/src/scss/animations.scss index 4ecfc34743..61b9bac890 100644 --- a/ui/src/scss/animations.scss +++ b/ui/src/scss/animations.scss @@ -15,34 +15,34 @@ */ @import "~compass-sass-mixins/lib/animate"; -@include keyframes(tbMoveFromTopFade) { +@keyframes tbMoveFromTopFade { from { opacity: 0; - @include transform(translate(0, -100%)); + transform: translate(0, -100%); } } -@include keyframes(tbMoveToTopFade) { +@keyframes tbMoveToTopFade { to { opacity: 0; - @include transform(translate(0, -100%)); + transform: translate(0, -100%); } } -@include keyframes(tbMoveFromBottomFade) { +@keyframes tbMoveFromBottomFade { from { opacity: 0; - @include transform(translate(0, 100%)); + transform: translate(0, 100%); } } -@include keyframes(tbMoveToBottomFade) { +@keyframes tbMoveToBottomFade { to { opacity: 0; - @include transform(translate(0, 150%)); + transform: translate(0, 150%); } } diff --git a/ui/src/scss/main.scss b/ui/src/scss/main.scss index 27c2d3b245..c1a437a848 100644 --- a/ui/src/scss/main.scss +++ b/ui/src/scss/main.scss @@ -42,7 +42,7 @@ textarea { word-wrap: normal; white-space: nowrap; direction: ltr; - -webkit-font-feature-settings: "liga"; + -webkit-font-feature-settings: "liga"; /* stylelint-disable-line property-no-vendor-prefix */ } a { @@ -51,7 +51,7 @@ a { text-decoration: none; border-bottom: 1px solid rgba(64, 84, 178, .25); - @include transition(border-bottom .35s); + transition: border-bottom .35s; } a:hover, @@ -258,13 +258,7 @@ label { } .tb-noselect { - -webkit-touch-callout: none; /* iOS Safari */ - -webkit-user-select: none; /* Safari */ - -khtml-user-select: none; /* Konqueror HTML */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* Internet Explorer/Edge */ - user-select: none; /* Non-prefixed version, currently - supported by Chrome and Opera */ + user-select: none; } .tb-readonly-label { @@ -556,7 +550,7 @@ $previewSize: 100px !default; } .tb-error-message.ng-animate { - @include transition(all .3s cubic-bezier(.55, 0, .55, .2)); + transition: all .3s cubic-bezier(.55, 0, .55, .2); } .tb-error-message.ng-enter-prepare, @@ -652,13 +646,13 @@ section.tb-top-header-buttons { } .tb-header-buttons .tb-btn-header { - @include animation(tbMoveFromTopFade .3s ease both); position: relative !important; display: inline-block !important; + animation: tbMoveFromTopFade .3s ease both; } .tb-header-buttons .tb-btn-header.ng-hide { - @include animation(tbMoveToTopFade .3s ease both); + animation: tbMoveToTopFade .3s ease both; } /*********************** @@ -669,24 +663,24 @@ section.tb-footer-buttons { position: fixed; right: 20px; bottom: 20px; - z-index: 13; + z-index: 30; pointer-events: none; } .tb-footer-buttons .tb-btn-footer { - @include animation(tbMoveFromBottomFade .3s ease both); position: relative !important; display: inline-block !important; + animation: tbMoveFromBottomFade .3s ease both; } .tb-footer-buttons .tb-btn-footer.ng-hide { - @include animation(tbMoveToBottomFade .3s ease both); + animation: tbMoveToBottomFade .3s ease both; } ._md-toast-open-bottom .tb-footer-buttons { - @include transition(all .4s cubic-bezier(.25, .8, .25, 1)); + transition: all .4s cubic-bezier(.25, .8, .25, 1); - @include transform(translate3d(0, -42px, 0)); + transform: translate3d(0, -42px, 0); } /*********************** diff --git a/ui/src/scss/mixins.scss b/ui/src/scss/mixins.scss index a7545386aa..4d2c6045d6 100644 --- a/ui/src/scss/mixins.scss +++ b/ui/src/scss/mixins.scss @@ -15,6 +15,7 @@ */ @import "~compass-sass-mixins/lib/compass"; +/* stylelint-disable selector-no-vendor-prefix */ @mixin input-placeholder { // replaces compass/css/user-interface/input-placeholder() @@ -36,6 +37,7 @@ @content; } } +/* stylelint-enable selector-no-vendor-prefix */ @mixin line-clamp($numLines: 1, $lineHeight: 1.412) { position: relative;