thingsboard-aplcache

Details

diff --git a/ui/src/app/api/alias-controller.js b/ui/src/app/api/alias-controller.js
index fd970c0..3c5f786 100644
--- a/ui/src/app/api/alias-controller.js
+++ b/ui/src/app/api/alias-controller.js
@@ -78,6 +78,16 @@ export default class AliasController {
         return this.entityAliases;
     }
 
+    getEntityAliasId(aliasName) {
+        for (var aliasId in this.entityAliases) {
+            var alias = this.entityAliases[aliasId];
+            if (alias.alias == aliasName) {
+                return aliasId;
+            }
+        }
+        return null;
+    }
+
     getAliasInfo(aliasId) {
         var deferred = this.$q.defer();
         var aliasInfo = this.resolvedAliases[aliasId];
diff --git a/ui/src/app/components/widget/widget.controller.js b/ui/src/app/components/widget/widget.controller.js
index 03f1b96..8dde1fa 100644
--- a/ui/src/app/components/widget/widget.controller.js
+++ b/ui/src/app/components/widget/widget.controller.js
@@ -123,7 +123,8 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele
             getActionDescriptors: getActionDescriptors,
             handleWidgetAction: handleWidgetAction
         },
-        stateController: stateController
+        stateController: stateController,
+        aliasController: aliasController
     };
 
     widgetContext.customHeaderActions = [];
diff --git a/ui/src/app/widget/lib/image-map.js b/ui/src/app/widget/lib/image-map.js
index 089e9f4..e3b5162 100644
--- a/ui/src/app/widget/lib/image-map.js
+++ b/ui/src/app/widget/lib/image-map.js
@@ -26,7 +26,7 @@ const pinSvg = `<svg class="image-map-pin-image" xmlns="http://www.w3.org/2000/s
 
 export default class TbImageMap {
 
-    constructor(ctx, $containerElement, initCallback, imageUrl, posFunction) {
+    constructor(ctx, $containerElement, initCallback, imageUrl, posFunction, imageEntityAlias, imageUrlAttribute) {
 
         this.ctx = ctx;
         this.tooltips = [];
@@ -39,12 +39,7 @@ export default class TbImageMap {
         this.width = 0;
         this.height = 0;
         this.markers = [];
-
-        if (!imageUrl) {
-            imageUrl = 'data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg==';
-        }
-
-        this.imageMap.css({backgroundImage: 'url('+imageUrl+')'});
+        this.initCallback = initCallback;
 
         if (angular.isDefined(posFunction) && posFunction.length > 0) {
             try {
@@ -57,6 +52,76 @@ export default class TbImageMap {
             this.posFunction = (origXPos, origYPos) => {return {x: origXPos, y: origYPos}};
         }
 
+        if (!this.subscribeForImageAttribute(imageEntityAlias, imageUrlAttribute)) {
+            this.loadImage(imageUrl, initCallback);
+        }
+    }
+
+    subscribeForImageAttribute(imageEntityAlias, imageUrlAttribute) {
+        if (!imageEntityAlias || !imageEntityAlias.length ||
+            !imageUrlAttribute || !imageUrlAttribute.length) {
+            return false;
+        }
+        var entityAliasId = this.ctx.aliasController.getEntityAliasId(imageEntityAlias);
+        if (!entityAliasId) {
+            return false;
+        }
+        var types = this.ctx.$scope.$injector.get('types');
+        var datasources = [
+            {
+                type: types.datasourceType.entity,
+                name: imageEntityAlias,
+                aliasName: imageEntityAlias,
+                entityAliasId: entityAliasId,
+                dataKeys: [
+                    {
+                        type: types.dataKeyType.attribute,
+                        name: imageUrlAttribute,
+                        label: imageUrlAttribute,
+                        settings: {},
+                        _hash: Math.random()
+                    }
+                ]
+            }
+        ];
+        var imageMap = this;
+        var imageUrlSubscriptionOptions = {
+            datasources: datasources,
+            useDashboardTimewindow: false,
+            type: types.widgetType.latest.value,
+            callbacks: {
+                onDataUpdated: (subscription, apply) => {imageMap.imageUrlDataUpdated(subscription, apply)}
+            }
+        };
+        this.ctx.subscriptionApi.createSubscription(imageUrlSubscriptionOptions, true).then(
+            (subscription) => {
+                imageMap.imageUrlSubscription = subscription;
+            }
+        );
+        return true;
+    }
+
+    imageUrlDataUpdated(subscription, apply) {
+        var data = subscription.data;
+        if (data.length) {
+            var keyData = data[0];
+            if (keyData && keyData.data && keyData.data[0]) {
+                var attrValue = keyData.data[0][1];
+                if (attrValue && attrValue.length) {
+                    this.loadImage(attrValue, this.aspect > 0 ? null : this.initCallback);
+                }
+            }
+        }
+        if (apply) {
+            this.ctx.$scope.$digest();
+        }
+    }
+
+    loadImage(imageUrl, initCallback) {
+        if (!imageUrl) {
+            imageUrl = 'data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg==';
+        }
+        this.imageMap.css({backgroundImage: 'url('+imageUrl+')'});
         var imageMap = this;
         var testImage = document.createElement('img'); // eslint-disable-line
         testImage.style.visibility = 'hidden';
@@ -66,6 +131,8 @@ export default class TbImageMap {
             imageMap.onresize();
             if (initCallback) {
                 setTimeout(initCallback, 0); //eslint-disable-line
+            } else {
+                imageMap.onresize();
             }
         }
         document.body.appendChild(testImage); //eslint-disable-line
diff --git a/ui/src/app/widget/lib/map-widget2.js b/ui/src/app/widget/lib/map-widget2.js
index 87a0cd5..7eae9f3 100644
--- a/ui/src/app/widget/lib/map-widget2.js
+++ b/ui/src/app/widget/lib/map-widget2.js
@@ -79,7 +79,11 @@ export default class TbMapWidgetV2 {
         } else if (mapProvider === 'openstreet-map') {
             this.map = new TbOpenStreetMap($element, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel);
         } else if (mapProvider === 'image-map') {
-            this.map = new TbImageMap(this.ctx, $element, initCallback, settings.mapImageUrl, settings.posFunction);
+            this.map = new TbImageMap(this.ctx, $element, initCallback,
+                settings.mapImageUrl,
+                settings.posFunction,
+                settings.imageEntityAlias,
+                settings.imageUrlAttribute);
         }
     }
 
@@ -703,6 +707,16 @@ const imageMapSettingsSchema =
                 "type": "string",
                 "default": "data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg=="
             },
+            "imageEntityAlias": {
+                "title": "Image URL source entity alias",
+                "type": "string",
+                "default": ""
+            },
+            "imageUrlAttribute": {
+                "title": "Image URL source entity attribute",
+                "type": "string",
+                "default": ""
+            },
             "xPosKeyName":{
                 "title":"X position key name",
                 "type":"string",
@@ -783,13 +797,15 @@ const imageMapSettingsSchema =
                 }
             }
         },
-        "required":["mapImageUrl"]
+        "required":[]
     },
     "form":[
         {
             "key": "mapImageUrl",
             "type": "image"
         },
+        "imageEntityAlias",
+        "imageUrlAttribute",
         "xPosKeyName",
         "yPosKeyName",
         "showLabel",