thingsboard-aplcache
Changes
ui/src/app/app.js 2(+2 -0)
ui/src/app/components/dashboard.directive.js 108(+93 -15)
ui/src/app/components/dashboard.tpl.html 144(+85 -59)
ui/src/app/dashboard/dashboard.controller.js 211(+203 -8)
ui/src/app/dashboard/index.js 2(+2 -0)
ui/src/app/services/item-buffer.service.js 191(+191 -0)
ui/src/locale/en_US.json 7(+5 -2)
ui/src/scss/main.scss 3(+3 -0)
Details
ui/src/app/app.js 2(+2 -0)
diff --git a/ui/src/app/app.js b/ui/src/app/app.js
index 3aa6cea..982f15e 100644
--- a/ui/src/app/app.js
+++ b/ui/src/app/app.js
@@ -49,6 +49,7 @@ import thingsboardDialogs from './components/datakey-config-dialog.controller';
import thingsboardMenu from './services/menu.service';
import thingsboardUtils from './common/utils.service';
import thingsboardTypes from './common/types.constant';
+import thingsboardKeyboardShortcut from './components/keyboard-shortcut.filter';
import thingsboardHelp from './help/help.directive';
import thingsboardToast from './services/toast';
import thingsboardHome from './layout';
@@ -95,6 +96,7 @@ angular.module('thingsboard', [
thingsboardMenu,
thingsboardUtils,
thingsboardTypes,
+ thingsboardKeyboardShortcut,
thingsboardHelp,
thingsboardToast,
thingsboardHome,
ui/src/app/components/dashboard.directive.js 108(+93 -15)
diff --git a/ui/src/app/components/dashboard.directive.js b/ui/src/app/components/dashboard.directive.js
index f90b055..7c5094a 100644
--- a/ui/src/app/components/dashboard.directive.js
+++ b/ui/src/app/components/dashboard.directive.js
@@ -23,6 +23,7 @@ import thingsboardWidget from './widget.directive';
import thingsboardToast from '../services/toast';
import thingsboardTimewindow from './timewindow.directive';
import thingsboardEvents from './tb-event-directives';
+import thingsboardMousepointMenu from './mousepoint-menu.directive';
/* eslint-disable import/no-unresolved, import/default */
@@ -38,6 +39,7 @@ export default angular.module('thingsboard.directives.dashboard', [thingsboardTy
thingsboardWidget,
thingsboardTimewindow,
thingsboardEvents,
+ thingsboardMousepointMenu,
gridster.name])
.directive('tbDashboard', Dashboard)
.name;
@@ -59,7 +61,10 @@ function Dashboard() {
isRemoveActionEnabled: '=',
onEditWidget: '&?',
onRemoveWidget: '&?',
+ onWidgetMouseDown: '&?',
onWidgetClicked: '&?',
+ prepareDashboardContextMenu: '&?',
+ prepareWidgetContextMenu: '&?',
loadWidgets: '&?',
onInit: '&?',
onInitFailed: '&?',
@@ -75,8 +80,9 @@ function Dashboard() {
function DashboardController($scope, $rootScope, $element, $timeout, $log, toast, types) {
var highlightedMode = false;
- var highlightedIndex = -1;
- var mouseDownIndex = -1;
+ var highlightedWidget = null;
+ var selectedWidget = null;
+ var mouseDownWidget = -1;
var widgetMouseMoved = false;
var gridsterParent = null;
@@ -117,6 +123,8 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
vm.isWidgetExpanded = false;
vm.isHighlighted = isHighlighted;
vm.isNotHighlighted = isNotHighlighted;
+ vm.selectWidget = selectWidget;
+ vm.getSelectedWidget = getSelectedWidget;
vm.highlightWidget = highlightWidget;
vm.resetHighlight = resetHighlight;
@@ -134,6 +142,17 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
vm.removeWidget = removeWidget;
vm.loading = loading;
+ vm.openDashboardContextMenu = openDashboardContextMenu;
+ vm.openWidgetContextMenu = openWidgetContextMenu;
+
+ vm.getEventGridPosition = getEventGridPosition;
+
+ vm.contextMenuItems = [];
+ vm.contextMenuEvent = null;
+
+ vm.widgetContextMenuItems = [];
+ vm.widgetContextMenuEvent = null;
+
//$element[0].onmousemove=function(){
// widgetMouseMove();
// }
@@ -305,7 +324,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
}
function resetWidgetClick () {
- mouseDownIndex = -1;
+ mouseDownWidget = -1;
widgetMouseMoved = false;
}
@@ -315,25 +334,27 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
}
function widgetMouseDown ($event, widget) {
- mouseDownIndex = vm.widgets.indexOf(widget);
+ mouseDownWidget = widget;
widgetMouseMoved = false;
+ if (vm.onWidgetMouseDown) {
+ vm.onWidgetMouseDown({event: $event, widget: widget});
+ }
}
function widgetMouseMove () {
- if (mouseDownIndex > -1) {
+ if (mouseDownWidget) {
widgetMouseMoved = true;
}
}
function widgetMouseUp ($event, widget) {
$timeout(function () {
- if (!widgetMouseMoved && mouseDownIndex > -1) {
- var index = vm.widgets.indexOf(widget);
- if (index === mouseDownIndex) {
+ if (!widgetMouseMoved && mouseDownWidget) {
+ if (widget === mouseDownWidget) {
widgetClicked($event, widget);
}
}
- mouseDownIndex = -1;
+ mouseDownWidget = null;
widgetMouseMoved = false;
}, 0);
}
@@ -347,6 +368,41 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
}
}
+ function openDashboardContextMenu($event, $mdOpenMousepointMenu) {
+ if (vm.prepareDashboardContextMenu) {
+ vm.contextMenuItems = vm.prepareDashboardContextMenu();
+ if (vm.contextMenuItems && vm.contextMenuItems.length > 0) {
+ vm.contextMenuEvent = $event;
+ $mdOpenMousepointMenu($event);
+ }
+ }
+ }
+
+ function openWidgetContextMenu($event, widget, $mdOpenMousepointMenu) {
+ if (vm.prepareWidgetContextMenu) {
+ vm.widgetContextMenuItems = vm.prepareWidgetContextMenu({widget: widget});
+ if (vm.widgetContextMenuItems && vm.widgetContextMenuItems.length > 0) {
+ vm.widgetContextMenuEvent = $event;
+ $mdOpenMousepointMenu($event);
+ }
+ }
+ }
+
+ function getEventGridPosition(event) {
+ var pos = {
+ row: 0,
+ column: 0
+ }
+ var offset = gridsterParent.offset();
+ var x = event.pageX - offset.left + gridsterParent.scrollLeft();
+ var y = event.pageY - offset.top + gridsterParent.scrollTop();
+ if (gridster) {
+ pos.row = gridster.pixelsToRows(y);
+ pos.column = gridster.pixelsToColumns(x);
+ }
+ return pos;
+ }
+
function editWidget ($event, widget) {
resetWidgetClick();
if ($event) {
@@ -367,10 +423,10 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
}
}
- function highlightWidget(widgetIndex, delay) {
+ function highlightWidget(widget, delay) {
highlightedMode = true;
- highlightedIndex = widgetIndex;
- var item = $('.gridster-item', gridster.$element)[widgetIndex];
+ highlightedWidget = widget;
+ var item = $('.gridster-item', gridster.$element)[vm.widgets.indexOf(widget)];
if (item) {
var height = $(item).outerHeight(true);
var rectHeight = gridsterParent.height();
@@ -385,17 +441,39 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
}
}
+ function selectWidget(widget, delay) {
+ selectedWidget = widget;
+ var item = $('.gridster-item', gridster.$element)[vm.widgets.indexOf(widget)];
+ if (item) {
+ var height = $(item).outerHeight(true);
+ var rectHeight = gridsterParent.height();
+ var offset = (rectHeight - height) / 2;
+ var scrollTop = item.offsetTop;
+ if (offset > 0) {
+ scrollTop -= offset;
+ }
+ gridsterParent.animate({
+ scrollTop: scrollTop
+ }, delay);
+ }
+ }
+
+ function getSelectedWidget() {
+ return selectedWidget;
+ }
+
function resetHighlight() {
highlightedMode = false;
- highlightedIndex = -1;
+ highlightedWidget = null;
+ selectedWidget = null;
}
function isHighlighted(widget) {
- return highlightedMode && vm.widgets.indexOf(widget) === highlightedIndex;
+ return (highlightedMode && highlightedWidget === widget) || (selectedWidget === widget);
}
function isNotHighlighted(widget) {
- return highlightedMode && vm.widgets.indexOf(widget) != highlightedIndex;
+ return highlightedMode && highlightedWidget != widget;
}
function widgetColor(widget) {
ui/src/app/components/dashboard.tpl.html 144(+85 -59)
diff --git a/ui/src/app/components/dashboard.tpl.html b/ui/src/app/components/dashboard.tpl.html
index f0cecef..c43caac 100644
--- a/ui/src/app/components/dashboard.tpl.html
+++ b/ui/src/app/components/dashboard.tpl.html
@@ -19,64 +19,90 @@
ng-show="(vm.loading() || vm.dashboardLoading) && !vm.isEdit">
<md-progress-circular md-mode="indeterminate" class="md-warn" md-diameter="100"></md-progress-circular>
</md-content>
-<md-content id="gridster-parent" class="tb-dashboard-content" flex layout-wrap>
- <div ng-style="vm.dashboardStyle" id="gridster-background" style="height: auto; min-height: 100%;">
- <div id="gridster-child" gridster="vm.gridsterOpts">
- <ul>
- <!-- ng-click="widgetClicked($event, widget)" -->
- <li gridster-item="widget" ng-repeat="widget in vm.widgets">
- <div tb-expand-fullscreen expand-button-id="expand-button" on-fullscreen-changed="vm.onWidgetFullscreenChanged(expanded, widget)" layout="column" class="tb-widget md-whiteframe-4dp"
- ng-class="{'tb-highlighted': vm.isHighlighted(widget), 'tb-not-highlighted': vm.isNotHighlighted(widget)}"
- tb-mousedown="vm.widgetMouseDown($event, widget)"
- tb-mousemove="vm.widgetMouseMove($event, widget)"
- tb-mouseup="vm.widgetMouseUp($event, widget)"
- style="
- cursor: pointer;
- color: {{vm.widgetColor(widget)}};
- background-color: {{vm.widgetBackgroundColor(widget)}};
- padding: {{vm.widgetPadding(widget)}}
- ">
- <div class="tb-widget-title" layout="column" ng-show="vm.showWidgetTitle(widget) || vm.hasTimewindow(widget)">
- <span ng-show="vm.showWidgetTitle(widget)" class="md-subhead">{{widget.config.title}}</span>
- <tb-timewindow ng-if="vm.hasTimewindow(widget)" ng-model="widget.config.timewindow"></tb-timewindow>
- </div>
- <div class="tb-widget-actions" layout="row" layout-align="start center">
- <md-button id="expand-button"
- aria-label="{{ 'fullscreen.fullscreen' | translate }}"
- class="md-icon-button md-primary"></md-button>
- <md-button ng-show="vm.isEditActionEnabled && !vm.isWidgetExpanded"
- ng-disabled="vm.loading()"
- class="md-icon-button md-primary"
- ng-click="vm.editWidget($event, widget)"
- aria-label="{{ 'widget.edit' | translate }}">
- <md-tooltip md-direction="top">
- {{ 'widget.edit' | translate }}
- </md-tooltip>
- <md-icon class="material-icons">
- edit
- </md-icon>
- </md-button>
- <md-button ng-show="vm.isRemoveActionEnabled && !vm.isWidgetExpanded"
- ng-disabled="vm.loading()"
- class="md-icon-button md-primary"
- ng-click="vm.removeWidget($event, widget)"
- aria-label="{{ 'widget.remove' | translate }}">
- <md-tooltip md-direction="top">
- {{ 'widget.remove' | translate }}
- </md-tooltip>
- <md-icon class="material-icons">
- close
- </md-icon>
- </md-button>
- </div>
- <div flex layout="column" class="tb-widget-content">
- <div flex tb-widget
- locals="{ visibleRect: vm.visibleRect, widget: widget, deviceAliasList: vm.deviceAliasList, isPreview: vm.isEdit }">
+<md-menu md-position-mode="target target" tb-mousepoint-menu>
+ <md-content id="gridster-parent" class="tb-dashboard-content" flex layout-wrap ng-click="" tb-contextmenu="vm.openDashboardContextMenu($event, $mdOpenMousepointMenu)">
+ <div ng-style="vm.dashboardStyle" id="gridster-background" style="height: auto; min-height: 100%;">
+ <div id="gridster-child" gridster="vm.gridsterOpts">
+ <ul>
+ <!-- ng-click="widgetClicked($event, widget)" -->
+ <li gridster-item="widget" ng-repeat="widget in vm.widgets">
+ <md-menu md-position-mode="target target" tb-mousepoint-menu>
+ <div tb-expand-fullscreen
+ expand-button-id="expand-button" on-fullscreen-changed="vm.onWidgetFullscreenChanged(expanded, widget)" layout="column" class="tb-widget md-whiteframe-4dp"
+ ng-class="{'tb-highlighted': vm.isHighlighted(widget), 'tb-not-highlighted': vm.isNotHighlighted(widget)}"
+ tb-mousedown="vm.widgetMouseDown($event, widget)"
+ tb-mousemove="vm.widgetMouseMove($event, widget)"
+ tb-mouseup="vm.widgetMouseUp($event, widget)"
+ ng-click=""
+ tb-contextmenu="vm.openWidgetContextMenu($event, widget, $mdOpenMousepointMenu)"
+ style="
+ cursor: pointer;
+ color: {{vm.widgetColor(widget)}};
+ background-color: {{vm.widgetBackgroundColor(widget)}};
+ padding: {{vm.widgetPadding(widget)}}
+ ">
+ <div class="tb-widget-title" layout="column" ng-show="vm.showWidgetTitle(widget) || vm.hasTimewindow(widget)">
+ <span ng-show="vm.showWidgetTitle(widget)" class="md-subhead">{{widget.config.title}}</span>
+ <tb-timewindow ng-if="vm.hasTimewindow(widget)" ng-model="widget.config.timewindow"></tb-timewindow>
+ </div>
+ <div class="tb-widget-actions" layout="row" layout-align="start center">
+ <md-button id="expand-button"
+ ng-show="!vm.isEdit"
+ aria-label="{{ 'fullscreen.fullscreen' | translate }}"
+ class="md-icon-button md-primary"></md-button>
+ <md-button ng-show="vm.isEditActionEnabled && !vm.isWidgetExpanded"
+ ng-disabled="vm.loading()"
+ class="md-icon-button md-primary"
+ ng-click="vm.editWidget($event, widget)"
+ aria-label="{{ 'widget.edit' | translate }}">
+ <md-tooltip md-direction="top">
+ {{ 'widget.edit' | translate }}
+ </md-tooltip>
+ <md-icon class="material-icons">
+ edit
+ </md-icon>
+ </md-button>
+ <md-button ng-show="vm.isRemoveActionEnabled && !vm.isWidgetExpanded"
+ ng-disabled="vm.loading()"
+ class="md-icon-button md-primary"
+ ng-click="vm.removeWidget($event, widget)"
+ aria-label="{{ 'widget.remove' | translate }}">
+ <md-tooltip md-direction="top">
+ {{ 'widget.remove' | translate }}
+ </md-tooltip>
+ <md-icon class="material-icons">
+ close
+ </md-icon>
+ </md-button>
+ </div>
+ <div flex layout="column" class="tb-widget-content">
+ <div flex tb-widget
+ locals="{ visibleRect: vm.visibleRect, widget: widget, deviceAliasList: vm.deviceAliasList, isPreview: vm.isEdit }">
+ </div>
+ </div>
</div>
- </div>
- </div>
- </li>
- </ul>
+ <md-menu-content id="menu" width="4" ng-mouseleave="$mdCloseMousepointMenu()">
+ <md-menu-item ng-repeat ="item in vm.widgetContextMenuItems">
+ <md-button ng-disabled="!item.enabled" ng-click="item.action(vm.widgetContextMenuEvent, widget)">
+ <md-icon ng-if="item.icon" md-menu-align-target aria-label="{{ item.value | translate }}" class="material-icons">{{item.icon}}</md-icon>
+ <span translate>{{item.value}}</span>
+ <span ng-if="item.shortcut" class="tb-alt-text"> {{ item.shortcut | keyboardShortcut }}</span>
+ </md-button>
+ </md-menu-item>
+ </md-menu-content>
+ </md-menu>
+ </li>
+ </ul>
+ </div>
</div>
- </div>
-</md-content>
\ No newline at end of file
+ </md-content>
+ <md-menu-content id="menu" width="4" ng-mouseleave="$mdCloseMousepointMenu()">
+ <md-menu-item ng-repeat ="item in vm.contextMenuItems">
+ <md-button ng-disabled="!item.enabled" ng-click="item.action(vm.contextMenuEvent)">
+ <md-icon ng-if="item.icon" md-menu-align-target aria-label="{{ item.value | translate }}" class="material-icons">{{item.icon}}</md-icon>
+ <span translate>{{item.value}}</span>
+ <span ng-if="item.shortcut" class="tb-alt-text"> {{ item.shortcut | keyboardShortcut }}</span>
+ </md-button>
+ </md-menu-item>
+ </md-menu-content>
+</md-menu>
\ No newline at end of file
diff --git a/ui/src/app/components/keyboard-shortcut.filter.js b/ui/src/app/components/keyboard-shortcut.filter.js
new file mode 100644
index 0000000..289afe2
--- /dev/null
+++ b/ui/src/app/components/keyboard-shortcut.filter.js
@@ -0,0 +1,40 @@
+/*
+ * Copyright © 2016 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.
+ */
+export default angular.module('thingsboard.filters.keyboardShortcut', [])
+ .filter('keyboardShortcut', KeyboardShortcut)
+ .name;
+
+/*@ngInject*/
+function KeyboardShortcut($window) {
+ return function(str) {
+ if (!str) return;
+ var keys = str.split('-');
+ var isOSX = /Mac OS X/.test($window.navigator.userAgent);
+
+ var seperator = (!isOSX || keys.length > 2) ? '+' : '';
+
+ var abbreviations = {
+ M: isOSX ? '⌘' : 'Ctrl',
+ A: isOSX ? 'Option' : 'Alt',
+ S: 'Shift'
+ };
+
+ return keys.map(function(key, index) {
+ var last = index == keys.length - 1;
+ return last ? key : abbreviations[key];
+ }).join(seperator);
+ };
+}
diff --git a/ui/src/app/components/mousepoint-menu.directive.js b/ui/src/app/components/mousepoint-menu.directive.js
new file mode 100644
index 0000000..b3f24c7
--- /dev/null
+++ b/ui/src/app/components/mousepoint-menu.directive.js
@@ -0,0 +1,51 @@
+/*
+ * Copyright © 2016 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.
+ */
+
+export default angular.module('thingsboard.directives.mousepointMenu', [])
+ .directive('tbMousepointMenu', MousepointMenu)
+ .name;
+
+/*@ngInject*/
+function MousepointMenu() {
+
+ var linker = function ($scope, $element, $attrs, RightClickContextMenu) {
+
+ $scope.$mdOpenMousepointMenu = function($event){
+ RightClickContextMenu.offsets = function(){
+ var offset = $element.offset();
+ var x = $event.pageX - offset.left;
+ var y = $event.pageY - offset.top;
+
+ var offsets = {
+ left: x,
+ top: y
+ }
+ return offsets;
+ }
+ RightClickContextMenu.open($event);
+ };
+
+ $scope.$mdCloseMousepointMenu = function() {
+ RightClickContextMenu.close();
+ }
+ }
+
+ return {
+ restrict: "A",
+ link: linker,
+ require: 'mdMenu'
+ };
+}
diff --git a/ui/src/app/components/tb-event-directives.js b/ui/src/app/components/tb-event-directives.js
index 1fefd51..f7e7fe9 100644
--- a/ui/src/app/components/tb-event-directives.js
+++ b/ui/src/app/components/tb-event-directives.js
@@ -20,7 +20,7 @@ const PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
var tbEventDirectives = {};
angular.forEach(
- 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
+ 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave contextmenu keydown keyup keypress submit focus blur copy cut paste'.split(' '),
function(eventName) {
var directiveName = directiveNormalize('tb-' + eventName);
tbEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse) {
diff --git a/ui/src/app/components/widgets-bundle-select.directive.js b/ui/src/app/components/widgets-bundle-select.directive.js
index fcf5def..1525523 100644
--- a/ui/src/app/components/widgets-bundle-select.directive.js
+++ b/ui/src/app/components/widgets-bundle-select.directive.js
@@ -55,12 +55,26 @@ function WidgetsBundleSelect($compile, $templateCache, widgetService, types) {
if (widgetsBundles.length > 0) {
scope.widgetsBundle = widgetsBundles[0];
}
+ } else if (angular.isDefined(scope.selectBundleAlias)) {
+ selectWidgetsBundleByAlias(scope.selectBundleAlias);
}
},
function fail() {
}
);
+ function selectWidgetsBundleByAlias(alias) {
+ if (scope.widgetsBundles && alias) {
+ for (var w in scope.widgetsBundles) {
+ var widgetsBundle = scope.widgetsBundles[w];
+ if (widgetsBundle.alias === alias) {
+ scope.widgetsBundle = widgetsBundle;
+ break;
+ }
+ }
+ }
+ }
+
scope.isSystem = function(item) {
return item && item.tenantId.id === types.id.nullUid;
}
@@ -79,6 +93,12 @@ function WidgetsBundleSelect($compile, $templateCache, widgetService, types) {
scope.updateView();
});
+ scope.$watch('selectBundleAlias', function (newVal, prevVal) {
+ if (newVal !== prevVal) {
+ selectWidgetsBundleByAlias(scope.selectBundleAlias);
+ }
+ });
+
$compile(element.contents())(scope);
}
@@ -90,7 +110,8 @@ function WidgetsBundleSelect($compile, $templateCache, widgetService, types) {
bundlesScope: '@',
theForm: '=?',
tbRequired: '=?',
- selectFirstBundle: '='
+ selectFirstBundle: '=',
+ selectBundleAlias: '=?'
}
};
}
\ No newline at end of file
ui/src/app/dashboard/dashboard.controller.js 211(+203 -8)
diff --git a/ui/src/app/dashboard/dashboard.controller.js b/ui/src/app/dashboard/dashboard.controller.js
index 68978c3..896e7a1 100644
--- a/ui/src/app/dashboard/dashboard.controller.js
+++ b/ui/src/app/dashboard/dashboard.controller.js
@@ -23,7 +23,7 @@ import addWidgetTemplate from './add-widget.tpl.html';
/*@ngInject*/
export default function DashboardController(types, widgetService, userService,
- dashboardService, $window, $rootScope,
+ dashboardService, itembuffer, hotkeys, $window, $rootScope,
$scope, $state, $stateParams, $mdDialog, $timeout, $document, $q, $translate, $filter) {
var user = userService.getCurrentUser();
@@ -48,7 +48,10 @@ export default function DashboardController(types, widgetService, userService,
vm.addWidgetFromType = addWidgetFromType;
vm.dashboardInited = dashboardInited;
vm.dashboardInitFailed = dashboardInitFailed;
+ vm.widgetMouseDown = widgetMouseDown;
vm.widgetClicked = widgetClicked;
+ vm.prepareDashboardContextMenu = prepareDashboardContextMenu;
+ vm.prepareWidgetContextMenu = prepareWidgetContextMenu;
vm.editWidget = editWidget;
vm.isTenantAdmin = isTenantAdmin;
vm.loadDashboard = loadDashboard;
@@ -63,6 +66,7 @@ export default function DashboardController(types, widgetService, userService,
vm.toggleDashboardEditMode = toggleDashboardEditMode;
vm.onRevertWidgetEdit = onRevertWidgetEdit;
vm.helpLinkIdForWidgetType = helpLinkIdForWidgetType;
+ vm.displayTitle = displayTitle;
vm.widgetsBundle;
@@ -194,6 +198,7 @@ export default function DashboardController(types, widgetService, userService,
function dashboardInited(dashboard) {
vm.dashboardContainer = dashboard;
+ initHotKeys();
}
function isTenantAdmin() {
@@ -289,18 +294,188 @@ export default function DashboardController(types, widgetService, userService,
var delayOffset = transition ? 350 : 0;
var delay = transition ? 400 : 300;
$timeout(function () {
- vm.dashboardContainer.highlightWidget(vm.editingWidgetIndex, delay);
+ vm.dashboardContainer.highlightWidget(widget, delay);
}, delayOffset, false);
}
}
}
+ function widgetMouseDown($event, widget) {
+ if (vm.isEdit && !vm.isEditingWidget) {
+ vm.dashboardContainer.selectWidget(widget, 0);
+ }
+ }
+
function widgetClicked($event, widget) {
if (vm.isEditingWidget) {
editWidget($event, widget);
}
}
+ function initHotKeys() {
+ $translate(['action.copy', 'action.paste', 'action.delete']).then(function (translations) {
+ hotkeys.bindTo($scope)
+ .add({
+ combo: 'ctrl+c',
+ description: translations['action.copy'],
+ allowIn: ['INPUT', 'SELECT', 'TEXTAREA'],
+ callback: function (event) {
+ if (vm.isEdit && !vm.isEditingWidget && !vm.widgetEditMode) {
+ var widget = vm.dashboardContainer.getSelectedWidget();
+ if (widget) {
+ event.preventDefault();
+ copyWidget(event, widget);
+ }
+ }
+ }
+ })
+ .add({
+ combo: 'ctrl+v',
+ description: translations['action.paste'],
+ allowIn: ['INPUT', 'SELECT', 'TEXTAREA'],
+ callback: function (event) {
+ if (vm.isEdit && !vm.isEditingWidget && !vm.widgetEditMode) {
+ if (itembuffer.hasWidget()) {
+ event.preventDefault();
+ pasteWidget(event);
+ }
+ }
+ }
+ })
+ .add({
+ combo: 'ctrl+x',
+ description: translations['action.delete'],
+ allowIn: ['INPUT', 'SELECT', 'TEXTAREA'],
+ callback: function (event) {
+ if (vm.isEdit && !vm.isEditingWidget && !vm.widgetEditMode) {
+ var widget = vm.dashboardContainer.getSelectedWidget();
+ if (widget) {
+ event.preventDefault();
+ removeWidget(event, widget);
+ }
+ }
+ }
+ });
+ });
+ }
+
+ function prepareDashboardContextMenu() {
+ var dashboardContextActions = [];
+ if (vm.isEdit && !vm.isEditingWidget && !vm.widgetEditMode) {
+ dashboardContextActions.push(
+ {
+ action: openDashboardSettings,
+ enabled: true,
+ value: "dashboard.settings",
+ icon: "settings"
+ }
+ );
+ dashboardContextActions.push(
+ {
+ action: openDeviceAliases,
+ enabled: true,
+ value: "device.aliases",
+ icon: "devices_other"
+ }
+ );
+ dashboardContextActions.push(
+ {
+ action: pasteWidget,
+ enabled: itembuffer.hasWidget(),
+ value: "action.paste",
+ icon: "content_paste",
+ shortcut: "M-V"
+ }
+ );
+ }
+ return dashboardContextActions;
+ }
+
+ function pasteWidget($event) {
+ var pos = vm.dashboardContainer.getEventGridPosition($event);
+ itembuffer.pasteWidget(vm.dashboard, pos);
+ }
+
+ function prepareWidgetContextMenu() {
+ var widgetContextActions = [];
+ if (vm.isEdit && !vm.isEditingWidget) {
+ widgetContextActions.push(
+ {
+ action: editWidget,
+ enabled: true,
+ value: "action.edit",
+ icon: "edit"
+ }
+ );
+ if (!vm.widgetEditMode) {
+ widgetContextActions.push(
+ {
+ action: copyWidget,
+ enabled: true,
+ value: "action.copy",
+ icon: "content_copy",
+ shortcut: "M-C"
+ }
+ );
+ widgetContextActions.push(
+ {
+ action: removeWidget,
+ enabled: true,
+ value: "action.delete",
+ icon: "clear",
+ shortcut: "M-X"
+ }
+ );
+ }
+ }
+ return widgetContextActions;
+ }
+
+ function copyWidget($event, widget) {
+ var aliasesInfo = {
+ datasourceAliases: {},
+ targetDeviceAliases: {}
+ };
+ var originalColumns = 24;
+ if (vm.dashboard.configuration.gridSettings &&
+ vm.dashboard.configuration.gridSettings.columns) {
+ originalColumns = vm.dashboard.configuration.gridSettings.columns;
+ }
+ if (widget.config && vm.dashboard.configuration
+ && vm.dashboard.configuration.deviceAliases) {
+ var deviceAlias;
+ if (widget.config.datasources) {
+ for (var i=0;i<widget.config.datasources.length;i++) {
+ var datasource = widget.config.datasources[i];
+ if (datasource.type === types.datasourceType.device && datasource.deviceAliasId) {
+ deviceAlias = vm.dashboard.configuration.deviceAliases[datasource.deviceAliasId];
+ if (deviceAlias) {
+ aliasesInfo.datasourceAliases[i] = {
+ aliasName: deviceAlias.alias,
+ deviceId: deviceAlias.deviceId
+ }
+ }
+ }
+ }
+ }
+ if (widget.config.targetDeviceAliasIds) {
+ for (i=0;i<widget.config.targetDeviceAliasIds.length;i++) {
+ var targetDeviceAliasId = widget.config.targetDeviceAliasIds[i];
+ if (targetDeviceAliasId) {
+ deviceAlias = vm.dashboard.configuration.deviceAliases[targetDeviceAliasId];
+ if (deviceAlias) {
+ aliasesInfo.targetDeviceAliases[i] = {
+ aliasName: deviceAlias.alias,
+ deviceId: deviceAlias.deviceId
+ }
+ }
+ }
+ }
+ }
+ }
+ itembuffer.copyWidget(widget, aliasesInfo, originalColumns);
+ }
+
function helpLinkIdForWidgetType() {
var link = 'widgetsConfig';
if (vm.editingWidget && vm.editingWidget.type) {
@@ -322,6 +497,15 @@ export default function DashboardController(types, widgetService, userService,
return link;
}
+ function displayTitle() {
+ if (vm.dashboard && vm.dashboard.configuration.gridSettings &&
+ angular.isDefined(vm.dashboard.configuration.gridSettings.showTitle)) {
+ return vm.dashboard.configuration.gridSettings.showTitle;
+ } else {
+ return true;
+ }
+ }
+
function onRevertWidgetEdit(widgetForm) {
if (widgetForm.$dirty) {
widgetForm.$setPristine();
@@ -331,7 +515,9 @@ export default function DashboardController(types, widgetService, userService,
function saveWidget(widgetForm) {
widgetForm.$setPristine();
- vm.widgets[vm.editingWidgetIndex] = angular.copy(vm.editingWidget);
+ var widget = angular.copy(vm.editingWidget);
+ vm.widgets[vm.editingWidgetIndex] = widget;
+ vm.dashboardContainer.highlightWidget(widget, 0);
}
function onEditWidgetClosed() {
@@ -421,8 +607,8 @@ export default function DashboardController(types, widgetService, userService,
});
}
- function toggleDashboardEditMode() {
- vm.isEdit = !vm.isEdit;
+ function setEditMode(isEdit, revert) {
+ vm.isEdit = isEdit;
if (vm.isEdit) {
if (vm.widgetEditMode) {
vm.prevWidgets = angular.copy(vm.widgets);
@@ -433,14 +619,23 @@ export default function DashboardController(types, widgetService, userService,
if (vm.widgetEditMode) {
vm.widgets = vm.prevWidgets;
} else {
- vm.dashboard = vm.prevDashboard;
- vm.widgets = vm.dashboard.configuration.widgets;
+ if (vm.dashboardContainer) {
+ vm.dashboardContainer.resetHighlight();
+ }
+ if (revert) {
+ vm.dashboard = vm.prevDashboard;
+ vm.widgets = vm.dashboard.configuration.widgets;
+ }
}
}
}
+ function toggleDashboardEditMode() {
+ setEditMode(!vm.isEdit, true);
+ }
+
function saveDashboard() {
- vm.isEdit = false;
+ setEditMode(false, false);
notifyDashboardUpdated();
}
diff --git a/ui/src/app/dashboard/dashboard.tpl.html b/ui/src/app/dashboard/dashboard.tpl.html
index 0c815a6..ca46b97 100644
--- a/ui/src/app/dashboard/dashboard.tpl.html
+++ b/ui/src/app/dashboard/dashboard.tpl.html
@@ -51,7 +51,7 @@
</md-button>
</section>
<section ng-if="!vm.widgetEditMode" class="tb-dashboard-title" layout="row" layout-align="center center">
- <h3 ng-show="!vm.isEdit">{{ vm.dashboard.title }}</h3>
+ <h3 ng-show="!vm.isEdit && vm.displayTitle()">{{ vm.dashboard.title }}</h3>
<md-input-container ng-show="vm.isEdit" class="md-block" style="height: 30px;">
<label translate>dashboard.title</label>
<input class="tb-dashboard-title" required name="title" ng-model="vm.dashboard.title">
@@ -64,7 +64,7 @@
</md-button>
</section>
<div class="tb-absolute-fill"
- ng-class="{ 'tb-padded' : !vm.widgetEditMode, 'tb-shrinked' : vm.isEditingWidget }">
+ ng-class="{ 'tb-padded' : !vm.widgetEditMode && (vm.isEdit || vm.displayTitle()), 'tb-shrinked' : vm.isEditingWidget }">
<tb-dashboard
dashboard-style="{'background-color': vm.dashboard.configuration.gridSettings.backgroundColor,
'background-image': 'url('+vm.dashboard.configuration.gridSettings.backgroundImageUrl+')',
@@ -82,7 +82,11 @@
is-edit-action-enabled="vm.isEdit || vm.widgetEditMode"
is-remove-action-enabled="vm.isEdit && !vm.widgetEditMode"
on-edit-widget="vm.editWidget(event, widget)"
+ on-widget-mouse-down="vm.widgetMouseDown(event, widget)"
on-widget-clicked="vm.widgetClicked(event, widget)"
+ on-widget-context-menu="vm.widgetContextMenu(event, widget)"
+ prepare-dashboard-context-menu="vm.prepareDashboardContextMenu()"
+ prepare-widget-context-menu="vm.prepareWidgetContextMenu(widget)"
on-remove-widget="vm.removeWidget(event, widget)"
load-widgets="vm.loadDashboard()"
on-init="vm.dashboardInited(dashboard)"
diff --git a/ui/src/app/dashboard/dashboard-settings.controller.js b/ui/src/app/dashboard/dashboard-settings.controller.js
index d15359e..3a46566 100644
--- a/ui/src/app/dashboard/dashboard-settings.controller.js
+++ b/ui/src/app/dashboard/dashboard-settings.controller.js
@@ -28,6 +28,10 @@ export default function DashboardSettingsController($scope, $mdDialog, gridSetti
vm.gridSettings = gridSettings || {};
+ if (angular.isUndefined(vm.gridSettings.showTitle)) {
+ vm.gridSettings.showTitle = true;
+ }
+
vm.gridSettings.backgroundColor = vm.gridSettings.backgroundColor || 'rgba(0,0,0,0)';
vm.gridSettings.columns = vm.gridSettings.columns || 24;
vm.gridSettings.margins = vm.gridSettings.margins || [10, 10];
diff --git a/ui/src/app/dashboard/dashboard-settings.tpl.html b/ui/src/app/dashboard/dashboard-settings.tpl.html
index f69eb02..e28798d 100644
--- a/ui/src/app/dashboard/dashboard-settings.tpl.html
+++ b/ui/src/app/dashboard/dashboard-settings.tpl.html
@@ -31,6 +31,11 @@
<md-dialog-content>
<div class="md-dialog-content">
<fieldset ng-disabled="loading">
+ <div layout="row" layout-padding>
+ <md-checkbox flex aria-label="{{ 'dashboard.display-title' | translate }}"
+ ng-model="vm.gridSettings.showTitle">{{ 'dashboard.display-title' | translate }}
+ </md-checkbox>
+ </div>
<md-input-container class="md-block">
<label translate>dashboard.columns-count</label>
<input required type="number" step="any" name="columns" ng-model="vm.gridSettings.columns" min="10"
ui/src/app/dashboard/index.js 2(+2 -0)
diff --git a/ui/src/app/dashboard/index.js b/ui/src/app/dashboard/index.js
index 7400463..461f56e 100644
--- a/ui/src/app/dashboard/index.js
+++ b/ui/src/app/dashboard/index.js
@@ -29,6 +29,7 @@ import thingsboardDashboard from '../components/dashboard.directive';
import thingsboardExpandFullscreen from '../components/expand-fullscreen.directive';
import thingsboardWidgetsBundleSelect from '../components/widgets-bundle-select.directive';
import thingsboardTypes from '../common/types.constant';
+import thingsboardItemBuffer from '../services/item-buffer.service';
import DashboardRoutes from './dashboard.routes';
import DashboardsController from './dashboards.controller';
@@ -45,6 +46,7 @@ export default angular.module('thingsboard.dashboard', [
uiRouter,
gridster.name,
thingsboardTypes,
+ thingsboardItemBuffer,
thingsboardGrid,
thingsboardApiWidget,
thingsboardApiUser,
diff --git a/ui/src/app/device/attribute/add-widget-to-dashboard-dialog.controller.js b/ui/src/app/device/attribute/add-widget-to-dashboard-dialog.controller.js
index 9149cf1..3f6d769 100644
--- a/ui/src/app/device/attribute/add-widget-to-dashboard-dialog.controller.js
+++ b/ui/src/app/device/attribute/add-widget-to-dashboard-dialog.controller.js
@@ -14,7 +14,7 @@
* limitations under the License.
*/
/*@ngInject*/
-export default function AddWidgetToDashboardDialogController($scope, $mdDialog, $state, dashboardService, deviceId, deviceName, widget) {
+export default function AddWidgetToDashboardDialogController($scope, $mdDialog, $state, itembuffer, dashboardService, deviceId, deviceName, widget) {
var vm = this;
@@ -34,62 +34,20 @@ export default function AddWidgetToDashboardDialogController($scope, $mdDialog,
function add() {
$scope.theForm.$setPristine();
var theDashboard;
- var deviceAliases;
- widget.col = 0;
- widget.sizeX /= 2;
- widget.sizeY /= 2;
if (vm.addToDashboardType === 0) {
theDashboard = vm.dashboard;
- if (!theDashboard.configuration) {
- theDashboard.configuration = {};
- }
- deviceAliases = theDashboard.configuration.deviceAliases;
- if (!deviceAliases) {
- deviceAliases = {};
- theDashboard.configuration.deviceAliases = deviceAliases;
- }
- var newAliasId;
- for (var aliasId in deviceAliases) {
- if (deviceAliases[aliasId].deviceId === deviceId) {
- newAliasId = aliasId;
- break;
- }
- }
- if (!newAliasId) {
- var newAliasName = createDeviceAliasName(deviceAliases, deviceName);
- newAliasId = 0;
- for (aliasId in deviceAliases) {
- newAliasId = Math.max(newAliasId, aliasId);
- }
- newAliasId++;
- deviceAliases[newAliasId] = {alias: newAliasName, deviceId: deviceId};
- }
- widget.config.datasources[0].deviceAliasId = newAliasId;
-
- if (!theDashboard.configuration.widgets) {
- theDashboard.configuration.widgets = [];
- }
-
- var row = 0;
- for (var w in theDashboard.configuration.widgets) {
- var existingWidget = theDashboard.configuration.widgets[w];
- var wRow = existingWidget.row ? existingWidget.row : 0;
- var wSizeY = existingWidget.sizeY ? existingWidget.sizeY : 1;
- var bottom = wRow + wSizeY;
- row = Math.max(row, bottom);
- }
- widget.row = row;
- theDashboard.configuration.widgets.push(widget);
} else {
theDashboard = vm.newDashboard;
- deviceAliases = {};
- deviceAliases['1'] = {alias: deviceName, deviceId: deviceId};
- theDashboard.configuration = {};
- theDashboard.configuration.widgets = [];
- widget.row = 0;
- theDashboard.configuration.widgets.push(widget);
- theDashboard.configuration.deviceAliases = deviceAliases;
}
+ var aliasesInfo = {
+ datasourceAliases: {},
+ targetDeviceAliases: {}
+ };
+ aliasesInfo.datasourceAliases[0] = {
+ aliasName: deviceName,
+ deviceId: deviceId
+ };
+ theDashboard = itembuffer.addWidgetToDashboard(theDashboard, widget, aliasesInfo, 48, -1, -1);
dashboardService.saveDashboard(theDashboard).then(
function success(dashboard) {
$mdDialog.hide();
@@ -98,25 +56,6 @@ export default function AddWidgetToDashboardDialogController($scope, $mdDialog,
}
}
);
-
- }
-
- function createDeviceAliasName(deviceAliases, alias) {
- var c = 0;
- var newAlias = angular.copy(alias);
- var unique = false;
- while (!unique) {
- unique = true;
- for (var devAliasId in deviceAliases) {
- var devAlias = deviceAliases[devAliasId];
- if (newAlias === devAlias.alias) {
- c++;
- newAlias = alias + c;
- unique = false;
- }
- }
- }
- return newAlias;
}
}
diff --git a/ui/src/app/device/attribute/attribute-table.directive.js b/ui/src/app/device/attribute/attribute-table.directive.js
index 0a5e0cd..24e9022 100644
--- a/ui/src/app/device/attribute/attribute-table.directive.js
+++ b/ui/src/app/device/attribute/attribute-table.directive.js
@@ -239,6 +239,8 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
index: 0
}
scope.widgetsBundle = null;
+ scope.firstBundle = true;
+ scope.selectedWidgetsBundleAlias = types.systemBundleAlias.cards;
scope.deviceAliases = {};
scope.deviceAliases['1'] = {alias: scope.deviceName, deviceId: scope.deviceId};
@@ -326,13 +328,6 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
}
}
});
-
- widgetService.getWidgetsBundleByAlias(types.systemBundleAlias.cards).then(
- function success(widgetsBundle) {
- scope.firstBundle = true;
- scope.widgetsBundle = widgetsBundle;
- }
- );
}
scope.exitWidgetMode = function() {
@@ -344,6 +339,7 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
scope.widgetsIndexWatch();
scope.widgetsIndexWatch = null;
}
+ scope.selectedWidgetsBundleAlias = null;
scope.mode = 'default';
scope.getDeviceAttributes(true);
}
diff --git a/ui/src/app/device/attribute/attribute-table.tpl.html b/ui/src/app/device/attribute/attribute-table.tpl.html
index 880d880..a88fe02 100644
--- a/ui/src/app/device/attribute/attribute-table.tpl.html
+++ b/ui/src/app/device/attribute/attribute-table.tpl.html
@@ -105,7 +105,8 @@
<tb-widgets-bundle-select flex-offset="5"
flex
ng-model="widgetsBundle"
- select-first-bundle="false">
+ select-first-bundle="false"
+ select-bundle-alias="selectedWidgetsBundleAlias">
</tb-widgets-bundle-select>
</div>
<md-button ng-show="widgetsList.length > 0" class="md-accent md-hue-2 md-raised" ng-click="addWidgetToDashboard($event)">
ui/src/app/services/item-buffer.service.js 191(+191 -0)
diff --git a/ui/src/app/services/item-buffer.service.js b/ui/src/app/services/item-buffer.service.js
new file mode 100644
index 0000000..56d8d6f
--- /dev/null
+++ b/ui/src/app/services/item-buffer.service.js
@@ -0,0 +1,191 @@
+/*
+ * Copyright © 2016 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 angularStorage from 'angular-storage';
+
+export default angular.module('thingsboard.itembuffer', [angularStorage])
+ .factory('itembuffer', ItemBuffer)
+ .factory('bufferStore', function(store) {
+ var newStore = store.getNamespacedStore('tbBufferStore', null, null, false);
+ return newStore;
+ })
+ .name;
+
+/*@ngInject*/
+function ItemBuffer(bufferStore) {
+
+ const WIDGET_ITEM = "widget_item";
+
+ var service = {
+ copyWidget: copyWidget,
+ hasWidget: hasWidget,
+ pasteWidget: pasteWidget,
+ addWidgetToDashboard: addWidgetToDashboard
+ }
+
+ return service;
+
+ /**
+ aliasesInfo {
+ datasourceAliases: {
+ datasourceIndex: {
+ aliasName: "...",
+ deviceId: "..."
+ }
+ }
+ targetDeviceAliases: {
+ targetDeviceAliasIndex: {
+ aliasName: "...",
+ deviceId: "..."
+ }
+ }
+ ....
+ }
+ **/
+
+ function copyWidget(widget, aliasesInfo, originalColumns) {
+ var widgetItem = {
+ widget: widget,
+ aliasesInfo: aliasesInfo,
+ originalColumns: originalColumns
+ }
+ bufferStore.set(WIDGET_ITEM, angular.toJson(widgetItem));
+ }
+
+ function hasWidget() {
+ return bufferStore.get(WIDGET_ITEM);
+ }
+
+ function pasteWidget(targetDasgboard, position) {
+ var widgetItemJson = bufferStore.get(WIDGET_ITEM);
+ if (widgetItemJson) {
+ var widgetItem = angular.fromJson(widgetItemJson);
+ var widget = widgetItem.widget;
+ var aliasesInfo = widgetItem.aliasesInfo;
+ var originalColumns = widgetItem.originalColumns;
+ var targetRow = -1;
+ var targetColumn = -1;
+ if (position) {
+ targetRow = position.row;
+ targetColumn = position.column;
+ }
+ addWidgetToDashboard(targetDasgboard, widget, aliasesInfo, originalColumns, targetRow, targetColumn);
+ }
+ }
+
+ function addWidgetToDashboard(dashboard, widget, aliasesInfo, originalColumns, row, column) {
+ var theDashboard;
+ if (dashboard) {
+ theDashboard = dashboard;
+ } else {
+ theDashboard = {};
+ }
+ if (!theDashboard.configuration) {
+ theDashboard.configuration = {};
+ }
+ if (!theDashboard.configuration.deviceAliases) {
+ theDashboard.configuration.deviceAliases = {};
+ }
+ updateAliases(theDashboard, widget, aliasesInfo);
+
+ if (!theDashboard.configuration.widgets) {
+ theDashboard.configuration.widgets = [];
+ }
+ var targetColumns = 24;
+ if (theDashboard.configuration.gridSettings &&
+ theDashboard.configuration.gridSettings.columns) {
+ targetColumns = theDashboard.configuration.gridSettings.columns;
+ }
+ if (targetColumns != originalColumns) {
+ var ratio = targetColumns / originalColumns;
+ widget.sizeX *= ratio;
+ widget.sizeY *= ratio;
+ }
+ if (row > -1 && column > - 1) {
+ widget.row = row;
+ widget.col = column;
+ } else {
+ row = 0;
+ for (var w in theDashboard.configuration.widgets) {
+ var existingWidget = theDashboard.configuration.widgets[w];
+ var wRow = existingWidget.row ? existingWidget.row : 0;
+ var wSizeY = existingWidget.sizeY ? existingWidget.sizeY : 1;
+ var bottom = wRow + wSizeY;
+ row = Math.max(row, bottom);
+ }
+ widget.row = row;
+ widget.col = 0;
+ }
+ theDashboard.configuration.widgets.push(widget);
+ return theDashboard;
+ }
+
+ function updateAliases(dashboard, widget, aliasesInfo) {
+ var deviceAliases = dashboard.configuration.deviceAliases;
+ var aliasInfo;
+ var newAliasId;
+ for (var datasourceIndex in aliasesInfo.datasourceAliases) {
+ aliasInfo = aliasesInfo.datasourceAliases[datasourceIndex];
+ newAliasId = getDeviceAliasId(deviceAliases, aliasInfo);
+ widget.config.datasources[datasourceIndex].deviceAliasId = newAliasId;
+ }
+ for (var targetDeviceAliasIndex in aliasesInfo.targetDeviceAliases) {
+ aliasInfo = aliasesInfo.targetDeviceAliases[targetDeviceAliasIndex];
+ newAliasId = getDeviceAliasId(deviceAliases, aliasInfo);
+ widget.config.targetDeviceAliasIds[targetDeviceAliasIndex] = newAliasId;
+ }
+ }
+
+ function getDeviceAliasId(deviceAliases, aliasInfo) {
+ var newAliasId;
+ for (var aliasId in deviceAliases) {
+ if (deviceAliases[aliasId].deviceId === aliasInfo.deviceId) {
+ newAliasId = aliasId;
+ break;
+ }
+ }
+ if (!newAliasId) {
+ var newAliasName = createDeviceAliasName(deviceAliases, aliasInfo.aliasName);
+ newAliasId = 0;
+ for (aliasId in deviceAliases) {
+ newAliasId = Math.max(newAliasId, aliasId);
+ }
+ newAliasId++;
+ deviceAliases[newAliasId] = {alias: newAliasName, deviceId: aliasInfo.deviceId};
+ }
+ return newAliasId;
+ }
+
+ function createDeviceAliasName(deviceAliases, alias) {
+ var c = 0;
+ var newAlias = angular.copy(alias);
+ var unique = false;
+ while (!unique) {
+ unique = true;
+ for (var devAliasId in deviceAliases) {
+ var devAlias = deviceAliases[devAliasId];
+ if (newAlias === devAlias.alias) {
+ c++;
+ newAlias = alias + c;
+ unique = false;
+ }
+ }
+ }
+ return newAlias;
+ }
+
+
+}
\ No newline at end of file
ui/src/locale/en_US.json 7(+5 -2)
diff --git a/ui/src/locale/en_US.json b/ui/src/locale/en_US.json
index 9da68d6..5de8938 100644
--- a/ui/src/locale/en_US.json
+++ b/ui/src/locale/en_US.json
@@ -38,7 +38,9 @@
"create": "Create",
"drag": "Drag",
"refresh": "Refresh",
- "undo": "Undo"
+ "undo": "Undo",
+ "copy": "Copy",
+ "paste": "Paste"
},
"admin": {
"general": "General",
@@ -211,7 +213,8 @@
"vertical-margin": "Vertical margin",
"vertical-margin-required": "Vertical margin value is required.",
"min-vertical-margin-message": "Only 0 is allowed as minimum vertical margin value.",
- "max-vertical-margin-message": "Only 50 is allowed as maximum vertical margin value."
+ "max-vertical-margin-message": "Only 50 is allowed as maximum vertical margin value.",
+ "display-title": "Display dashboard title"
},
"datakey": {
"settings": "Settings",
ui/src/scss/main.scss 3(+3 -0)
diff --git a/ui/src/scss/main.scss b/ui/src/scss/main.scss
index e8f8283..b9b869e 100644
--- a/ui/src/scss/main.scss
+++ b/ui/src/scss/main.scss
@@ -169,6 +169,9 @@ md-menu-item {
md-menu-item {
.md-button {
display: block;
+ .tb-alt-text {
+ float: right;
+ }
}
}