Commit c66b06b8f71f240d064d76019090c4c4d078ac1c

Authored by Igor Kulikov
1 parent 7378973a

Minor UI bug fixes and improvements.

... ... @@ -295,9 +295,8 @@ function AlarmService($http, $q, $interval, $filter, $timeout, utils, types) {
295 295 if (alarmSourceListener.lastUpdateTs) {
296 296 var interval = now - alarmSourceListener.lastUpdateTs;
297 297 alarmSourceListener.alarmsQuery.startTime += interval;
298   - } else {
299   - alarmSourceListener.lastUpdateTs = now;
300 298 }
  299 + alarmSourceListener.lastUpdateTs = now;
301 300 }
302 301 alarmSourceListener.alarmsUpdated(alarms, false);
303 302 }
... ...
... ... @@ -63,7 +63,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
63 63 return deferred.promise;
64 64 }
65 65
66   - function getTenantDashboards(pageLink) {
  66 + function getTenantDashboards(pageLink, applyCustomersInfo) {
67 67 var deferred = $q.defer();
68 68 var url = '/api/tenant/dashboards?limit=' + pageLink.limit;
69 69 if (angular.isDefined(pageLink.textSearch)) {
... ... @@ -76,22 +76,26 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
76 76 url += '&textOffset=' + pageLink.textOffset;
77 77 }
78 78 $http.get(url, null).then(function success(response) {
79   - customerService.applyAssignedCustomersInfo(response.data.data).then(
80   - function success(data) {
81   - response.data.data = data;
82   - deferred.resolve(response.data);
83   - },
84   - function fail() {
85   - deferred.reject();
86   - }
87   - );
  79 + if (applyCustomersInfo) {
  80 + customerService.applyAssignedCustomersInfo(response.data.data).then(
  81 + function success(data) {
  82 + response.data.data = data;
  83 + deferred.resolve(response.data);
  84 + },
  85 + function fail() {
  86 + deferred.reject();
  87 + }
  88 + );
  89 + } else {
  90 + deferred.resolve(response.data);
  91 + }
88 92 }, function fail() {
89 93 deferred.reject();
90 94 });
91 95 return deferred.promise;
92 96 }
93 97
94   - function getCustomerDashboards(customerId, pageLink) {
  98 + function getCustomerDashboards(customerId, pageLink, applyCustomersInfo) {
95 99 var deferred = $q.defer();
96 100 var url = '/api/customer/' + customerId + '/dashboards?limit=' + pageLink.limit;
97 101 if (angular.isDefined(pageLink.textSearch)) {
... ... @@ -104,15 +108,19 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
104 108 url += '&textOffset=' + pageLink.textOffset;
105 109 }
106 110 $http.get(url, null).then(function success(response) {
107   - customerService.applyAssignedCustomerInfo(response.data.data, customerId).then(
108   - function success(data) {
109   - response.data.data = data;
110   - deferred.resolve(response.data);
111   - },
112   - function fail() {
113   - deferred.reject();
114   - }
115   - );
  111 + if (applyCustomersInfo) {
  112 + customerService.applyAssignedCustomerInfo(response.data.data, customerId).then(
  113 + function success(data) {
  114 + response.data.data = data;
  115 + deferred.resolve(response.data);
  116 + },
  117 + function fail() {
  118 + deferred.reject();
  119 + }
  120 + );
  121 + } else {
  122 + deferred.resolve(response.data);
  123 + }
116 124 }, function fail() {
117 125 deferred.reject();
118 126 });
... ...
... ... @@ -267,9 +267,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
267 267 break;
268 268 case types.entityType.dashboard:
269 269 if (user.authority === 'CUSTOMER_USER') {
270   - promise = dashboardService.getCustomerDashboards(customerId, pageLink);
  270 + promise = dashboardService.getCustomerDashboards(customerId, pageLink, false);
271 271 } else {
272   - promise = dashboardService.getTenantDashboards(pageLink);
  272 + promise = dashboardService.getTenantDashboards(pageLink, false);
273 273 }
274 274 break;
275 275 case types.entityType.user:
... ...
... ... @@ -674,11 +674,14 @@ export default class Subscription {
674 674
675 675 alarmsUpdated(alarms, apply) {
676 676 this.notifyDataLoaded();
  677 + var updated = !angular.equals(this.alarms, alarms);
677 678 this.alarms = alarms;
678 679 if (this.subscriptionTimewindow && this.subscriptionTimewindow.realtimeWindowMs) {
679 680 this.updateTimewindow();
680 681 }
681   - this.onDataUpdated(apply);
  682 + if (updated) {
  683 + this.onDataUpdated(apply);
  684 + }
682 685 }
683 686
684 687 updateLegend(dataIndex, data, apply) {
... ...
... ... @@ -265,9 +265,9 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi
265 265 var pageLink = {limit: 100};
266 266 var fetchDashboardsPromise;
267 267 if (currentUser.authority === 'TENANT_ADMIN') {
268   - fetchDashboardsPromise = dashboardService.getTenantDashboards(pageLink);
  268 + fetchDashboardsPromise = dashboardService.getTenantDashboards(pageLink, false);
269 269 } else {
270   - fetchDashboardsPromise = dashboardService.getCustomerDashboards(currentUser.customerId, pageLink);
  270 + fetchDashboardsPromise = dashboardService.getCustomerDashboards(currentUser.customerId, pageLink, false);
271 271 }
272 272 fetchDashboardsPromise.then(
273 273 function success(result) {
... ...
... ... @@ -48,7 +48,7 @@ function DashboardAutocomplete($compile, $templateCache, $q, dashboardService, u
48 48 var promise;
49 49 if (scope.dashboardsScope === 'customer' || userService.getAuthority() === 'CUSTOMER_USER') {
50 50 if (scope.customerId) {
51   - promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink);
  51 + promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, false);
52 52 } else {
53 53 promise = $q.when({data: []});
54 54 }
... ... @@ -60,7 +60,7 @@ function DashboardAutocomplete($compile, $templateCache, $q, dashboardService, u
60 60 promise = $q.when({data: []});
61 61 }
62 62 } else {
63   - promise = dashboardService.getTenantDashboards(pageLink);
  63 + promise = dashboardService.getTenantDashboards(pageLink, false);
64 64 }
65 65 }
66 66
... ...
... ... @@ -48,12 +48,12 @@ function DashboardSelect($compile, $templateCache, $q, $mdMedia, $mdPanel, $docu
48 48 var promise;
49 49 if (scope.dashboardsScope === 'customer' || userService.getAuthority() === 'CUSTOMER_USER') {
50 50 if (scope.customerId && scope.customerId != types.id.nullUid) {
51   - promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink);
  51 + promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, false);
52 52 } else {
53 53 promise = $q.when({data: []});
54 54 }
55 55 } else {
56   - promise = dashboardService.getTenantDashboards(pageLink);
  56 + promise = dashboardService.getTenantDashboards(pageLink, false);
57 57 }
58 58
59 59 promise.then(function success(result) {
... ...
... ... @@ -15,7 +15,6 @@
15 15 */
16 16 import './dashboard.scss';
17 17
18   -import $ from 'jquery';
19 18 import 'javascript-detect-element-resize/detect-element-resize';
20 19 import angularGridster from 'angular-gridster';
21 20 import thingsboardTypes from '../common/types.constant';
... ... @@ -94,8 +93,8 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
94 93 var highlightedWidget = null;
95 94 var selectedWidget = null;
96 95
97   - var gridsterParent = $('#gridster-parent', $element);
98   - var gridsterElement = angular.element($('#gridster-child', gridsterParent));
  96 + var gridsterParent = angular.element('#gridster-parent', $element);
  97 + var gridsterElement = angular.element('#gridster-child', gridsterParent);
99 98
100 99 var vm = this;
101 100
... ... @@ -228,15 +227,15 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
228 227 }
229 228 };
230 229
231   - addResizeListener(gridsterParent[0], onGirdsterParentResize); // eslint-disable-line no-undef
  230 + addResizeListener(gridsterParent[0], onGridsterParentResize); // eslint-disable-line no-undef
232 231
233 232 $scope.$on("$destroy", function () {
234   - removeResizeListener(gridsterParent[0], onGirdsterParentResize); // eslint-disable-line no-undef
  233 + removeResizeListener(gridsterParent[0], onGridsterParentResize); // eslint-disable-line no-undef
235 234 });
236 235
237 236 watchWidgets();
238 237
239   - function onGirdsterParentResize() {
  238 + function onGridsterParentResize() {
240 239 if (gridsterParent.height() && autofillHeight()) {
241 240 updateMobileOpts();
242 241 }
... ... @@ -244,6 +243,10 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
244 243
245 244 function watchWidgets() {
246 245 $scope.widgetsCollectionWatch = $scope.$watchCollection('vm.widgets', function () {
  246 + if (vm.skipInitialWidgetsWatch) {
  247 + $timeout(function() { vm.skipInitialWidgetsWatch = false; });
  248 + return;
  249 + }
247 250 var ids = [];
248 251 for (var i=0;i<vm.widgets.length;i++) {
249 252 var widget = vm.widgets[i];
... ... @@ -524,6 +527,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
524 527 }
525 528 return res;
526 529 });
  530 + vm.skipInitialWidgetsWatch = true;
527 531 watchWidgets();
528 532 }
529 533
... ... @@ -714,9 +718,9 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
714 718
715 719 function scrollToWidget(widget, delay) {
716 720 if (vm.gridster) {
717   - var item = $('.gridster-item', vm.gridster.$element)[vm.widgets.indexOf(widget)];
  721 + var item = angular.element('.gridster-item', vm.gridster.$element)[vm.widgets.indexOf(widget)];
718 722 if (item) {
719   - var height = $(item).outerHeight(true);
  723 + var height = angular.element(item).outerHeight(true);
720 724 var rectHeight = gridsterParent.height();
721 725 var offset = (rectHeight - height) / 2;
722 726 var scrollTop = item.offsetTop;
... ...
... ... @@ -55,7 +55,7 @@
55 55 ng-show="!vm.isEdit"
56 56 ng-click="action.onAction($event)"
57 57 class="md-icon-button">
58   - <md-tooltip md-direction="top">
  58 + <md-tooltip md-direction="{{vm.isWidgetExpanded ? 'bottom' : 'top'}}">
59 59 {{action.displayName}}
60 60 </md-tooltip>
61 61 <ng-md-icon size="20" icon="{{action.icon}}"></ng-md-icon>
... ... @@ -65,7 +65,7 @@
65 65 ng-show="!vm.isEdit && action.show"
66 66 ng-click="action.onAction($event)"
67 67 class="md-icon-button">
68   - <md-tooltip md-direction="top">
  68 + <md-tooltip md-direction="{{vm.isWidgetExpanded ? 'bottom' : 'top'}}">
69 69 {{ action.name | translate }}
70 70 </md-tooltip>
71 71 <ng-md-icon size="20" icon="{{action.icon}}"></ng-md-icon>
... ... @@ -114,7 +114,8 @@
114 114 isEdit: vm.isEdit,
115 115 isMobile: vm.isMobileSize,
116 116 dashboardTimewindow: vm.dashboardTimewindow,
117   - dashboardTimewindowApi: vm.dashboardTimewindowApi }">
  117 + dashboardTimewindowApi: vm.dashboardTimewindowApi,
  118 + dashboard: vm }">
118 119 </div>
119 120 </div>
120 121 </div>
... ...
... ... @@ -186,7 +186,7 @@
186 186 <div translate ng-message="entityKeys" ng-if="widgetType === types.widgetType.latest.value" class="tb-error-message">datakey.timeseries-or-attributes-required</div>
187 187 <div translate ng-message="entityKeys" ng-if="widgetType === types.widgetType.alarm.value" class="tb-error-message">datakey.alarm-fields-required</div>
188 188 </div>
189   - <div class="md-caption" style="color: rgba(0,0,0,0.57);" ng-if="maxDataKeys != -1"
  189 + <div class="md-caption" style="color: rgba(0,0,0,0.57);" ng-if="maxDataKeys > -1"
190 190 translate="datakey.maximum-timeseries-or-attributes"
191 191 translate-values="{count: maxDataKeys}"
192 192 translate-interpolation="messageformat"
... ...
... ... @@ -132,7 +132,7 @@
132 132 <div translate ng-message="datasourceKeys" ng-if="widgetType !== types.widgetType.alarm.value" class="tb-error-message">datakey.function-types-required</div>
133 133 <div translate ng-message="datasourceKeys" ng-if="widgetType === types.widgetType.alarm.value" class="tb-error-message">datakey.alarm-fields-required</div>
134 134 </div>
135   - <div class="md-caption" style="color: rgba(0,0,0,0.57);" ng-if="maxDataKeys != -1"
  135 + <div class="md-caption" style="color: rgba(0,0,0,0.57);" ng-if="maxDataKeys > -1"
136 136 translate="datakey.maximum-function-types"
137 137 translate-values="{count: maxDataKeys}"
138 138 translate-interpolation="messageformat"
... ...
... ... @@ -22,7 +22,7 @@ import Subscription from '../../api/subscription';
22 22 /*@ngInject*/
23 23 export default function WidgetController($scope, $state, $timeout, $window, $element, $q, $log, $injector, $filter, $compile, tbRaf, types, utils, timeService,
24 24 datasourceService, alarmService, entityService, dashboardService, deviceService, visibleRect, isEdit, isMobile, dashboardTimewindow,
25   - dashboardTimewindowApi, widget, aliasController, stateController, widgetInfo, widgetType) {
  25 + dashboardTimewindowApi, dashboard, widget, aliasController, stateController, widgetInfo, widgetType) {
26 26
27 27 var vm = this;
28 28
... ... @@ -67,6 +67,7 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele
67 67 hideTitlePanel: false,
68 68 isEdit: isEdit,
69 69 isMobile: isMobile,
  70 + dashboard: dashboard,
70 71 widgetConfig: widget.config,
71 72 settings: widget.config.settings,
72 73 units: widget.config.units || '',
... ...
... ... @@ -52,7 +52,7 @@ export default function AddDashboardsToCustomerController(dashboardService, $mdD
52 52 fetchMoreItems_: function () {
53 53 if (vm.dashboards.hasNext && !vm.dashboards.pending) {
54 54 vm.dashboards.pending = true;
55   - dashboardService.getTenantDashboards(vm.dashboards.nextPageLink).then(
  55 + dashboardService.getTenantDashboards(vm.dashboards.nextPageLink, false).then(
56 56 function success(dashboards) {
57 57 vm.dashboards.data = vm.dashboards.data.concat(dashboards.data);
58 58 vm.dashboards.nextPageLink = dashboards.nextPageLink;
... ...
... ... @@ -15,7 +15,7 @@
15 15 limitations under the License.
16 16
17 17 -->
18   -<md-content flex tb-expand-fullscreen="vm.widgetEditMode || vm.iframeMode || forceFullscreen" expand-button-id="dashboard-expand-button"
  18 +<md-content style="padding-top: 150px;" flex tb-expand-fullscreen="vm.widgetEditMode || vm.iframeMode || forceFullscreen" expand-button-id="dashboard-expand-button"
19 19 hide-expand-button="vm.widgetEditMode || vm.iframeMode || forceFullscreen" expand-tooltip-direction="bottom" ng-if="vm.dashboard">
20 20 <section class="tb-dashboard-toolbar" ng-show="vm.showDashboardToolbar()"
21 21 ng-class="{ 'tb-dashboard-toolbar-opened': vm.toolbarOpened, 'tb-dashboard-toolbar-closed': !vm.toolbarOpened }">
... ...
... ... @@ -167,7 +167,7 @@ export function DashboardsController(userService, dashboardService, customerServ
167 167
168 168 if (vm.dashboardsScope === 'tenant') {
169 169 fetchDashboardsFunction = function (pageLink) {
170   - return dashboardService.getTenantDashboards(pageLink);
  170 + return dashboardService.getTenantDashboards(pageLink, true);
171 171 };
172 172 deleteDashboardFunction = function (dashboardId) {
173 173 return dashboardService.deleteDashboard(dashboardId);
... ... @@ -290,7 +290,7 @@ export function DashboardsController(userService, dashboardService, customerServ
290 290 });
291 291 } else if (vm.dashboardsScope === 'customer' || vm.dashboardsScope === 'customer_user') {
292 292 fetchDashboardsFunction = function (pageLink) {
293   - return dashboardService.getCustomerDashboards(customerId, pageLink);
  293 + return dashboardService.getCustomerDashboards(customerId, pageLink, true);
294 294 };
295 295 deleteDashboardFunction = function (dashboardId) {
296 296 return dashboardService.unassignDashboardFromCustomer(dashboardId);
... ... @@ -468,7 +468,7 @@ export function DashboardsController(userService, dashboardService, customerServ
468 468 $event.stopPropagation();
469 469 }
470 470 var pageSize = 10;
471   - dashboardService.getTenantDashboards({limit: pageSize, textSearch: ''}).then(
  471 + dashboardService.getTenantDashboards({limit: pageSize, textSearch: ''}, false).then(
472 472 function success(_dashboards) {
473 473 var dashboards = {
474 474 pageSize: pageSize,
... ...
... ... @@ -22,18 +22,22 @@ export default function BreadcrumbLabel($translate) {
22 22 var labelObj;
23 23 labelObj = angular.fromJson(bLabel);
24 24 if (labelObj) {
  25 + var translate = !(labelObj.translate && labelObj.translate === 'false');
  26 + var key = translate ? $translate.use() : 'orig';
25 27 if (!labels[labelObj.label]) {
26   - labels[labelObj.label] = labelObj.label;
27   - var translate = !(labelObj.translate && labelObj.translate === 'false');
  28 + labels[labelObj.label] = {};
  29 + }
  30 + if (!labels[labelObj.label][key]) {
  31 + labels[labelObj.label][key] = labelObj.label;
28 32 if (translate) {
29 33 $translate([labelObj.label]).then(
30 34 function (translations) {
31   - labels[labelObj.label] = translations[labelObj.label];
  35 + labels[labelObj.label][key] = translations[labelObj.label];
32 36 }
33 37 )
34 38 }
35 39 }
36   - return labels[labelObj.label];
  40 + return labels[labelObj.label][key];
37 41 } else {
38 42 return '';
39 43 }
... ...
... ... @@ -22,7 +22,7 @@
22 22 <div class="md-toolbar-tools">
23 23 <md-button class="md-icon-button" aria-label="{{ 'action.search' | translate }}">
24 24 <md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">search</md-icon>
25   - <md-tooltip md-direction="top">
  25 + <md-tooltip md-direction="{{vm.ctx.dashboard.isWidgetExpanded ? 'bottom' : 'top'}}">
26 26 {{'alarm.search' | translate}}
27 27 </md-tooltip>
28 28 </md-button>
... ... @@ -32,7 +32,7 @@
32 32 </md-input-container>
33 33 <md-button class="md-icon-button" aria-label="Close" ng-click="vm.exitFilterMode()">
34 34 <md-icon aria-label="Close" class="material-icons">close</md-icon>
35   - <md-tooltip md-direction="top">
  35 + <md-tooltip md-direction="{{vm.ctx.dashboard.isWidgetExpanded ? 'bottom' : 'top'}}">
36 36 {{ 'action.close' | translate }}
37 37 </md-tooltip>
38 38 </md-button>
... ... @@ -46,13 +46,13 @@
46 46 <span flex></span>
47 47 <md-button ng-if="vm.allowAcknowledgment" class="md-icon-button" ng-click="vm.ackAlarms($event)">
48 48 <md-icon>done</md-icon>
49   - <md-tooltip md-direction="top">
  49 + <md-tooltip md-direction="{{vm.ctx.dashboard.isWidgetExpanded ? 'bottom' : 'top'}}">
50 50 {{ 'alarm.acknowledge' | translate }}
51 51 </md-tooltip>
52 52 </md-button>
53 53 <md-button ng-if="vm.allowClear" class="md-icon-button" ng-click="vm.clearAlarms($event)">
54 54 <md-icon>clear</md-icon>
55   - <md-tooltip md-direction="top">
  55 + <md-tooltip md-direction="{{vm.ctx.dashboard.isWidgetExpanded ? 'bottom' : 'top'}}">
56 56 {{ 'alarm.clear' | translate }}
57 57 </md-tooltip>
58 58 </md-button>
... ...
... ... @@ -17,6 +17,8 @@ import $ from 'jquery';
17 17 import canvasGauges from 'canvas-gauges';
18 18 /*import tinycolor from 'tinycolor2';*/
19 19
  20 +/* eslint-disable angular/angularelement */
  21 +
20 22 export default class TbAnalogueCompass {
21 23 constructor(ctx, canvasId) {
22 24 this.ctx = ctx;
... ... @@ -436,3 +438,5 @@ export default class TbAnalogueCompass {
436 438 };
437 439 }
438 440 }
  441 +
  442 +/* eslint-enable angular/angularelement */
... ...
... ... @@ -21,7 +21,7 @@
21 21 <div class="md-toolbar-tools">
22 22 <md-button class="md-icon-button" aria-label="{{ 'action.search' | translate }}">
23 23 <md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">search</md-icon>
24   - <md-tooltip md-direction="top">
  24 + <md-tooltip md-direction="{{vm.ctx.dashboard.isWidgetExpanded ? 'bottom' : 'top'}}">
25 25 {{'entity.search' | translate}}
26 26 </md-tooltip>
27 27 </md-button>
... ... @@ -31,7 +31,7 @@
31 31 </md-input-container>
32 32 <md-button class="md-icon-button" aria-label="Close" ng-click="vm.exitFilterMode()">
33 33 <md-icon aria-label="Close" class="material-icons">close</md-icon>
34   - <md-tooltip md-direction="top">
  34 + <md-tooltip md-direction="{{vm.ctx.dashboard.isWidgetExpanded ? 'bottom' : 'top'}}">
35 35 {{ 'action.close' | translate }}
36 36 </md-tooltip>
37 37 </md-button>
... ...
... ... @@ -275,9 +275,11 @@ export default class TbMapWidgetV2 {
275 275 for (var i = 0; i < latData.length; i++) {
276 276 lat = latData[i][1];
277 277 lng = lngData[i][1];
278   - latLng = tbMap.map.createLatLng(lat, lng);
279   - if (i == 0 || !latLngs[latLngs.length - 1].equals(latLng)) {
280   - latLngs.push(latLng);
  278 + if (angular.isDefined(lat) && lat != null && angular.isDefined(lng) && lng != null) {
  279 + latLng = tbMap.map.createLatLng(lat, lng);
  280 + if (i == 0 || !latLngs[latLngs.length - 1].equals(latLng)) {
  281 + latLngs.push(latLng);
  282 + }
281 283 }
282 284 }
283 285 if (latLngs.length > 0) {
... ... @@ -308,24 +310,28 @@ export default class TbMapWidgetV2 {
308 310 // Create or update marker
309 311 lat = latData[latData.length - 1][1];
310 312 lng = lngData[lngData.length - 1][1];
311   - latLng = tbMap.map.createLatLng(lat, lng);
312   - if (!location.marker) {
313   - location.marker = tbMap.map.createMarker(latLng, location.settings,
314   - function (event) {
315   - tbMap.callbacks.onLocationClick(location);
316   - locationRowClick(event, location);
317   - }, [location.dsIndex]);
318   - tbMap.markers.push(location.marker);
319   - locationChanged = true;
320   - } else {
321   - var prevPosition = tbMap.map.getMarkerPosition(location.marker);
322   - if (!prevPosition.equals(latLng)) {
323   - tbMap.map.setMarkerPosition(location.marker, latLng);
  313 + if (angular.isDefined(lat) && lat != null && angular.isDefined(lng) && lng != null) {
  314 + latLng = tbMap.map.createLatLng(lat, lng);
  315 + if (!location.marker) {
  316 + location.marker = tbMap.map.createMarker(latLng, location.settings,
  317 + function (event) {
  318 + tbMap.callbacks.onLocationClick(location);
  319 + locationRowClick(event, location);
  320 + }, [location.dsIndex]);
  321 + tbMap.markers.push(location.marker);
324 322 locationChanged = true;
  323 + } else {
  324 + var prevPosition = tbMap.map.getMarkerPosition(location.marker);
  325 + if (!prevPosition.equals(latLng)) {
  326 + tbMap.map.setMarkerPosition(location.marker, latLng);
  327 + locationChanged = true;
  328 + }
325 329 }
326 330 }
327 331 }
328   - updateLocationStyle(location, dataMap);
  332 + if (location.marker) {
  333 + updateLocationStyle(location, dataMap);
  334 + }
329 335 }
330 336 }
331 337 return locationChanged;
... ...