/*
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import $ from 'jquery';
import 'javascript-detect-element-resize/detect-element-resize';
import Subscription from '../../api/subscription';
/* eslint-disable angular/angularelement */
/*@ngInject*/
export default function WidgetController($scope, $state, $timeout, $window, $element, $q, $log, $injector, $filter, $compile, tbRaf, types, utils, timeService,
datasourceService, alarmService, entityService, dashboardService, deviceService, visibleRect, isEdit, isMobile, dashboardTimewindow,
dashboardTimewindowApi, dashboard, widget, aliasController, stateController, widgetInfo, widgetType) {
var vm = this;
$scope.$timeout = $timeout;
$scope.$q = $q;
$scope.$injector = $injector;
$scope.tbRaf = tbRaf;
$scope.rpcRejection = null;
$scope.rpcErrorText = null;
$scope.rpcEnabled = false;
$scope.executingRpcRequest = false;
vm.dashboardTimewindow = dashboardTimewindow;
var gridsterItemInited = false;
var subscriptionInited = false;
var widgetSizeDetected = false;
var cafs = {};
var actionDescriptorsBySourceId = {};
if (widget.config.actions) {
for (var actionSourceId in widget.config.actions) {
var descriptors = widget.config.actions[actionSourceId];
var actionDescriptors = [];
descriptors.forEach(function(descriptor) {
var actionDescriptor = angular.copy(descriptor);
actionDescriptor.displayName = utils.customTranslation(descriptor.name, descriptor.name);
actionDescriptors.push(actionDescriptor);
});
actionDescriptorsBySourceId[actionSourceId] = actionDescriptors;
}
}
var widgetContext = {
inited: false,
$container: null,
$containerParent: null,
width: 0,
height: 0,
hideTitlePanel: false,
isEdit: isEdit,
isMobile: isMobile,
dashboard: dashboard,
widgetConfig: widget.config,
settings: widget.config.settings,
units: widget.config.units || '',
decimals: angular.isDefined(widget.config.decimals) ? widget.config.decimals : 2,
subscriptions: {},
defaultSubscription: null,
dashboardTimewindow: dashboardTimewindow,
timewindowFunctions: {
onUpdateTimewindow: function(startTimeMs, endTimeMs) {
if (widgetContext.defaultSubscription) {
widgetContext.defaultSubscription.onUpdateTimewindow(startTimeMs, endTimeMs);
}
},
onResetTimewindow: function() {
if (widgetContext.defaultSubscription) {
widgetContext.defaultSubscription.onResetTimewindow();
}
}
},
subscriptionApi: {
createSubscription: function(options, subscribe) {
return createSubscription(options, subscribe);
},
createSubscriptionFromInfo: function (type, subscriptionsInfo, options, useDefaultComponents, subscribe) {
return createSubscriptionFromInfo(type, subscriptionsInfo, options, useDefaultComponents, subscribe);
},
removeSubscription: function(id) {
var subscription = widgetContext.subscriptions[id];
if (subscription) {
subscription.destroy();
delete widgetContext.subscriptions[id];
}
}
},
controlApi: {
sendOneWayCommand: function(method, params, timeout) {
if (widgetContext.defaultSubscription) {
return widgetContext.defaultSubscription.sendOneWayCommand(method, params, timeout);
}
return null;
},
sendTwoWayCommand: function(method, params, timeout) {
if (widgetContext.defaultSubscription) {
return widgetContext.defaultSubscription.sendTwoWayCommand(method, params, timeout);
}
return null;
}
},
utils: {
formatValue: formatValue
},
actionsApi: {
actionDescriptorsBySourceId: actionDescriptorsBySourceId,
getActionDescriptors: getActionDescriptors,
handleWidgetAction: handleWidgetAction
},
stateController: stateController,
aliasController: aliasController
};
widgetContext.customHeaderActions = [];
var headerActionsDescriptors = getActionDescriptors(types.widgetActionSources.headerButton.value);
for (var i=0;i<headerActionsDescriptors.length;i++) {
var descriptor = headerActionsDescriptors[i];
var headerAction = {};
headerAction.name = descriptor.name;
headerAction.displayName = descriptor.displayName;
headerAction.icon = descriptor.icon;
headerAction.descriptor = descriptor;
headerAction.onAction = function($event) {
var entityInfo = getFirstEntityInfo();
var entityId = entityInfo ? entityInfo.entityId : null;
var entityName = entityInfo ? entityInfo.entityName : null;
handleWidgetAction($event, this.descriptor, entityId, entityName);
}
widgetContext.customHeaderActions.push(headerAction);
}
var subscriptionContext = {
$scope: $scope,
$q: $q,
$filter: $filter,
$timeout: $timeout,
tbRaf: tbRaf,
timeService: timeService,
deviceService: deviceService,
datasourceService: datasourceService,
alarmService: alarmService,
utils: utils,
widgetUtils: widgetContext.utils,
dashboardTimewindowApi: dashboardTimewindowApi,
types: types,
getStDiff: dashboardService.getServerTimeDiff,
aliasController: aliasController
};
widget.$ctx = function() {
return widgetContext;
}
var widgetTypeInstance;
vm.typeParameters = widgetInfo.typeParameters;
try {
widgetTypeInstance = new widgetType(widgetContext);
} catch (e) {
handleWidgetException(e);
widgetTypeInstance = {};
}
if (!widgetTypeInstance.onInit) {
widgetTypeInstance.onInit = function() {};
}
if (!widgetTypeInstance.onDataUpdated) {
widgetTypeInstance.onDataUpdated = function() {};
}
if (!widgetTypeInstance.onResize) {
widgetTypeInstance.onResize = function() {};
}
if (!widgetTypeInstance.onEditModeChanged) {
widgetTypeInstance.onEditModeChanged = function() {};
}
if (!widgetTypeInstance.onMobileModeChanged) {
widgetTypeInstance.onMobileModeChanged = function() {};
}
if (!widgetTypeInstance.onDestroy) {
widgetTypeInstance.onDestroy = function() {};
}
//TODO: widgets visibility
//var bounds = {top: 0, left: 0, bottom: 0, right: 0};
/*var visible = false;*/
/*vm.visibleRectChanged = visibleRectChanged;
function visibleRectChanged(newVisibleRect) {
visibleRect = newVisibleRect;
updateVisibility();
}*/
$scope.clearRpcError = function() {
if (widgetContext.defaultSubscription) {
widgetContext.defaultSubscription.clearRpcError();
}
}
vm.gridsterItemInitialized = gridsterItemInitialized;
initialize().then(
function(){
onInit();
}
);
function createSubscriptionFromInfo(type, subscriptionsInfo, options, useDefaultComponents, subscribe) {
var deferred = $q.defer();
options.type = type;
if (useDefaultComponents) {
defaultComponentsOptions(options);
} else {
if (!options.timeWindowConfig) {
options.useDashboardTimewindow = true;
}
}
var createDatasourcesPromise;
if (options.type == types.widgetType.alarm.value) {
createDatasourcesPromise = entityService.createAlarmSourceFromSubscriptionInfo(subscriptionsInfo);
} else {
createDatasourcesPromise = entityService.createDatasourcesFromSubscriptionsInfo(subscriptionsInfo);
}
createDatasourcesPromise.then(
function (result) {
if (options.type == types.widgetType.alarm.value) {
options.alarmSource = result;
} else {
options.datasources = result;
}
createSubscription(options, subscribe).then(
function success(subscription) {
if (useDefaultComponents) {
defaultSubscriptionOptions(subscription, options);
}
deferred.resolve(subscription);
},
function fail() {
deferred.reject();
}
);
}
);
return deferred.promise;
}
function createSubscription(options, subscribe) {
var deferred = $q.defer();
options.dashboardTimewindow = vm.dashboardTimewindow;
new Subscription(subscriptionContext, options).then(
function success(subscription) {
widgetContext.subscriptions[subscription.id] = subscription;
if (subscribe) {
subscription.subscribe();
}
deferred.resolve(subscription);
},
function fail() {
deferred.reject();
}
);
return deferred.promise;
}
function defaultComponentsOptions(options) {
options.useDashboardTimewindow = angular.isDefined(widget.config.useDashboardTimewindow)
? widget.config.useDashboardTimewindow : true;
options.timeWindowConfig = options.useDashboardTimewindow ? vm.dashboardTimewindow : widget.config.timewindow;
options.legendConfig = null;
if ($scope.displayLegend) {
options.legendConfig = $scope.legendConfig;
}
options.decimals = widgetContext.decimals;
options.units = widgetContext.units;
options.callbacks = {
onDataUpdated: function() {
widgetTypeInstance.onDataUpdated();
},
onDataUpdateError: function(subscription, e) {
handleWidgetException(e);
},
dataLoading: function(subscription) {
if ($scope.loadingData !== subscription.loadingData) {
$scope.loadingData = subscription.loadingData;
}
},
legendDataUpdated: function(subscription, apply) {
if (apply) {
$scope.$digest();
}
},
timeWindowUpdated: function(subscription, timeWindowConfig) {
widget.config.timewindow = timeWindowConfig;
$scope.$apply();
}
}
}
function defaultSubscriptionOptions(subscription, options) {
if (!options.useDashboardTimewindow) {
$scope.$watch(function () {
return widget.config.timewindow;
}, function (newTimewindow, prevTimewindow) {
if (!angular.equals(newTimewindow, prevTimewindow)) {
subscription.updateTimewindowConfig(widget.config.timewindow);
}
});
}
if ($scope.displayLegend) {
$scope.legendData = subscription.legendData;
}
}
function createDefaultSubscription() {
var options;
var deferred = $q.defer();
if (widget.type !== types.widgetType.rpc.value && widget.type !== types.widgetType.static.value) {
options = {
type: widget.type,
stateData: vm.typeParameters.stateData
}
if (widget.type == types.widgetType.alarm.value) {
options.alarmSource = angular.copy(widget.config.alarmSource);
options.alarmSearchStatus = angular.isDefined(widget.config.alarmSearchStatus) ?
widget.config.alarmSearchStatus : types.alarmSearchStatus.any;
options.alarmsPollingInterval = angular.isDefined(widget.config.alarmsPollingInterval) ?
widget.config.alarmsPollingInterval * 1000 : 5000;
} else {
options.datasources = angular.copy(widget.config.datasources)
}
defaultComponentsOptions(options);
createSubscription(options).then(
function success(subscription) {
defaultSubscriptionOptions(subscription, options);
// backward compatibility
widgetContext.datasources = subscription.datasources;
widgetContext.data = subscription.data;
widgetContext.hiddenData = subscription.hiddenData;
widgetContext.timeWindow = subscription.timeWindow;
widgetContext.defaultSubscription = subscription;
deferred.resolve();
},
function fail() {
deferred.reject();
}
);
} else if (widget.type === types.widgetType.rpc.value) {
$scope.loadingData = false;
options = {
type: widget.type,
targetDeviceAliasIds: widget.config.targetDeviceAliasIds
}
options.callbacks = {
rpcStateChanged: function(subscription) {
$scope.rpcEnabled = subscription.rpcEnabled;
$scope.executingRpcRequest = subscription.executingRpcRequest;
},
onRpcSuccess: function(subscription) {
$scope.executingRpcRequest = subscription.executingRpcRequest;
$scope.rpcErrorText = subscription.rpcErrorText;
$scope.rpcRejection = subscription.rpcRejection;
},
onRpcFailed: function(subscription) {
$scope.executingRpcRequest = subscription.executingRpcRequest;
$scope.rpcErrorText = subscription.rpcErrorText;
$scope.rpcRejection = subscription.rpcRejection;
},
onRpcErrorCleared: function() {
$scope.rpcErrorText = null;
$scope.rpcRejection = null;
}
}
createSubscription(options).then(
function success(subscription) {
widgetContext.defaultSubscription = subscription;
deferred.resolve();
},
function fail() {
deferred.reject();
}
);
} else if (widget.type === types.widgetType.static.value) {
$scope.loadingData = false;
deferred.resolve();
} else {
deferred.resolve();
}
return deferred.promise;
}
function getActionDescriptors(actionSourceId) {
var result = widgetContext.actionsApi.actionDescriptorsBySourceId[actionSourceId];
if (!result) {
result = [];
}
return result;
}
function updateEntityParams(params, targetEntityParamName, targetEntityId, entityName) {
if (targetEntityId) {
var targetEntityParams;
if (targetEntityParamName && targetEntityParamName.length) {
targetEntityParams = params[targetEntityParamName];
if (!targetEntityParams) {
targetEntityParams = {};
params[targetEntityParamName] = targetEntityParams;
}
} else {
targetEntityParams = params;
}
targetEntityParams.entityId = targetEntityId;
if (entityName) {
targetEntityParams.entityName = entityName;
}
}
}
function handleWidgetAction($event, descriptor, entityId, entityName, additionalParams) {
var type = descriptor.type;
var targetEntityParamName = descriptor.stateEntityParamName;
var targetEntityId;
if (descriptor.setEntityId) {
targetEntityId = entityId;
}
switch (type) {
case types.widgetActionTypes.openDashboardState.value:
case types.widgetActionTypes.updateDashboardState.value:
var targetDashboardStateId = descriptor.targetDashboardStateId;
var params = angular.copy(widgetContext.stateController.getStateParams());
if (!params) {
params = {};
}
updateEntityParams(params, targetEntityParamName, targetEntityId, entityName);
if (type == types.widgetActionTypes.openDashboardState.value) {
widgetContext.stateController.openState(targetDashboardStateId, params, descriptor.openRightLayout);
} else {
widgetContext.stateController.updateState(targetDashboardStateId, params, descriptor.openRightLayout);
}
break;
case types.widgetActionTypes.openDashboard.value:
var targetDashboardId = descriptor.targetDashboardId;
targetDashboardStateId = descriptor.targetDashboardStateId;
var stateObject = {};
stateObject.params = {};
updateEntityParams(stateObject.params, targetEntityParamName, targetEntityId, entityName);
if (targetDashboardStateId) {
stateObject.id = targetDashboardStateId;
}
var stateParams = {
dashboardId: targetDashboardId,
state: utils.objToBase64([ stateObject ])
}
if ($state.current.name === 'dashboard') {
$state.go('dashboard', stateParams);
} else {
$state.go('home.dashboards.dashboard', stateParams);
}
break;
case types.widgetActionTypes.custom.value:
var customFunction = descriptor.customFunction;
if (angular.isDefined(customFunction) && customFunction.length > 0) {
try {
if (!additionalParams) {
additionalParams = {};
}
var customActionFunction = new Function('$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', customFunction);
customActionFunction($event, widgetContext, entityId, entityName, additionalParams);
} catch (e) {
//
}
}
break;
}
}
function getFirstEntityInfo() {
var entityInfo;
for (var id in widgetContext.subscriptions) {
var subscription = widgetContext.subscriptions[id];
entityInfo = subscription.getFirstEntityInfo();
if (entityInfo) {
break;
}
}
return entityInfo;
}
function configureWidgetElement() {
$scope.displayLegend = angular.isDefined(widget.config.showLegend) ?
widget.config.showLegend : widget.type === types.widgetType.timeseries.value;
if ($scope.displayLegend) {
$scope.legendConfig = widget.config.legendConfig ||
{
position: types.position.bottom.value,
showMin: false,
showMax: false,
showAvg: widget.type === types.widgetType.timeseries.value,
showTotal: false
};
$scope.legendData = {
keys: [],
data: []
};
}
var html = '<div class="tb-absolute-fill tb-widget-error" ng-if="widgetErrorData">' +
'<span>Widget Error: {{ widgetErrorData.name + ": " + widgetErrorData.message}}</span>' +
'</div>' +
'<div class="tb-absolute-fill tb-widget-loading" ng-show="loadingData" layout="column" layout-align="center center">' +
'<md-progress-circular md-mode="indeterminate" ng-disabled="!loadingData" class="md-accent" md-diameter="40"></md-progress-circular>' +
'</div>';
var containerHtml = '<div id="container">' + widgetInfo.templateHtml + '</div>';
if ($scope.displayLegend) {
var layoutType;
if ($scope.legendConfig.position === types.position.top.value ||
$scope.legendConfig.position === types.position.bottom.value) {
layoutType = 'column';
} else {
layoutType = 'row';
}
var legendStyle;
switch($scope.legendConfig.position) {
case types.position.top.value:
legendStyle = 'padding-bottom: 8px; max-height: 50%; overflow-y: auto;';
break;
case types.position.bottom.value:
legendStyle = 'padding-top: 8px; max-height: 50%; overflow-y: auto;';
break;
case types.position.left.value:
legendStyle = 'padding-right: 0px; max-width: 50%; overflow-y: auto;';
break;
case types.position.right.value:
legendStyle = 'padding-left: 0px; max-width: 50%; overflow-y: auto;';
break;
}
var legendHtml = '<tb-legend style="'+legendStyle+'" legend-config="legendConfig" legend-data="legendData"></tb-legend>';
containerHtml = '<div flex id="widget-container">' + containerHtml + '</div>';
html += '<div class="tb-absolute-fill" layout="'+layoutType+'">';
if ($scope.legendConfig.position === types.position.top.value ||
$scope.legendConfig.position === types.position.left.value) {
html += legendHtml;
html += containerHtml;
} else {
html += containerHtml;
html += legendHtml;
}
html += '</div>';
} else {
html += containerHtml;
}
//TODO:
/*if (progressElement) {
progressScope.$destroy();
progressScope = null;
progressElement.remove();
progressElement = null;
}*/
$element.html(html);
var containerElement = $scope.displayLegend ? angular.element($element[0].querySelector('#widget-container')) : $element;
widgetContext.$container = $('#container', containerElement);
widgetContext.$containerParent = $(containerElement);
if (widgetSizeDetected) {
widgetContext.$container.css('height', widgetContext.height + 'px');
widgetContext.$container.css('width', widgetContext.width + 'px');
}
widgetContext.$scope = $scope.$new();
$compile($element.contents())(widgetContext.$scope);
addResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef
}
function destroyWidgetElement() {
removeResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef
$element.html('');
if (widgetContext.$scope) {
widgetContext.$scope.$destroy();
}
widgetContext.$container = null;
widgetContext.$containerParent = null;
}
function initialize() {
$scope.$on('toggleDashboardEditMode', function (event, isEdit) {
onEditModeChanged(isEdit);
});
$scope.$watch(function () {
return widget.row + ',' + widget.col + ',' + widget.config.mobileOrder;
}, function () {
//updateBounds();
$scope.$emit("widgetPositionChanged", widget);
});
$scope.$on('gridster-item-resized', function (event, item) {
if (!widgetContext.isMobile) {
widget.sizeX = item.sizeX;
widget.sizeY = item.sizeY;
}
});
$scope.$on('mobileModeChanged', function (event, newIsMobile) {
onMobileModeChanged(newIsMobile);
});
$scope.$on('entityAliasesChanged', function (event, aliasIds) {
var subscriptionChanged = false;
for (var id in widgetContext.subscriptions) {
var subscription = widgetContext.subscriptions[id];
subscriptionChanged = subscriptionChanged || subscription.onAliasesChanged(aliasIds);
}
if (subscriptionChanged && !vm.typeParameters.useCustomDatasources) {
reInit();
}
});
$scope.$on('dashboardTimewindowChanged', function (event, newDashboardTimewindow) {
vm.dashboardTimewindow = newDashboardTimewindow;
widgetContext.dashboardTimewindow = newDashboardTimewindow;
});
$scope.$on("$destroy", function () {
onDestroy();
});
configureWidgetElement();
var deferred = $q.defer();
if (!vm.typeParameters.useCustomDatasources) {
createDefaultSubscription().then(
function success() {
subscriptionInited = true;
deferred.resolve();
},
function fail() {
subscriptionInited = true;
deferred.reject();
}
);
} else {
$scope.loadingData = false;
subscriptionInited = true;
deferred.resolve();
}
return deferred.promise;
}
function reInit() {
onDestroy();
configureWidgetElement();
if (!vm.typeParameters.useCustomDatasources) {
createDefaultSubscription().then(
function success() {
subscriptionInited = true;
onInit();
},
function fail() {
subscriptionInited = true;
onInit();
}
);
} else {
subscriptionInited = true;
onInit();
}
}
function handleWidgetException(e) {
$log.error(e);
$scope.widgetErrorData = utils.processWidgetException(e);
}
function isReady() {
return subscriptionInited && gridsterItemInited && widgetSizeDetected;
}
function onInit(skipSizeCheck) {
if (!widgetContext.$containerParent) {
return;
}
if (!skipSizeCheck) {
checkSize();
}
if (!widgetContext.inited && isReady()) {
widgetContext.inited = true;
try {
widgetTypeInstance.onInit();
} catch (e) {
handleWidgetException(e);
}
if (!vm.typeParameters.useCustomDatasources && widgetContext.defaultSubscription) {
widgetContext.defaultSubscription.subscribe();
}
}
}
function checkSize() {
var width = widgetContext.$containerParent.width();
var height = widgetContext.$containerParent.height();
var sizeChanged = false;
if (!widgetContext.width || widgetContext.width != width || !widgetContext.height || widgetContext.height != height) {
if (width > 0 && height > 0) {
widgetContext.$container.css('height', height + 'px');
widgetContext.$container.css('width', width + 'px');
widgetContext.width = width;
widgetContext.height = height;
sizeChanged = true;
widgetSizeDetected = true;
}
}
return sizeChanged;
}
function onResize() {
if (checkSize()) {
if (widgetContext.inited) {
if (cafs['resize']) {
cafs['resize']();
cafs['resize'] = null;
}
cafs['resize'] = tbRaf(function() {
try {
widgetTypeInstance.onResize();
} catch (e) {
handleWidgetException(e);
}
});
} else {
onInit(true);
}
}
}
function gridsterItemInitialized(item) {
if (item && item.gridster) {
gridsterItemInited = true;
onInit();
// gridsterItemElement = $(item.$element);
//updateVisibility();
}
}
function onEditModeChanged(isEdit) {
if (widgetContext.isEdit != isEdit) {
widgetContext.isEdit = isEdit;
if (widgetContext.inited) {
if (cafs['editMode']) {
cafs['editMode']();
cafs['editMode'] = null;
}
cafs['editMode'] = tbRaf(function() {
try {
widgetTypeInstance.onEditModeChanged();
} catch (e) {
handleWidgetException(e);
}
});
}
}
}
function onMobileModeChanged(isMobile) {
if (widgetContext.isMobile != isMobile) {
widgetContext.isMobile = isMobile;
if (widgetContext.inited) {
if (cafs['mobileMode']) {
cafs['mobileMode']();
cafs['mobileMode'] = null;
}
cafs['mobileMode'] = tbRaf(function() {
try {
widgetTypeInstance.onMobileModeChanged();
} catch (e) {
handleWidgetException(e);
}
});
}
}
}
function isNumeric(val) {
return (val - parseFloat( val ) + 1) >= 0;
}
function formatValue(value, dec, units, showZeroDecimals) {
if (angular.isDefined(value) &&
value !== null && isNumeric(value)) {
var formatted = Number(value);
if (angular.isDefined(dec)) {
formatted = formatted.toFixed(dec);
}
if (!showZeroDecimals) {
formatted = (formatted * 1);
}
formatted = formatted.toString();
if (angular.isDefined(units) && units.length > 0) {
formatted += ' ' + units;
}
return formatted;
} else {
return value;
}
}
function onDestroy() {
for (var id in widgetContext.subscriptions) {
var subscription = widgetContext.subscriptions[id];
subscription.destroy();
}
subscriptionInited = false;
widgetContext.subscriptions = {};
if (widgetContext.inited) {
widgetContext.inited = false;
for (var cafId in cafs) {
if (cafs[cafId]) {
cafs[cafId]();
cafs[cafId] = null;
}
}
try {
widgetTypeInstance.onDestroy();
} catch (e) {
handleWidgetException(e);
}
}
destroyWidgetElement();
}
//TODO: widgets visibility
/*function updateVisibility(forceRedraw) {
if (visibleRect) {
forceRedraw = forceRedraw || visibleRect.containerResized;
var newVisible = false;
if (visibleRect.isMobile && gridsterItemElement) {
var topPx = gridsterItemElement.position().top;
var bottomPx = topPx + widget.sizeY * visibleRect.curRowHeight;
newVisible = !(topPx > visibleRect.bottomPx ||
bottomPx < visibleRect.topPx);
} else {
newVisible = !(bounds.left > visibleRect.right ||
bounds.right < visibleRect.left ||
bounds.top > visibleRect.bottom ||
bounds.bottom < visibleRect.top);
}
if (visible != newVisible) {
visible = newVisible;
if (visible) {
onRedraw(50);
}
} else if (forceRedraw && visible) {
onRedraw(50);
}
}
}*/
/* function updateBounds() {
bounds = {
top: widget.row,
left: widget.col,
bottom: widget.row + widget.sizeY,
right: widget.col + widget.sizeX
};
updateVisibility(true);
onRedraw();
}*/
}
/* eslint-enable angular/angularelement */