Browse Source

Merge branch 'master' into feature/api-limits

pull/440/head
Igor Kulikov 9 years ago
parent
commit
3f6df819c2
  1. 4
      application/src/main/data/json/system/widget_bundles/cards.json
  2. 4
      application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java
  3. 37
      ui/src/app/api/telemetry-websocket.service.js
  4. 35
      ui/src/app/common/utils.service.js
  5. 2
      ui/src/app/entity/attribute/attribute-table.scss
  6. 1
      ui/src/app/locale/locale.constant.js
  7. 2
      ui/src/app/widget/lib/CanvasDigitalGauge.js
  8. 4
      ui/src/app/widget/lib/alarms-table-widget.tpl.html
  9. 2
      ui/src/app/widget/lib/entities-table-widget.tpl.html
  10. 123
      ui/src/app/widget/lib/google-map.js
  11. 141
      ui/src/app/widget/lib/image-map.js
  12. 71
      ui/src/app/widget/lib/map-widget.js
  13. 74
      ui/src/app/widget/lib/map-widget2.js
  14. 120
      ui/src/app/widget/lib/openstreet-map.js
  15. 58
      ui/src/app/widget/lib/timeseries-table-widget.js
  16. 4
      ui/src/app/widget/lib/timeseries-table-widget.scss
  17. 92
      ui/src/app/widget/lib/timeseries-table-widget.tpl.html

4
application/src/main/data/json/system/widget_bundles/cards.json

@ -112,9 +112,9 @@
"templateHtml": "<tb-timeseries-table-widget \n table-id=\"tableId\"\n ctx=\"ctx\">\n</tb-timeseries-table-widget>",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n var id = self.ctx.$scope.$injector.get('utils').guid();\n scope.tableId = \"table-\"+id;\n scope.ctx = self.ctx;\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.$broadcast('timeseries-table-data-updated', self.ctx.$scope.tableId);\n}\n\nself.onDestroy = function() {\n}",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"TimeseriesTableSettings\",\n \"properties\": {\n \"showTimestamp\": {\n \"title\": \"Display timestamp column\",\n \"type\": \"boolean\",\n \"default\": true\n }\n },\n \"required\": []\n },\n \"form\": [\n \"showTimestamp\"\n ]\n}",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"TimeseriesTableSettings\",\n \"properties\": {\n \"showTimestamp\": {\n \"title\": \"Display timestamp column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"displayPagination\": {\n \"title\": \"Display pagination\",\n \"type\": \"boolean\",\n \"default\": true\n }, \n \"defaultPageSize\": {\n \"title\": \"Default page size\",\n \"type\": \"number\",\n \"default\": 10\n }\n },\n \"required\": []\n },\n \"form\": [\n \"showTimestamp\",\n \"displayPagination\",\n \"defaultPageSize\"\n ]\n}",
"dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"useCellStyleFunction\": {\n \"title\": \"Use cell style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellStyleFunction\": {\n \"title\": \"Cell style function: f(value)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useCellContentFunction\": {\n \"title\": \"Use cell content function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellContentFunction\": {\n \"title\": \"Cell content function: f(value, rowData, filter)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\"\n },\n \"useCellContentFunction\",\n {\n \"key\": \"cellContentFunction\",\n \"type\": \"javascript\"\n }\n ]\n}",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature °C\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = (value + 60)/120 * 100;\\n var color = tinycolor.mix('blue', 'red', amount = percent);\\n color.setAlpha(.5);\\n return {\\n paddingLeft: '20px',\\n color: '#ffffff',\\n background: color.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\"},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity, %\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = value;\\n var backgroundColor = tinycolor('blue');\\n backgroundColor.setAlpha(value/100);\\n var color = 'blue';\\n if (value > 50) {\\n color = 'white';\\n }\\n \\n return {\\n paddingLeft: '20px',\\n color: color,\\n background: backgroundColor.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showTimestamp\":true},\"title\":\"Timeseries table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":false,\"showLegend\":false}"
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature °C\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = (value + 60)/120 * 100;\\n var color = tinycolor.mix('blue', 'red', amount = percent);\\n color.setAlpha(.5);\\n return {\\n paddingLeft: '20px',\\n color: '#ffffff',\\n background: color.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\"},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity, %\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = value;\\n var backgroundColor = tinycolor('blue');\\n backgroundColor.setAlpha(value/100);\\n var color = 'blue';\\n if (value > 50) {\\n color = 'white';\\n }\\n \\n return {\\n paddingLeft: '20px',\\n color: color,\\n background: backgroundColor.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showTimestamp\":true,\"displayPagination\":true,\"defaultPageSize\":10},\"title\":\"Timeseries table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":false,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}"
}
}
]

4
application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java

@ -47,8 +47,8 @@ public class WebSocketConfiguration implements WebSocketConfigurer {
@Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(8192);
container.setMaxBinaryMessageBufferSize(8192);
container.setMaxTextMessageBufferSize(32768);
container.setMaxBinaryMessageBufferSize(32768);
return container;
}

37
ui/src/app/api/telemetry-websocket.service.js

@ -23,6 +23,8 @@ export default angular.module('thingsboard.api.telemetryWebsocket', [thingsboard
const RECONNECT_INTERVAL = 2000;
const WS_IDLE_TIMEOUT = 90000;
const MAX_PUBLISH_COMMANDS = 10;
/*@ngInject*/
function TelemetryWebsocketService($rootScope, $websocket, $timeout, $window, types, userService) {
@ -75,19 +77,40 @@ function TelemetryWebsocketService($rootScope, $websocket, $timeout, $window, ty
return service;
function publishCommands () {
if (isOpened && (cmdsWrapper.tsSubCmds.length > 0 ||
cmdsWrapper.historyCmds.length > 0 ||
cmdsWrapper.attrSubCmds.length > 0)) {
dataStream.send(angular.copy(cmdsWrapper)).then(function () {
while(isOpened && hasCommands()) {
dataStream.send(preparePublishCommands()).then(function () {
checkToClose();
});
cmdsWrapper.tsSubCmds = [];
cmdsWrapper.historyCmds = [];
cmdsWrapper.attrSubCmds = [];
}
tryOpenSocket();
}
function hasCommands() {
return cmdsWrapper.tsSubCmds.length > 0 ||
cmdsWrapper.historyCmds.length > 0 ||
cmdsWrapper.attrSubCmds.length > 0;
}
function preparePublishCommands() {
var preparedWrapper = {};
var leftCount = MAX_PUBLISH_COMMANDS;
preparedWrapper.tsSubCmds = popCmds(cmdsWrapper.tsSubCmds, leftCount);
leftCount -= preparedWrapper.tsSubCmds.length;
preparedWrapper.historyCmds = popCmds(cmdsWrapper.historyCmds, leftCount);
leftCount -= preparedWrapper.historyCmds.length;
preparedWrapper.attrSubCmds = popCmds(cmdsWrapper.attrSubCmds, leftCount);
return preparedWrapper;
}
function popCmds(cmds, leftCount) {
var toPublish = Math.min(cmds.length, leftCount);
if (toPublish > 0) {
return cmds.splice(0, toPublish);
} else {
return [];
}
}
function onError (/*message*/) {
isOpening = false;
}

35
ui/src/app/common/utils.service.js

@ -134,6 +134,8 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
defaultAlarmDataKeys.push(dataKey);
}
var imageAspectMap = {};
var service = {
getDefaultDatasource: getDefaultDatasource,
generateObjectFromJsonSchema: generateObjectFromJsonSchema,
@ -159,7 +161,8 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
insertVariable: insertVariable,
customTranslation: customTranslation,
objToBase64: objToBase64,
base64toObj: base64toObj
base64toObj: base64toObj,
loadImageAspect: loadImageAspect
}
return service;
@ -543,4 +546,34 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
return obj;
}
function loadImageAspect(imageUrl) {
var deferred = $q.defer();
if (imageUrl && imageUrl.length) {
var urlHashCode = hashCode(imageUrl);
var aspect = imageAspectMap[urlHashCode];
if (angular.isUndefined(aspect)) {
var testImage = document.createElement('img'); // eslint-disable-line
testImage.style.visibility = 'hidden';
testImage.onload = function() {
aspect = testImage.width / testImage.height;
document.body.removeChild(testImage); //eslint-disable-line
imageAspectMap[urlHashCode] = aspect;
deferred.resolve(aspect);
};
testImage.onerror = function() {
aspect = 0;
imageAspectMap[urlHashCode] = aspect;
deferred.resolve(aspect);
};
document.body.appendChild(testImage); //eslint-disable-line
testImage.src = imageUrl;
} else {
deferred.resolve(aspect);
}
} else {
deferred.resolve(0);
}
return deferred.promise;
}
}

2
ui/src/app/entity/attribute/attribute-table.scss

@ -47,7 +47,7 @@ md-toolbar.md-table-toolbar.alternate {
.widgets-carousel {
position: relative;
margin: 0px;
height: calc(100% - 100px);
min-height: 150px !important;
tb-dashboard {

1
ui/src/app/locale/locale.constant.js

@ -1213,6 +1213,7 @@ export default angular.module('thingsboard.locale', [])
"remove-widget-title": "Are you sure you want to remove the widget '{{widgetTitle}}'?",
"remove-widget-text": "After the confirmation the widget and all related data will become unrecoverable.",
"timeseries": "Time series",
"search-data": "Search data",
"latest-values": "Latest values",
"rpc": "Control widget",
"alarm": "Alarm widget",

2
ui/src/app/widget/lib/CanvasDigitalGauge.js

@ -455,7 +455,7 @@ function barDimensions(context, options, x, y, w, h) {
if (options.hideMinMax && options.label === '') {
bd.labelY = bd.barBottom;
bd.barLeft = bd.origBaseX + options.fontMinMaxSize/3 * bd.fontSizeFactor;
bd.barRight = bd.bd.origBaseX + w + /*bd.width*/ - options.fontMinMaxSize/3 * bd.fontSizeFactor;
bd.barRight = bd.origBaseX + w + /*bd.width*/ - options.fontMinMaxSize/3 * bd.fontSizeFactor;
} else {
context.font = canvasGauges.drawings.font(options, 'MinMax', bd.fontSizeFactor);
var minTextWidth = context.measureText(options.minValue+'').width;

4
ui/src/app/widget/lib/alarms-table-widget.tpl.html

@ -77,7 +77,7 @@
</td>
<td md-cell ng-if="vm.displayDetails" class="tb-action-cell">
<md-button class="md-icon-button" aria-label="{{ 'alarm.details' | translate }}"
ng-click="vm.openAlarmDetails($event, alarm)">
ng-click="vm.openAlarmDetails($event, alarm)" ng-disabled="$root.loading">
<md-icon aria-label="{{ 'alarm.details' | translate }}" class="material-icons">more_horiz</md-icon>
<md-tooltip md-direction="top">
{{ 'alarm.details' | translate }}
@ -90,7 +90,7 @@
width: vm.actionCellDescriptors.length*36+'px'}">
<md-button class="md-icon-button" ng-repeat="actionDescriptor in vm.actionCellDescriptors"
aria-label="{{ actionDescriptor.displayName }}"
ng-click="vm.onActionButtonClick($event, alarm, actionDescriptor)">
ng-click="vm.onActionButtonClick($event, alarm, actionDescriptor)" ng-disabled="$root.loading">
<md-icon aria-label="{{ actionDescriptor.displayName }}" class="material-icons">{{actionDescriptor.icon}}</md-icon>
<md-tooltip md-direction="top">
{{ actionDescriptor.displayName }}

2
ui/src/app/widget/lib/entities-table-widget.tpl.html

@ -63,7 +63,7 @@
width: vm.actionCellDescriptors.length*36+'px'}">
<md-button class="md-icon-button" ng-repeat="actionDescriptor in vm.actionCellDescriptors"
aria-label="{{ actionDescriptor.displayName }}"
ng-click="vm.onActionButtonClick($event, entity, actionDescriptor)">
ng-click="vm.onActionButtonClick($event, entity, actionDescriptor)" ng-disabled="$root.loading">
<md-icon aria-label="{{ actionDescriptor.displayName }}" class="material-icons">{{actionDescriptor.icon}}</md-icon>
<md-tooltip md-direction="top">
{{ actionDescriptor.displayName }}

123
ui/src/app/widget/lib/google-map.js

@ -13,16 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var gmGlobals = {
loadingGmId: null,
gmApiKeys: {}
}
export default class TbGoogleMap {
constructor($containerElement, initCallback, defaultZoomLevel, dontFitMapBounds, minZoomLevel, gmApiKey, gmDefaultMapType) {
constructor($containerElement, utils, initCallback, defaultZoomLevel, dontFitMapBounds, minZoomLevel, gmApiKey, gmDefaultMapType) {
var tbMap = this;
this.utils = utils;
this.defaultZoomLevel = defaultZoomLevel;
this.dontFitMapBounds = dontFitMapBounds;
this.minZoomLevel = minZoomLevel;
@ -151,80 +151,97 @@ export default class TbGoogleMap {
/* eslint-disable no-undef */
updateMarkerColor(marker, color) {
var pinColor = color.substr(1);
var pinImage = new google.maps.MarkerImage("https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|" + pinColor,
new google.maps.Size(21, 34),
new google.maps.Point(0,0),
new google.maps.Point(10, 34));
marker.setIcon(pinImage);
this.createDefaultMarkerIcon(marker, color, (iconInfo) => {
marker.setIcon(iconInfo.icon);
});
}
/* eslint-enable no-undef */
/* eslint-disable no-undef */
updateMarkerImage(marker, settings, image, maxSize) {
var testImage = document.createElement('img'); // eslint-disable-line
testImage.style.visibility = 'hidden';
testImage.onload = function() {
var width;
var height;
var aspect = testImage.width / testImage.height;
document.body.removeChild(testImage); //eslint-disable-line
if (aspect > 1) {
width = maxSize;
height = maxSize / aspect;
} else {
width = maxSize * aspect;
height = maxSize;
}
var pinImage = {
url: image,
scaledSize : new google.maps.Size(width, height)
}
marker.setIcon(pinImage);
updateMarkerIcon(marker, settings) {
this.createMarkerIcon(marker, settings, (iconInfo) => {
marker.setIcon(iconInfo.icon);
if (settings.showLabel) {
marker.set('labelAnchor', new google.maps.Point(100, height + 20));
marker.set('labelAnchor', new google.maps.Point(100, iconInfo.size[1] + 20));
}
});
}
/* eslint-disable no-undef */
/* eslint-disable no-undef */
createMarkerIcon(marker, settings, onMarkerIconReady) {
var currentImage = settings.currentImage;
var gMap = this;
if (currentImage && currentImage.url) {
this.utils.loadImageAspect(currentImage.url).then(
(aspect) => {
if (aspect) {
var width;
var height;
if (aspect > 1) {
width = currentImage.size;
height = currentImage.size / aspect;
} else {
width = currentImage.size * aspect;
height = currentImage.size;
}
var icon = {
url: currentImage.url,
scaledSize : new google.maps.Size(width, height)
};
var iconInfo = {
size: [width, height],
icon: icon
};
onMarkerIconReady(iconInfo);
} else {
gMap.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady);
}
}
);
} else {
this.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady);
}
document.body.appendChild(testImage); //eslint-disable-line
testImage.src = image;
}
/* eslint-enable no-undef */
/* eslint-disable no-undef */
createMarker(location, settings, onClickListener, markerArgs) {
var height = 34;
var pinColor = settings.color.substr(1);
var pinImage = new google.maps.MarkerImage("https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|" + pinColor,
new google.maps.Size(21, 34),
new google.maps.Point(0,0),
new google.maps.Point(10, 34));
var pinShadow = new google.maps.MarkerImage("https://chart.apis.google.com/chart?chst=d_map_pin_shadow",
createDefaultMarkerIcon(marker, color, onMarkerIconReady) {
var pinColor = color.substr(1);
var icon = new google.maps.MarkerImage("https://chart.apis.google.com/chart?chst=d_map_pin_letter_withshadow&chld=%E2%80%A2|" + pinColor,
new google.maps.Size(40, 37),
new google.maps.Point(0, 0),
new google.maps.Point(12, 35));
new google.maps.Point(0,0),
new google.maps.Point(10, 37));
var iconInfo = {
size: [40, 37],
icon: icon
};
onMarkerIconReady(iconInfo);
}
/* eslint-enable no-undef */
/* eslint-disable no-undef */
createMarker(location, settings, onClickListener, markerArgs) {
var marker;
if (settings.showLabel) {
marker = new MarkerWithLabel({
position: location,
map: this.map,
icon: pinImage,
shadow: pinShadow,
labelContent: '<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>',
labelClass: "tb-labels",
labelAnchor: new google.maps.Point(100, height + 20)
labelClass: "tb-labels"
});
} else {
marker = new google.maps.Marker({
position: location,
map: this.map,
icon: pinImage,
shadow: pinShadow
});
}
if (settings.useMarkerImage) {
this.updateMarkerImage(marker, settings, settings.markerImage, settings.markerImageSize || 34);
}
var gMap = this;
this.createMarkerIcon(marker, settings, (iconInfo) => {
marker.setIcon(iconInfo.icon);
if (settings.showLabel) {
marker.set('labelAnchor', new google.maps.Point(100, iconInfo.size[1] + 20));
}
marker.setMap(gMap.map);
});
if (settings.displayTooltip) {
this.createTooltip(marker, settings.tooltipPattern, settings.tooltipReplaceInfo, settings.autocloseTooltip, markerArgs);

141
ui/src/app/widget/lib/image-map.js

@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import 'leaflet/dist/leaflet.css';
import * as L from 'leaflet';
@ -21,9 +20,10 @@ const maxZoom = 4;
export default class TbImageMap {
constructor(ctx, $containerElement, initCallback, imageUrl, posFunction, imageEntityAlias, imageUrlAttribute) {
constructor(ctx, $containerElement, utils, initCallback, imageUrl, posFunction, imageEntityAlias, imageUrlAttribute) {
this.ctx = ctx;
this.utils = utils;
this.tooltips = [];
this.$containerElement = $containerElement;
@ -117,18 +117,15 @@ export default class TbImageMap {
}
this.imageUrl = imageUrl;
var imageMap = this;
var testImage = document.createElement('img'); // eslint-disable-line
testImage.style.visibility = 'hidden';
testImage.onload = function() {
imageMap.aspect = testImage.width / testImage.height;
document.body.removeChild(testImage); //eslint-disable-line
imageMap.onresize(updateImage);
if (initCallback) {
setTimeout(initCallback, 0); //eslint-disable-line
this.utils.loadImageAspect(imageUrl).then(
(aspect) => {
imageMap.aspect = aspect;
imageMap.onresize(updateImage);
if (initCallback) {
setTimeout(initCallback, 0); //eslint-disable-line
}
}
}
document.body.appendChild(testImage); //eslint-disable-line
testImage.src = imageUrl;
);
}
onresize(updateImage) {
@ -229,83 +226,97 @@ export default class TbImageMap {
}
updateMarkerColor(marker, color) {
var pinColor = color.substr(1);
var icon = L.icon({
iconUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|' + pinColor,
iconSize: [21, 34],
iconAnchor: [10, 34],
popupAnchor: [0, -34],
shadowUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_shadow',
shadowSize: [40, 37],
shadowAnchor: [12, 35]
this.createDefaultMarkerIcon(marker, color, (iconInfo) => {
marker.setIcon(iconInfo.icon);
});
marker.setIcon(icon);
}
updateMarkerImage(marker, settings, image, maxSize) {
var testImage = document.createElement('img'); // eslint-disable-line
testImage.style.visibility = 'hidden';
testImage.onload = function() {
var width;
var height;
var aspect = testImage.width / testImage.height;
document.body.removeChild(testImage); //eslint-disable-line
if (aspect > 1) {
width = maxSize;
height = maxSize / aspect;
} else {
width = maxSize * aspect;
height = maxSize;
}
var icon = L.icon({
iconUrl: image,
iconSize: [width, height],
iconAnchor: [marker.offsetX * width, marker.offsetY * height],
popupAnchor: [0, -height]
});
marker.setIcon(icon);
}
updateMarkerIcon(marker, settings) {
this.createMarkerIcon(marker, settings, (iconInfo) => {
marker.setIcon(iconInfo.icon);
if (settings.showLabel) {
marker.unbindTooltip();
marker.tooltipOffset = [0, -height * marker.offsetY + 10];
marker.tooltipOffset = [0, -iconInfo.size[1] * marker.offsetY + 10];
marker.bindTooltip('<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>',
{ className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset });
}
});
}
createMarkerIcon(marker, settings, onMarkerIconReady) {
var currentImage = settings.currentImage;
var opMap = this;
if (currentImage && currentImage.url) {
this.utils.loadImageAspect(currentImage.url).then(
(aspect) => {
if (aspect) {
var width;
var height;
if (aspect > 1) {
width = currentImage.size;
height = currentImage.size / aspect;
} else {
width = currentImage.size * aspect;
height = currentImage.size;
}
var icon = L.icon({
iconUrl: currentImage.url,
iconSize: [width, height],
iconAnchor: [marker.offsetX * width, marker.offsetY * height],
popupAnchor: [0, -height]
});
var iconInfo = {
size: [width, height],
icon: icon
};
onMarkerIconReady(iconInfo);
} else {
opMap.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady);
}
}
);
} else {
this.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady);
}
document.body.appendChild(testImage); //eslint-disable-line
testImage.src = image;
}
createMarker(position, settings, onClickListener, markerArgs) {
var height = 34;
var pinColor = settings.color.substr(1);
createDefaultMarkerIcon(marker, color, onMarkerIconReady) {
var pinColor = color.substr(1);
var icon = L.icon({
iconUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|' + pinColor,
iconSize: [21, 34],
iconAnchor: [21 * settings.markerOffsetX, 34 * settings.markerOffsetY],
iconAnchor: [21 * marker.offsetX, 34 * marker.offsetY],
popupAnchor: [0, -34],
shadowUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_shadow',
shadowSize: [40, 37],
shadowAnchor: [12, 35]
});
var iconInfo = {
size: [21, 34],
icon: icon
};
onMarkerIconReady(iconInfo);
}
createMarker(position, settings, onClickListener, markerArgs) {
var pos = this.posFunction(position.x, position.y);
var x = pos.x * this.width;
var y = pos.y * this.height;
var location = this.pointToLatLng(x, y);
var marker = L.marker(location, {icon: icon}).addTo(this.map);
var marker = L.marker(location, {});//.addTo(this.map);
marker.position = position;
marker.offsetX = settings.markerOffsetX;
marker.offsetY = settings.markerOffsetY;
if (settings.showLabel) {
marker.tooltipOffset = [0, -height * marker.offsetY + 10];
marker.bindTooltip('<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>',
{ className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset });
}
if (settings.useMarkerImage) {
this.updateMarkerImage(marker, settings, settings.markerImage, settings.markerImageSize || 34);
}
var opMap = this;
this.createMarkerIcon(marker, settings, (iconInfo) => {
marker.setIcon(iconInfo.icon);
if (settings.showLabel) {
marker.tooltipOffset = [0, -iconInfo.size[1] * marker.offsetY + 10];
marker.bindTooltip('<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>',
{ className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset });
}
marker.addTo(opMap.map);
});
if (settings.displayTooltip) {
this.createTooltip(marker, settings.tooltipPattern, settings.tooltipReplaceInfo, settings.autocloseTooltip, markerArgs);

71
ui/src/app/widget/lib/map-widget.js

@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import tinycolor from 'tinycolor2';
import TbGoogleMap from './google-map';
@ -75,7 +74,7 @@ export default class TbMapWidget {
if (!$element) {
$element = ctx.$container;
}
this.utils = ctx.$scope.$injector.get('utils');
this.drawRoutes = drawRoutes;
this.markers = [];
if (this.drawRoutes) {
@ -110,9 +109,9 @@ export default class TbMapWidget {
};
if (mapProvider === 'google-map') {
this.map = new TbGoogleMap($element, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel, settings.gmApiKey, settings.gmDefaultMapType);
this.map = new TbGoogleMap($element, this.utils, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel, settings.gmApiKey, settings.gmDefaultMapType);
} else if (mapProvider === 'openstreet-map') {
this.map = new TbOpenStreetMap($element, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel);
this.map = new TbOpenStreetMap($element, this.utils, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel);
}
}
@ -274,9 +273,13 @@ export default class TbMapWidget {
if (!this.locationsSettings[i].useMarkerImageFunction &&
angular.isDefined(configuredLocationsSettings[i].markerImage) &&
configuredLocationsSettings[i].markerImage.length > 0) {
this.locationsSettings[i].markerImage = configuredLocationsSettings[i].markerImage;
this.locationsSettings[i].useMarkerImage = true;
this.locationsSettings[i].markerImageSize = configuredLocationsSettings[i].markerImageSize || 34;
var url = this.ctx.settings.markerImage;
var size = this.ctx.settings.markerImageSize || 34;
this.locationSettings.currentImage = {
url: url,
size: size
};
}
if (this.drawRoutes) {
@ -380,17 +383,41 @@ export default class TbMapWidget {
}
}
function updateLocationMarkerImage(location, dataMap) {
function updateLocationMarkerIcon(location, dataMap) {
var image = calculateLocationMarkerImage(location, dataMap);
if (image != null && (!location.settings.calculatedImage || !angular.equals(location.settings.calculatedImage, image))) {
tbMap.map.updateMarkerImage(location.marker, location.settings, image.url, image.size);
location.settings.calculatedImage = image;
if (image && (!location.settings.currentImage || !angular.equals(location.settings.currentImage, image))) {
location.settings.currentImage = image;
tbMap.map.updateMarkerIcon(location.marker, location.settings);
}
}
function updateLocationStyle(location, dataMap) {
updateLocationColor(location, dataMap);
updateLocationMarkerImage(location, dataMap);
updateLocationMarkerIcon(location, dataMap);
}
function createOrUpdateLocationMarker(location, markerLocation, dataMap) {
var changed = false;
if (!location.marker) {
var image = calculateLocationMarkerImage(location, dataMap);
if (image && (!location.settings.currentImage || !angular.equals(location.settings.currentImage, image))) {
location.settings.currentImage = image;
}
location.marker = tbMap.map.createMarker(markerLocation, location.settings,
function() {
tbMap.callbacks.onLocationClick(location);
}
);
tbMap.markers.push(location.marker);
changed = true;
} else {
var prevPosition = tbMap.map.getMarkerPosition(location.marker);
if (!prevPosition.equals(markerLocation)) {
tbMap.map.setMarkerPosition(location.marker, markerLocation);
changed = true;
}
}
return changed;
}
function updateLocation(location, data, dataMap) {
@ -413,15 +440,7 @@ export default class TbMapWidget {
}
if (latLngs.length > 0) {
var markerLocation = latLngs[latLngs.length-1];
if (!location.marker) {
location.marker = tbMap.map.createMarker(markerLocation, location.settings,
function() {
tbMap.callbacks.onLocationClick(location);
}
);
} else {
tbMap.map.setMarkerPosition(location.marker, markerLocation);
}
createOrUpdateLocationMarker(location, markerLocation, dataMap);
}
if (!location.polyline) {
location.polyline = tbMap.map.createPolyline(latLngs, location.settings);
@ -439,18 +458,8 @@ export default class TbMapWidget {
lat = latData[latData.length-1][1];
lng = lngData[lngData.length-1][1];
latLng = tbMap.map.createLatLng(lat, lng);
if (!location.marker) {
location.marker = tbMap.map.createMarker(latLng, location.settings, function() {
tbMap.callbacks.onLocationClick(location);
});
tbMap.markers.push(location.marker);
if (createOrUpdateLocationMarker(location, latLng, dataMap)) {
locationChanged = true;
} else {
var prevPosition = tbMap.map.getMarkerPosition(location.marker);
if (!prevPosition.equals(latLng)) {
tbMap.map.setMarkerPosition(location.marker, latLng);
locationChanged = true;
}
}
}
updateLocationStyle(location, dataMap);

74
ui/src/app/widget/lib/map-widget2.js

@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import tinycolor from 'tinycolor2';
import TbGoogleMap from './google-map';
@ -75,11 +74,11 @@ export default class TbMapWidgetV2 {
});
if (mapProvider === 'google-map') {
this.map = new TbGoogleMap($element, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel, settings.gmApiKey, settings.gmDefaultMapType);
this.map = new TbGoogleMap($element, this.utils, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel, settings.gmApiKey, settings.gmDefaultMapType);
} else if (mapProvider === 'openstreet-map') {
this.map = new TbOpenStreetMap($element, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel, settings.mapProvider);
this.map = new TbOpenStreetMap($element, this.utils, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel, settings.mapProvider);
} else if (mapProvider === 'image-map') {
this.map = new TbImageMap(this.ctx, $element, initCallback,
this.map = new TbImageMap(this.ctx, $element, this.utils, initCallback,
settings.mapImageUrl,
settings.posFunction,
settings.imageEntityAlias,
@ -159,9 +158,13 @@ export default class TbMapWidgetV2 {
if (!this.locationSettings.useMarkerImageFunction &&
angular.isDefined(this.ctx.settings.markerImage) &&
this.ctx.settings.markerImage.length > 0) {
this.locationSettings.markerImage = this.ctx.settings.markerImage;
this.locationSettings.useMarkerImage = true;
this.locationSettings.markerImageSize = this.ctx.settings.markerImageSize || 34;
var url = this.ctx.settings.markerImage;
var size = this.ctx.settings.markerImageSize || 34;
this.locationSettings.currentImage = {
url: url,
size: size
};
}
if (this.drawRoutes) {
@ -235,10 +238,10 @@ export default class TbMapWidgetV2 {
}
}
function updateLocationMarkerImage(location, image) {
if (image && (!location.settings.calculatedImage || !angular.equals(location.settings.calculatedImage, image))) {
tbMap.map.updateMarkerImage(location.marker, location.settings, image.url, image.size);
location.settings.calculatedImage = image;
function updateLocationMarkerIcon(location, image) {
if (image && (!location.settings.currentImage || !angular.equals(location.settings.currentImage, image))) {
location.settings.currentImage = image;
tbMap.map.updateMarkerIcon(location.marker, location.settings);
}
}
@ -247,7 +250,31 @@ export default class TbMapWidgetV2 {
var color = calculateLocationColor(location, dataMap);
var image = calculateLocationMarkerImage(location, dataMap);
updateLocationColor(location, color, image);
updateLocationMarkerImage(location, image);
updateLocationMarkerIcon(location, image);
}
function createOrUpdateLocationMarker(location, markerLocation, dataMap) {
var changed = false;
if (!location.marker) {
var image = calculateLocationMarkerImage(location, dataMap);
if (image && (!location.settings.currentImage || !angular.equals(location.settings.currentImage, image))) {
location.settings.currentImage = image;
}
location.marker = tbMap.map.createMarker(markerLocation, location.settings,
function (event) {
tbMap.callbacks.onLocationClick(location);
locationRowClick(event, location);
}, [location.dsIndex]);
tbMap.markers.push(location.marker);
changed = true;
} else {
var prevPosition = tbMap.map.getMarkerPosition(location.marker);
if (!prevPosition.equals(markerLocation)) {
tbMap.map.setMarkerPosition(location.marker, markerLocation);
changed = true;
}
}
return changed;
}
function locationRowClick($event, location) {
@ -284,16 +311,7 @@ export default class TbMapWidgetV2 {
}
if (latLngs.length > 0) {
var markerLocation = latLngs[latLngs.length - 1];
if (!location.marker) {
location.marker = tbMap.map.createMarker(markerLocation, location.settings,
function (event) {
tbMap.callbacks.onLocationClick(location);
locationRowClick(event, location);
}, [location.dsIndex]
);
} else {
tbMap.map.setMarkerPosition(location.marker, markerLocation);
}
createOrUpdateLocationMarker(location, markerLocation, dataMap);
}
if (!location.polyline) {
location.polyline = tbMap.map.createPolyline(latLngs, location.settings);
@ -312,20 +330,8 @@ export default class TbMapWidgetV2 {
lng = lngData[lngData.length - 1][1];
if (angular.isDefined(lat) && lat != null && angular.isDefined(lng) && lng != null) {
latLng = tbMap.map.createLatLng(lat, lng);
if (!location.marker) {
location.marker = tbMap.map.createMarker(latLng, location.settings,
function (event) {
tbMap.callbacks.onLocationClick(location);
locationRowClick(event, location);
}, [location.dsIndex]);
tbMap.markers.push(location.marker);
if (createOrUpdateLocationMarker(location, latLng, dataMap)) {
locationChanged = true;
} else {
var prevPosition = tbMap.map.getMarkerPosition(location.marker);
if (!prevPosition.equals(latLng)) {
tbMap.map.setMarkerPosition(location.marker, latLng);
locationChanged = true;
}
}
}
}

120
ui/src/app/widget/lib/openstreet-map.js

@ -13,15 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import 'leaflet/dist/leaflet.css';
import * as L from 'leaflet';
import 'leaflet-providers';
export default class TbOpenStreetMap {
constructor($containerElement, initCallback, defaultZoomLevel, dontFitMapBounds, minZoomLevel, mapProvider) {
constructor($containerElement, utils, initCallback, defaultZoomLevel, dontFitMapBounds, minZoomLevel, mapProvider) {
this.utils = utils;
this.defaultZoomLevel = defaultZoomLevel;
this.dontFitMapBounds = dontFitMapBounds;
this.minZoomLevel = minZoomLevel;
@ -54,55 +54,62 @@ export default class TbOpenStreetMap {
}
updateMarkerColor(marker, color) {
var pinColor = color.substr(1);
var icon = L.icon({
iconUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|' + pinColor,
iconSize: [21, 34],
iconAnchor: [10, 34],
popupAnchor: [0, -34],
shadowUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_shadow',
shadowSize: [40, 37],
shadowAnchor: [12, 35]
this.createDefaultMarkerIcon(marker, color, (iconInfo) => {
marker.setIcon(iconInfo.icon);
});
marker.setIcon(icon);
}
updateMarkerImage(marker, settings, image, maxSize) {
var testImage = document.createElement('img'); // eslint-disable-line
testImage.style.visibility = 'hidden';
testImage.onload = function() {
var width;
var height;
var aspect = testImage.width / testImage.height;
document.body.removeChild(testImage); //eslint-disable-line
if (aspect > 1) {
width = maxSize;
height = maxSize / aspect;
} else {
width = maxSize * aspect;
height = maxSize;
}
var icon = L.icon({
iconUrl: image,
iconSize: [width, height],
iconAnchor: [width/2, height],
popupAnchor: [0, -height]
});
marker.setIcon(icon);
}
updateMarkerIcon(marker, settings) {
this.createMarkerIcon(marker, settings, (iconInfo) => {
marker.setIcon(iconInfo.icon);
if (settings.showLabel) {
marker.unbindTooltip();
marker.tooltipOffset = [0, -height + 10];
marker.tooltipOffset = [0, -iconInfo.size[1] + 10];
marker.bindTooltip('<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>',
{ className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset });
}
});
}
createMarkerIcon(marker, settings, onMarkerIconReady) {
var currentImage = settings.currentImage;
var opMap = this;
if (currentImage && currentImage.url) {
this.utils.loadImageAspect(currentImage.url).then(
(aspect) => {
if (aspect) {
var width;
var height;
if (aspect > 1) {
width = currentImage.size;
height = currentImage.size / aspect;
} else {
width = currentImage.size * aspect;
height = currentImage.size;
}
var icon = L.icon({
iconUrl: currentImage.url,
iconSize: [width, height],
iconAnchor: [width/2, height],
popupAnchor: [0, -height]
});
var iconInfo = {
size: [width, height],
icon: icon
};
onMarkerIconReady(iconInfo);
} else {
opMap.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady);
}
}
);
} else {
this.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady);
}
document.body.appendChild(testImage); //eslint-disable-line
testImage.src = image;
}
createMarker(location, settings, onClickListener, markerArgs) {
var height = 34;
var pinColor = settings.color.substr(1);
createDefaultMarkerIcon(marker, color, onMarkerIconReady) {
var pinColor = color.substr(1);
var icon = L.icon({
iconUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|' + pinColor,
iconSize: [21, 34],
@ -112,18 +119,25 @@ export default class TbOpenStreetMap {
shadowSize: [40, 37],
shadowAnchor: [12, 35]
});
var iconInfo = {
size: [21, 34],
icon: icon
};
onMarkerIconReady(iconInfo);
}
var marker = L.marker(location, {icon: icon}).addTo(this.map);
if (settings.showLabel) {
marker.tooltipOffset = [0, -height + 10];
marker.bindTooltip('<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>',
{ className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset });
}
if (settings.useMarkerImage) {
this.updateMarkerImage(marker, settings, settings.markerImage, settings.markerImageSize || 34);
}
createMarker(location, settings, onClickListener, markerArgs) {
var marker = L.marker(location, {});
var opMap = this;
this.createMarkerIcon(marker, settings, (iconInfo) => {
marker.setIcon(iconInfo.icon);
if (settings.showLabel) {
marker.tooltipOffset = [0, -iconInfo.size[1] + 10];
marker.bindTooltip('<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>',
{ className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset });
}
marker.addTo(opMap.map);
});
if (settings.displayTooltip) {
this.createTooltip(marker, settings.tooltipPattern, settings.tooltipReplaceInfo, settings.autocloseTooltip, markerArgs);

58
ui/src/app/widget/lib/timeseries-table-widget.js

@ -47,9 +47,37 @@ function TimeseriesTableWidget() {
/*@ngInject*/
function TimeseriesTableWidgetController($element, $scope, $filter) {
var vm = this;
let dateFormatFilter = 'yyyy-MM-dd HH:mm:ss';
vm.sources = [];
vm.sourceIndex = 0;
vm.defaultPageSize = 10;
vm.defaultSortOrder = '-0';
vm.query = {
"search": null
};
vm.enterFilterMode = enterFilterMode;
vm.exitFilterMode = exitFilterMode;
function enterFilterMode () {
vm.query.search = '';
vm.ctx.hideTitlePanel = true;
}
function exitFilterMode () {
vm.query.search = null;
vm.ctx.hideTitlePanel = false;
}
vm.searchAction = {
name: 'action.search',
show: true,
onAction: function() {
vm.enterFilterMode();
},
icon: 'search'
};
$scope.$watch('vm.ctx', function() {
if (vm.ctx) {
@ -62,6 +90,7 @@ function TimeseriesTableWidgetController($element, $scope, $filter) {
});
function initialize() {
vm.ctx.widgetActions = [ vm.searchAction ];
vm.showTimestamp = vm.settings.showTimestamp !== false;
var origColor = vm.widgetConfig.color || 'rgba(0, 0, 0, 0.87)';
var defaultColor = tinycolor(origColor);
@ -108,6 +137,8 @@ function TimeseriesTableWidgetController($element, $scope, $filter) {
cssParser.createStyleElement(namespace, cssString);
$element.addClass(namespace);
vm.displayPagination = angular.isDefined(vm.settings.displayPagination) ? vm.settings.displayPagination : true;
function hashCode(str) {
var hash = 0;
var i, char;
@ -163,7 +194,7 @@ function TimeseriesTableWidgetController($element, $scope, $filter) {
vm.cellContent = function(source, index, row, value) {
if (index === 0) {
return $filter('date')(value, 'yyyy-MM-dd HH:mm:ss');
return $filter('date')(value, dateFormatFilter);
} else {
var strContent = '';
if (angular.isDefined(value)) {
@ -211,7 +242,7 @@ function TimeseriesTableWidgetController($element, $scope, $filter) {
source.data = [];
source.rawData = [];
source.query = {
limit: 5,
limit: vm.settings.defaultPageSize || 10,
page: 1,
order: '-0'
}
@ -287,7 +318,30 @@ function TimeseriesTableWidgetController($element, $scope, $filter) {
}
function reorder(source) {
let searchRegExp = new RegExp(vm.query.search);
source.data = $filter('orderBy')(source.data, source.query.order);
if (vm.query.search !== null) {
source.data = source.data.filter(function(item){
for (let i = 0; i < item.length; i++) {
if (vm.showTimestamp) {
if (i === 0) {
if (searchRegExp.test($filter('date')(item[i], dateFormatFilter))) {
return true;
}
} else {
if (searchRegExp.test(item[i])) {
return true;
}
}
} else {
if (searchRegExp.test(item[i])) {
return true;
}
}
}
});
}
}
function convertData(data) {

4
ui/src/app/widget/lib/timeseries-table-widget.scss

@ -26,4 +26,8 @@ tb-timeseries-table-widget {
.md-table-pagination>* {
height: 46px;
}
.tb-data-table md-toolbar {
z-index: 10;
}
}

92
ui/src/app/widget/lib/timeseries-table-widget.tpl.html

@ -15,29 +15,71 @@
limitations under the License.
-->
<div class="tb-absolute-fill tb-entities-table tb-data-table timeseriesWidget" layout="column">
<div flex class="tb-absolute-fill" layout="column">
<md-toolbar class="md-table-toolbar md-default" ng-show="vm.query.search !== null">
<div class="md-toolbar-tools">
<md-button class="md-icon-button" aria-label="{{ 'action.search' | translate }}">
<md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">search</md-icon>
<md-tooltip md-direction="{{vm.ctx.dashboard.isWidgetExpanded ? 'bottom' : 'top'}}">
{{'entity.search' | translate}}
</md-tooltip>
</md-button>
<md-input-container flex>
<label>&nbsp;</label>
<input ng-model="vm.query.search" placeholder="{{'widget.search-data' | translate}}" md-autofocus/>
</md-input-container>
<md-button class="md-icon-button" aria-label="Close" ng-click="vm.exitFilterMode()">
<md-icon aria-label="Close" class="material-icons">close</md-icon>
<md-tooltip md-direction="{{vm.ctx.dashboard.isWidgetExpanded ? 'bottom' : 'top'}}">
{{ 'action.close' | translate }}
</md-tooltip>
</md-button>
</div>
</md-toolbar>
<md-tabs md-selected="vm.sourceIndex" ng-class="{'tb-headless': vm.sources.length === 1}"
id="tabs" md-border-bottom flex class="tb-absolute-fill">
<md-tab ng-repeat="source in vm.sources" label="{{ source.datasource.name }}">
<md-table-container>
<table md-table>
<thead md-head md-order="source.query.order" md-on-reorder="vm.onReorder(source)">
<tr md-row>
<th ng-show="vm.showTimestamp" md-column md-order-by="0"><span>Timestamp</span></th>
<th md-column md-order-by="{{ h.index }}" ng-repeat="h in source.ts.header"><span>{{ h.dataKey.label }}</span></th>
</tr>
</thead>
<tbody md-body>
<tr md-row ng-repeat="row in source.ts.data">
<td ng-show="$index > 0 || ($index === 0 && vm.showTimestamp)" md-cell ng-repeat="d in row track by $index" ng-style="vm.cellStyle(source, $index, d)" ng-bind-html="vm.cellContent(source, $index, row, d)">
</td>
</tr>
</tbody>
</table>
</md-table-container>
<md-table-pagination md-limit="source.query.limit" md-limit-options="[5, 10, 15]"
md-page="source.query.page" md-total="{{source.ts.count}}"
md-on-paginate="vm.onPaginate(source)" md-page-select>
</md-table-pagination>
</md-tab>
</md-tabs>
<md-tabs flex md-selected="vm.sourceIndex" ng-class="{'tb-headless': vm.sources.length === 1}"
id="tabs" md-border-bottom flex>
<md-tab ng-repeat="source in vm.sources" label="{{ source.datasource.name }}">
<md-table-container>
<table md-table>
<thead md-head md-order="source.query.order" md-on-reorder="vm.onReorder(source)">
<tr md-row>
<th ng-show="vm.showTimestamp"
md-column md-order-by="0"
>
<span>Timestamp</span>
</th>
<th md-column
md-order-by="{{ h.index }}"
ng-repeat="h in source.ts.header"
>
<span>{{ h.dataKey.label }}</span>
</th>
</tr>
</thead>
<tbody md-body>
<tr md-row ng-repeat="row in source.ts.data track by $index">
<td ng-show="$index > 0 || ($index === 0 && vm.showTimestamp)"
md-cell
ng-repeat="d in row track by $index"
ng-style="vm.cellStyle(source, $index, d)"
ng-bind-html="vm.cellContent(source, $index, row, d)"
></td>
</tr>
</tbody>
</table>
</md-table-container>
<md-table-pagination ng-if="vm.displayPagination"
md-limit="source.query.limit"
md-limit-options="vm.limitOptions"
md-page="source.query.page"
md-total="{{source.data.length}}"
md-on-paginate="vm.onPaginate(source)"
md-page-select>
</md-table-pagination>
</md-tab>
</md-tabs>
</div>
</div>
Loading…
Cancel
Save