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 2ecd90be28..9c97a03084 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('graph');\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, 'graph');\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,7 +147,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, '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", + "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, 'bar');\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},\"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('graph');\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, 'graph');\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/ui/src/app/api/subscription.js b/ui/src/app/api/subscription.js index e89650c6a4..728af2d38f 100644 --- a/ui/src/app/api/subscription.js +++ b/ui/src/app/api/subscription.js @@ -132,6 +132,13 @@ export default class Subscription { } this.subscriptionTimewindow = null; + this.comparisonEnabled = options.comparisonEnabled; + if (this.comparisonEnabled) { + this.timeForComparison = options.timeForComparison; + + this.comparisonTimeWindow = {}; + this.timewindowForComparison = null; + } this.units = options.units || ''; this.decimals = angular.isDefined(options.decimals) ? options.decimals : 2; @@ -311,13 +318,28 @@ export default class Subscription { } configureData() { + var additionalDatasources = []; var dataIndex = 0; + var additionalKeysNumber = 0; for (var i = 0; i < this.datasources.length; i++) { var datasource = this.datasources[i]; + var additionalDataKeys = []; + let datasourceAdditionalKeysNumber = 0; + for (var a = 0; a < datasource.dataKeys.length; a++) { var dataKey = datasource.dataKeys[a]; dataKey.hidden = false; dataKey.pattern = angular.copy(dataKey.label); + + if (this.comparisonEnabled && dataKey.settings.comparisonSettings && dataKey.settings.comparisonSettings.showValuesForComparison) { + datasourceAdditionalKeysNumber++; + additionalKeysNumber++; + let additionalDataKey = this.ctx.utils.createAdditionalDataKey(dataKey,datasource, this.timeForComparison,this.datasources,additionalKeysNumber); + dataKey.settings.comparisonSettings.color = additionalDataKey.color; + + additionalDataKeys.push(additionalDataKey); + } + var datasourceData = { datasource: datasource, dataKey: dataKey, @@ -341,8 +363,46 @@ export default class Subscription { this.legendData.data.push(legendKeyData); } } + + if (datasourceAdditionalKeysNumber > 0) { + let additionalDatasource = angular.copy(datasource); + additionalDatasource.dataKeys = additionalDataKeys; + additionalDatasource.isAdditional = true; + additionalDatasources.push(additionalDatasource); + } + } + + for (var j=0; j < additionalDatasources.length; j++) { + let additionalDatasource = additionalDatasources[j]; + for (var k=0; k < additionalDatasource.dataKeys.length; k++) { + let additionalDataKey = additionalDatasource.dataKeys[k]; + var additionalDatasourceData = { + datasource: additionalDatasource, + dataKey: additionalDataKey, + data: [] + }; + this.data.push(additionalDatasourceData); + this.hiddenData.push({data: []}); + if (this.displayLegend) { + var additionalLegendKey = { + dataKey: additionalDataKey, + dataIndex: dataIndex++ + }; + this.legendData.keys.push(additionalLegendKey); + var additionalLegendKeyData = { + min: null, + max: null, + avg: null, + total: null, + hidden: false + }; + this.legendData.data.push(additionalLegendKeyData); + } + } } + this.datasources = this.datasources.concat(additionalDatasources); + var subscription = this; var registration; @@ -638,7 +698,7 @@ export default class Subscription { updateTimewindow() { this.timeWindow.interval = this.subscriptionTimewindow.aggregation.interval || 1000; if (this.subscriptionTimewindow.realtimeWindowMs) { - this.timeWindow.maxTime = (new Date).getTime() + this.timeWindow.stDiff; + this.timeWindow.maxTime = (moment()).valueOf() + this.timeWindow.stDiff;//eslint-disable-line this.timeWindow.minTime = this.timeWindow.maxTime - this.subscriptionTimewindow.realtimeWindowMs; } else if (this.subscriptionTimewindow.fixedWindow) { this.timeWindow.maxTime = this.subscriptionTimewindow.fixedWindow.endTimeMs; @@ -659,6 +719,26 @@ export default class Subscription { return this.subscriptionTimewindow; } + updateComparisonTimewindow() { + this.comparisonTimeWindow.interval = this.timewindowForComparison.aggregation.interval || 1000; + if (this.timewindowForComparison.realtimeWindowMs) { + this.comparisonTimeWindow.maxTime = moment(this.timeWindow.maxTime).subtract(1, this.timeForComparison).valueOf(); //eslint-disable-line + this.comparisonTimeWindow.minTime = this.comparisonTimeWindow.maxTime - this.timewindowForComparison.realtimeWindowMs; + } else if (this.timewindowForComparison.fixedWindow) { + this.comparisonTimeWindow.maxTime = this.timewindowForComparison.fixedWindow.endTimeMs; + this.comparisonTimeWindow.minTime = this.timewindowForComparison.fixedWindow.startTimeMs; + } + } + + updateSubscriptionForComparison() { + if (!this.subscriptionTimewindow) { + this.subscriptionTimewindow = this.updateRealtimeSubscription(); + } + this.timewindowForComparison = this.ctx.timeService.createTimewindowForComparison(this.subscriptionTimewindow, this.timeForComparison); + this.updateComparisonTimewindow(); + return this.timewindowForComparison; + } + dataUpdated(sourceData, datasourceIndex, dataKeyIndex, apply) { for (var x = 0; x < this.datasourceListeners.length; x++) { this.datasources[x].dataReceived = this.datasources[x].dataReceived === true; @@ -689,6 +769,9 @@ export default class Subscription { if (update) { if (this.subscriptionTimewindow && this.subscriptionTimewindow.realtimeWindowMs) { this.updateTimewindow(); + if (this.timewindowForComparison && this.timewindowForComparison.realtimeWindowMs) { + this.updateComparisonTimewindow(); + } } currentData.data = sourceData.data; if (this.caulculateLegendData) { @@ -745,6 +828,9 @@ export default class Subscription { this.notifyDataLoading(); if (this.type === this.ctx.types.widgetType.timeseries.value && this.timeWindowConfig) { this.updateRealtimeSubscription(); + if (this.comparisonEnabled) { + this.updateSubscriptionForComparison(); + } if (this.subscriptionTimewindow.fixedWindow) { this.onDataUpdated(); } @@ -776,6 +862,17 @@ export default class Subscription { datasourceIndex: index }; + if (this.comparisonEnabled && datasource.isAdditional) { + listener.subscriptionTimewindow = this.timewindowForComparison; + listener.updateRealtimeSubscription = function () { + this.subscriptionTimewindow = subscription.updateSubscriptionForComparison(); + return this.subscriptionTimewindow; + }; + listener.setRealtimeSubscription = function () { + subscription.updateSubscriptionForComparison(); + }; + } + for (var a = 0; a < datasource.dataKeys.length; a++) { this.data[index + a].data = []; } @@ -908,7 +1005,6 @@ export default class Subscription { }); this.registrations = []; } - } function calculateMin(data) { diff --git a/ui/src/app/api/time.service.js b/ui/src/app/api/time.service.js index 878d43eed8..6b2e3f7687 100644 --- a/ui/src/app/api/time.service.js +++ b/ui/src/app/api/time.service.js @@ -47,6 +47,7 @@ function TimeService($translate, $http, $q, types) { defaultTimewindow: defaultTimewindow, toHistoryTimewindow: toHistoryTimewindow, createSubscriptionTimewindow: createSubscriptionTimewindow, + createTimewindowForComparison: createTimewindowForComparison, getMaxDatapointsLimit: function () { return maxDatapointsLimit; }, @@ -383,5 +384,27 @@ function TimeService($translate, $http, $q, types) { } } + function createTimewindowForComparison(subscriptionTimewindow, timeUnit) { + var timewindowForComparison = { + fixedWindow: null, + realtimeWindowMs: null, + aggregation: subscriptionTimewindow.aggregation + }; + + if (subscriptionTimewindow.realtimeWindowMs) { + timewindowForComparison.startTs = moment(subscriptionTimewindow.startTs).subtract(1, timeUnit).valueOf(); //eslint-disable-line + timewindowForComparison.realtimeWindowMs = subscriptionTimewindow.realtimeWindowMs; + } else if (subscriptionTimewindow.fixedWindow) { + var timeInterval = subscriptionTimewindow.fixedWindow.endTimeMs - subscriptionTimewindow.fixedWindow.startTimeMs; + var endTimeMs = moment(subscriptionTimewindow.fixedWindow.endTimeMs).subtract(1, timeUnit).valueOf(); //eslint-disable-line + timewindowForComparison.startTs = endTimeMs - timeInterval; + timewindowForComparison.fixedWindow = { + startTimeMs: timewindowForComparison.startTs, + endTimeMs: endTimeMs + }; + } + + return timewindowForComparison; + } } diff --git a/ui/src/app/common/utils.service.js b/ui/src/app/common/utils.service.js index dd2a32de3e..23a29308de 100644 --- a/ui/src/app/common/utils.service.js +++ b/ui/src/app/common/utils.service.js @@ -144,6 +144,7 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t isLocalUrl: isLocalUrl, validateDatasources: validateDatasources, createKey: createKey, + createAdditionalDataKey: createAdditionalDataKey, createLabelFromDatasource: createLabelFromDatasource, insertVariable: insertVariable, customTranslation: customTranslation, @@ -411,8 +412,8 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t return copy; } - function genNextColor(datasources) { - var index = 0; + function genNextColor(datasources, initialIndex) { + var index = initialIndex || 0; if (datasources) { for (var i = 0; i < datasources.length; i++) { var datasource = datasources[i]; @@ -491,6 +492,23 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t return dataKey; } + function createAdditionalDataKey(dataKey, datasource, timeUnit, datasources, additionalKeysNumber) { + let additionalDataKey = angular.copy(dataKey); + if (dataKey.settings.comparisonSettings.comparisonValuesLabel) { + additionalDataKey.label = createLabelFromDatasource(datasource, dataKey.settings.comparisonSettings.comparisonValuesLabel); + } else { + additionalDataKey.label = dataKey.label + ' ' + $translate.instant('legend.comparison-time-ago.'+timeUnit); + } + additionalDataKey.pattern = additionalDataKey.label; + if (dataKey.settings.comparisonSettings.color) { + additionalDataKey.color = dataKey.settings.comparisonSettings.color; + } else { + additionalDataKey.color = genNextColor(datasources, additionalKeysNumber); + } + additionalDataKey._hash = Math.random(); + return additionalDataKey; + } + function createLabelFromDatasource(datasource, pattern) { var label = angular.copy(pattern); var match = varsRegex.exec(pattern); diff --git a/ui/src/app/components/widget/widget.controller.js b/ui/src/app/components/widget/widget.controller.js index 593c3de36e..5d98e44d0e 100644 --- a/ui/src/app/components/widget/widget.controller.js +++ b/ui/src/app/components/widget/widget.controller.js @@ -357,8 +357,10 @@ export default function WidgetController($scope, $state, $timeout, $window, $ocL if (widget.type !== types.widgetType.rpc.value && widget.type !== types.widgetType.static.value) { options = { type: widget.type, - stateData: vm.typeParameters.stateData - } + stateData: vm.typeParameters.stateData, + comparisonEnabled: widgetContext.settings.comparisonEnabled, + timeForComparison: widgetContext.settings.timeForComparison + }; if (widget.type == types.widgetType.alarm.value) { options.alarmSource = angular.copy(widget.config.alarmSource); options.alarmSearchStatus = angular.isDefined(widget.config.alarmSearchStatus) ? diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json index 63050c0fa1..b7f6a0f23f 100644 --- a/ui/src/app/locale/locale.constant-en_US.json +++ b/ui/src/app/locale/locale.constant-en_US.json @@ -1195,7 +1195,13 @@ "min": "min", "max": "max", "avg": "avg", - "total": "total" + "total": "total", + "comparison-time-ago": { + "days": "(day ago)", + "weeks": "(week ago)", + "months": "(month ago)", + "years": "(year ago)" + } }, "login": { "login": "Login", diff --git a/ui/src/app/locale/locale.constant-ru_RU.json b/ui/src/app/locale/locale.constant-ru_RU.json index 42fd47e2b8..c2a9fd2109 100644 --- a/ui/src/app/locale/locale.constant-ru_RU.json +++ b/ui/src/app/locale/locale.constant-ru_RU.json @@ -1169,7 +1169,13 @@ "min": "Мин", "max": "Макс", "avg": "Среднее", - "total": "Сумма" + "total": "Сумма", + "comparison-time-ago": { + "days": "(день назад)", + "weeks": "(неделю назад)", + "months": "(месяц назад)", + "years": "(год назад)" + } }, "login": { "login": "Войти", diff --git a/ui/src/app/locale/locale.constant-uk_UA.json b/ui/src/app/locale/locale.constant-uk_UA.json index fa458eddb3..41c8db576f 100644 --- a/ui/src/app/locale/locale.constant-uk_UA.json +++ b/ui/src/app/locale/locale.constant-uk_UA.json @@ -1584,7 +1584,13 @@ "min": "мін", "max": "макс", "avg": "середнє", - "total": "Сума" + "total": "Сума", + "comparison-time-ago": { + "days": "(день тому)", + "weeks": "(тиждень тому)", + "months": "(місяць тому)", + "years": "(рік тому)" + } }, "login": { "login": "Вхід", diff --git a/ui/src/app/widget/lib/flot-widget.js b/ui/src/app/widget/lib/flot-widget.js index 6a26e6bec2..08496d574b 100644 --- a/ui/src/app/widget/lib/flot-widget.js +++ b/ui/src/app/widget/lib/flot-widget.js @@ -23,6 +23,7 @@ import 'flot/src/plugins/jquery.flot.selection'; import 'flot/src/plugins/jquery.flot.pie'; import 'flot/src/plugins/jquery.flot.crosshair'; import 'flot/src/plugins/jquery.flot.stack'; +import 'flot/src/plugins/jquery.flot.symbol'; import 'flot.curvedlines/curvedLines'; /* eslint-disable angular/angularelement */ @@ -32,6 +33,7 @@ export default class TbFlot { this.ctx = ctx; this.chartType = chartType || 'line'; var settings = ctx.settings; + var utils = this.ctx.$scope.$injector.get('utils'); ctx.tooltip = $('#flot-series-tooltip'); if (ctx.tooltip.length === 0) { @@ -59,7 +61,7 @@ export default class TbFlot { divElement.css({ display: "flex", alignItems: "center", - justifyContent: "flex-start" + justifyContent: "space-between" }); var lineSpan = $(''); lineSpan.css({ @@ -125,55 +127,99 @@ export default class TbFlot { } else { ctx.tooltipFormatter = function(hoverInfo, seriesIndex) { var content = ''; - var timestamp = parseInt(hoverInfo.time); - var date = moment(timestamp).format('YYYY-MM-DD HH:mm:ss'); - var dateDiv = $('