72 changed files with 4941 additions and 713 deletions
@ -0,0 +1,46 @@ |
|||
/** |
|||
* Copyright © 2016-2017 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. |
|||
*/ |
|||
package org.thingsboard.server.dao.device; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.relation.EntityRelation; |
|||
import org.thingsboard.server.dao.relation.RelationsSearchParameters; |
|||
import org.thingsboard.server.dao.relation.EntityRelationsQuery; |
|||
import org.thingsboard.server.dao.relation.EntityTypeFilter; |
|||
|
|||
import javax.annotation.Nullable; |
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
|
|||
@Data |
|||
public class DeviceSearchQuery { |
|||
|
|||
private RelationsSearchParameters parameters; |
|||
@Nullable |
|||
private String relationType; |
|||
@Nullable |
|||
private List<String> deviceTypes; |
|||
|
|||
public EntityRelationsQuery toEntitySearchQuery() { |
|||
EntityRelationsQuery query = new EntityRelationsQuery(); |
|||
query.setParameters(parameters); |
|||
query.setFilters( |
|||
Collections.singletonList(new EntityTypeFilter(relationType == null ? EntityRelation.CONTAINS_TYPE : relationType, |
|||
Collections.singletonList(EntityType.DEVICE)))); |
|||
return query; |
|||
} |
|||
} |
|||
@ -0,0 +1,283 @@ |
|||
/** |
|||
* Copyright © 2016-2017 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. |
|||
*/ |
|||
package org.thingsboard.server.dao.service; |
|||
|
|||
import com.datastax.driver.core.utils.UUIDs; |
|||
import com.google.common.util.concurrent.ListenableFuture; |
|||
import org.junit.After; |
|||
import org.junit.Assert; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.asset.Asset; |
|||
import org.thingsboard.server.common.data.id.AssetId; |
|||
import org.thingsboard.server.common.data.id.DeviceId; |
|||
import org.thingsboard.server.common.data.relation.EntityRelation; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.relation.EntityRelationsQuery; |
|||
import org.thingsboard.server.dao.relation.EntitySearchDirection; |
|||
import org.thingsboard.server.dao.relation.EntityTypeFilter; |
|||
import org.thingsboard.server.dao.relation.RelationsSearchParameters; |
|||
|
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
import java.util.concurrent.ExecutionException; |
|||
|
|||
public class RelationServiceImplTest extends AbstractServiceTest { |
|||
|
|||
@Before |
|||
public void before() { |
|||
} |
|||
|
|||
@After |
|||
public void after() { |
|||
} |
|||
|
|||
@Test |
|||
public void testSaveRelation() throws ExecutionException, InterruptedException { |
|||
AssetId parentId = new AssetId(UUIDs.timeBased()); |
|||
AssetId childId = new AssetId(UUIDs.timeBased()); |
|||
|
|||
EntityRelation relation = new EntityRelation(parentId, childId, EntityRelation.CONTAINS_TYPE); |
|||
|
|||
Assert.assertTrue(saveRelation(relation)); |
|||
|
|||
Assert.assertTrue(relationService.checkRelation(parentId, childId, EntityRelation.CONTAINS_TYPE).get()); |
|||
|
|||
Assert.assertFalse(relationService.checkRelation(parentId, childId, "NOT_EXISTING_TYPE").get()); |
|||
|
|||
Assert.assertFalse(relationService.checkRelation(childId, parentId, EntityRelation.CONTAINS_TYPE).get()); |
|||
|
|||
Assert.assertFalse(relationService.checkRelation(childId, parentId, "NOT_EXISTING_TYPE").get()); |
|||
} |
|||
|
|||
@Test |
|||
public void testDeleteRelation() throws ExecutionException, InterruptedException { |
|||
AssetId parentId = new AssetId(UUIDs.timeBased()); |
|||
AssetId childId = new AssetId(UUIDs.timeBased()); |
|||
AssetId subChildId = new AssetId(UUIDs.timeBased()); |
|||
|
|||
EntityRelation relationA = new EntityRelation(parentId, childId, EntityRelation.CONTAINS_TYPE); |
|||
EntityRelation relationB = new EntityRelation(childId, subChildId, EntityRelation.CONTAINS_TYPE); |
|||
|
|||
saveRelation(relationA); |
|||
saveRelation(relationB); |
|||
|
|||
Assert.assertTrue(relationService.deleteRelation(relationA).get()); |
|||
|
|||
Assert.assertFalse(relationService.checkRelation(parentId, childId, EntityRelation.CONTAINS_TYPE).get()); |
|||
|
|||
Assert.assertTrue(relationService.checkRelation(childId, subChildId, EntityRelation.CONTAINS_TYPE).get()); |
|||
|
|||
Assert.assertTrue(relationService.deleteRelation(childId, subChildId, EntityRelation.CONTAINS_TYPE).get()); |
|||
} |
|||
|
|||
@Test |
|||
public void testDeleteEntityRelations() throws ExecutionException, InterruptedException { |
|||
AssetId parentId = new AssetId(UUIDs.timeBased()); |
|||
AssetId childId = new AssetId(UUIDs.timeBased()); |
|||
AssetId subChildId = new AssetId(UUIDs.timeBased()); |
|||
|
|||
EntityRelation relationA = new EntityRelation(parentId, childId, EntityRelation.CONTAINS_TYPE); |
|||
EntityRelation relationB = new EntityRelation(childId, subChildId, EntityRelation.CONTAINS_TYPE); |
|||
|
|||
saveRelation(relationA); |
|||
saveRelation(relationB); |
|||
|
|||
Assert.assertTrue(relationService.deleteEntityRelations(childId).get()); |
|||
|
|||
Assert.assertFalse(relationService.checkRelation(parentId, childId, EntityRelation.CONTAINS_TYPE).get()); |
|||
|
|||
Assert.assertFalse(relationService.checkRelation(childId, subChildId, EntityRelation.CONTAINS_TYPE).get()); |
|||
} |
|||
|
|||
@Test |
|||
public void testFindFrom() throws ExecutionException, InterruptedException { |
|||
AssetId parentA = new AssetId(UUIDs.timeBased()); |
|||
AssetId parentB = new AssetId(UUIDs.timeBased()); |
|||
AssetId childA = new AssetId(UUIDs.timeBased()); |
|||
AssetId childB = new AssetId(UUIDs.timeBased()); |
|||
|
|||
EntityRelation relationA1 = new EntityRelation(parentA, childA, EntityRelation.CONTAINS_TYPE); |
|||
EntityRelation relationA2 = new EntityRelation(parentA, childB, EntityRelation.CONTAINS_TYPE); |
|||
|
|||
EntityRelation relationB1 = new EntityRelation(parentB, childA, EntityRelation.MANAGES_TYPE); |
|||
EntityRelation relationB2 = new EntityRelation(parentB, childB, EntityRelation.MANAGES_TYPE); |
|||
|
|||
saveRelation(relationA1); |
|||
saveRelation(relationA2); |
|||
|
|||
saveRelation(relationB1); |
|||
saveRelation(relationB2); |
|||
|
|||
List<EntityRelation> relations = relationService.findByFrom(parentA).get(); |
|||
Assert.assertEquals(2, relations.size()); |
|||
for (EntityRelation relation : relations) { |
|||
Assert.assertEquals(EntityRelation.CONTAINS_TYPE, relation.getType()); |
|||
Assert.assertEquals(parentA, relation.getFrom()); |
|||
Assert.assertTrue(childA.equals(relation.getTo()) || childB.equals(relation.getTo())); |
|||
} |
|||
|
|||
relations = relationService.findByFromAndType(parentA, EntityRelation.CONTAINS_TYPE).get(); |
|||
Assert.assertEquals(2, relations.size()); |
|||
|
|||
relations = relationService.findByFromAndType(parentA, EntityRelation.MANAGES_TYPE).get(); |
|||
Assert.assertEquals(0, relations.size()); |
|||
|
|||
relations = relationService.findByFrom(parentB).get(); |
|||
Assert.assertEquals(2, relations.size()); |
|||
for (EntityRelation relation : relations) { |
|||
Assert.assertEquals(EntityRelation.MANAGES_TYPE, relation.getType()); |
|||
Assert.assertEquals(parentB, relation.getFrom()); |
|||
Assert.assertTrue(childA.equals(relation.getTo()) || childB.equals(relation.getTo())); |
|||
} |
|||
|
|||
relations = relationService.findByFromAndType(parentB, EntityRelation.CONTAINS_TYPE).get(); |
|||
Assert.assertEquals(0, relations.size()); |
|||
|
|||
relations = relationService.findByFromAndType(parentB, EntityRelation.CONTAINS_TYPE).get(); |
|||
Assert.assertEquals(0, relations.size()); |
|||
} |
|||
|
|||
private Boolean saveRelation(EntityRelation relationA1) throws ExecutionException, InterruptedException { |
|||
return relationService.saveRelation(relationA1).get(); |
|||
} |
|||
|
|||
@Test |
|||
public void testFindTo() throws ExecutionException, InterruptedException { |
|||
AssetId parentA = new AssetId(UUIDs.timeBased()); |
|||
AssetId parentB = new AssetId(UUIDs.timeBased()); |
|||
AssetId childA = new AssetId(UUIDs.timeBased()); |
|||
AssetId childB = new AssetId(UUIDs.timeBased()); |
|||
|
|||
EntityRelation relationA1 = new EntityRelation(parentA, childA, EntityRelation.CONTAINS_TYPE); |
|||
EntityRelation relationA2 = new EntityRelation(parentA, childB, EntityRelation.CONTAINS_TYPE); |
|||
|
|||
EntityRelation relationB1 = new EntityRelation(parentB, childA, EntityRelation.MANAGES_TYPE); |
|||
EntityRelation relationB2 = new EntityRelation(parentB, childB, EntityRelation.MANAGES_TYPE); |
|||
|
|||
saveRelation(relationA1); |
|||
saveRelation(relationA2); |
|||
|
|||
saveRelation(relationB1); |
|||
saveRelation(relationB2); |
|||
|
|||
// Data propagation to views is async
|
|||
Thread.sleep(3000); |
|||
|
|||
List<EntityRelation> relations = relationService.findByTo(childA).get(); |
|||
Assert.assertEquals(2, relations.size()); |
|||
for (EntityRelation relation : relations) { |
|||
Assert.assertEquals(childA, relation.getTo()); |
|||
Assert.assertTrue(parentA.equals(relation.getFrom()) || parentB.equals(relation.getFrom())); |
|||
} |
|||
|
|||
relations = relationService.findByToAndType(childA, EntityRelation.CONTAINS_TYPE).get(); |
|||
Assert.assertEquals(1, relations.size()); |
|||
|
|||
relations = relationService.findByToAndType(childB, EntityRelation.MANAGES_TYPE).get(); |
|||
Assert.assertEquals(1, relations.size()); |
|||
|
|||
relations = relationService.findByToAndType(parentA, EntityRelation.MANAGES_TYPE).get(); |
|||
Assert.assertEquals(0, relations.size()); |
|||
|
|||
relations = relationService.findByToAndType(parentB, EntityRelation.MANAGES_TYPE).get(); |
|||
Assert.assertEquals(0, relations.size()); |
|||
|
|||
relations = relationService.findByTo(childB).get(); |
|||
Assert.assertEquals(2, relations.size()); |
|||
for (EntityRelation relation : relations) { |
|||
Assert.assertEquals(childB, relation.getTo()); |
|||
Assert.assertTrue(parentA.equals(relation.getFrom()) || parentB.equals(relation.getFrom())); |
|||
} |
|||
} |
|||
|
|||
@Test |
|||
public void testCyclicRecursiveRelation() throws ExecutionException, InterruptedException { |
|||
// A -> B -> C -> A
|
|||
AssetId assetA = new AssetId(UUIDs.timeBased()); |
|||
AssetId assetB = new AssetId(UUIDs.timeBased()); |
|||
AssetId assetC = new AssetId(UUIDs.timeBased()); |
|||
|
|||
EntityRelation relationA = new EntityRelation(assetA, assetB, EntityRelation.CONTAINS_TYPE); |
|||
EntityRelation relationB = new EntityRelation(assetB, assetC, EntityRelation.CONTAINS_TYPE); |
|||
EntityRelation relationC = new EntityRelation(assetC, assetA, EntityRelation.CONTAINS_TYPE); |
|||
|
|||
saveRelation(relationA); |
|||
saveRelation(relationB); |
|||
saveRelation(relationC); |
|||
|
|||
EntityRelationsQuery query = new EntityRelationsQuery(); |
|||
query.setParameters(new RelationsSearchParameters(assetA, EntitySearchDirection.FROM, -1)); |
|||
query.setFilters(Collections.singletonList(new EntityTypeFilter(EntityRelation.CONTAINS_TYPE, Collections.singletonList(EntityType.ASSET)))); |
|||
List<EntityRelation> relations = relationService.findByQuery(query).get(); |
|||
Assert.assertEquals(3, relations.size()); |
|||
Assert.assertTrue(relations.contains(relationA)); |
|||
Assert.assertTrue(relations.contains(relationB)); |
|||
Assert.assertTrue(relations.contains(relationC)); |
|||
} |
|||
|
|||
@Test |
|||
public void testRecursiveRelation() throws ExecutionException, InterruptedException { |
|||
// A -> B -> [C,D]
|
|||
AssetId assetA = new AssetId(UUIDs.timeBased()); |
|||
AssetId assetB = new AssetId(UUIDs.timeBased()); |
|||
AssetId assetC = new AssetId(UUIDs.timeBased()); |
|||
DeviceId deviceD = new DeviceId(UUIDs.timeBased()); |
|||
|
|||
EntityRelation relationAB = new EntityRelation(assetA, assetB, EntityRelation.CONTAINS_TYPE); |
|||
EntityRelation relationBC = new EntityRelation(assetB, assetC, EntityRelation.CONTAINS_TYPE); |
|||
EntityRelation relationBD = new EntityRelation(assetB, deviceD, EntityRelation.CONTAINS_TYPE); |
|||
|
|||
|
|||
saveRelation(relationAB); |
|||
saveRelation(relationBC); |
|||
saveRelation(relationBD); |
|||
|
|||
EntityRelationsQuery query = new EntityRelationsQuery(); |
|||
query.setParameters(new RelationsSearchParameters(assetA, EntitySearchDirection.FROM, -1)); |
|||
query.setFilters(Collections.singletonList(new EntityTypeFilter(EntityRelation.CONTAINS_TYPE, Collections.singletonList(EntityType.ASSET)))); |
|||
List<EntityRelation> relations = relationService.findByQuery(query).get(); |
|||
Assert.assertEquals(2, relations.size()); |
|||
Assert.assertTrue(relations.contains(relationAB)); |
|||
Assert.assertTrue(relations.contains(relationBC)); |
|||
} |
|||
|
|||
|
|||
@Test(expected = DataValidationException.class) |
|||
public void testSaveRelationWithEmptyFrom() throws ExecutionException, InterruptedException { |
|||
EntityRelation relation = new EntityRelation(); |
|||
relation.setTo(new AssetId(UUIDs.timeBased())); |
|||
relation.setType(EntityRelation.CONTAINS_TYPE); |
|||
Assert.assertTrue(saveRelation(relation)); |
|||
} |
|||
|
|||
@Test(expected = DataValidationException.class) |
|||
public void testSaveRelationWithEmptyTo() throws ExecutionException, InterruptedException { |
|||
EntityRelation relation = new EntityRelation(); |
|||
relation.setFrom(new AssetId(UUIDs.timeBased())); |
|||
relation.setType(EntityRelation.CONTAINS_TYPE); |
|||
Assert.assertTrue(saveRelation(relation)); |
|||
} |
|||
|
|||
@Test(expected = DataValidationException.class) |
|||
public void testSaveRelationWithEmptyType() throws ExecutionException, InterruptedException { |
|||
EntityRelation relation = new EntityRelation(); |
|||
relation.setFrom(new AssetId(UUIDs.timeBased())); |
|||
relation.setTo(new AssetId(UUIDs.timeBased())); |
|||
Assert.assertTrue(saveRelation(relation)); |
|||
} |
|||
} |
|||
@ -0,0 +1,128 @@ |
|||
/* |
|||
* Copyright © 2016-2017 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 './related-entity-autocomplete.scss'; |
|||
|
|||
/* eslint-disable import/no-unresolved, import/default */ |
|||
|
|||
import relatedEntityAutocompleteTemplate from './related-entity-autocomplete.tpl.html'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
|
|||
export default angular.module('thingsboard.directives.relatedEntityAutocomplete', []) |
|||
.directive('tbRelatedEntityAutocomplete', RelatedEntityAutocomplete) |
|||
.name; |
|||
|
|||
/*@ngInject*/ |
|||
function RelatedEntityAutocomplete($compile, $templateCache, $q, $filter, entityService) { |
|||
|
|||
var linker = function (scope, element, attrs, ngModelCtrl) { |
|||
var template = $templateCache.get(relatedEntityAutocompleteTemplate); |
|||
element.html(template); |
|||
|
|||
scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false; |
|||
scope.entity = null; |
|||
scope.entitySearchText = ''; |
|||
|
|||
scope.allEntities = null; |
|||
|
|||
scope.fetchEntities = function(searchText) { |
|||
var deferred = $q.defer(); |
|||
if (!scope.allEntities) { |
|||
entityService.getRelatedEntities(scope.rootEntityId, scope.entityType, scope.entitySubtypes, -1, []).then( |
|||
function success(entities) { |
|||
if (scope.excludeEntityId) { |
|||
var result = $filter('filter')(entities, {id: {id: scope.excludeEntityId.id} }, true); |
|||
result = $filter('filter')(result, {id: {entityType: scope.excludeEntityId.entityType} }, true); |
|||
if (result && result.length) { |
|||
var excludeEntity = result[0]; |
|||
var index = entities.indexOf(excludeEntity); |
|||
if (index > -1) { |
|||
entities.splice(index, 1); |
|||
} |
|||
} |
|||
} |
|||
scope.allEntities = entities; |
|||
filterEntities(searchText, deferred); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
} else { |
|||
filterEntities(searchText, deferred); |
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function filterEntities(searchText, deferred) { |
|||
var result = $filter('filter')(scope.allEntities, {name: searchText}); |
|||
deferred.resolve(result); |
|||
} |
|||
|
|||
scope.entitySearchTextChanged = function() { |
|||
} |
|||
|
|||
scope.updateView = function () { |
|||
if (!scope.disabled) { |
|||
ngModelCtrl.$setViewValue(scope.entity ? scope.entity.id : null); |
|||
} |
|||
} |
|||
|
|||
ngModelCtrl.$render = function () { |
|||
if (ngModelCtrl.$viewValue) { |
|||
entityService.getRelatedEntity(ngModelCtrl.$viewValue).then( |
|||
function success(entity) { |
|||
scope.entity = entity; |
|||
}, |
|||
function fail() { |
|||
scope.entity = null; |
|||
} |
|||
); |
|||
} else { |
|||
scope.entity = null; |
|||
} |
|||
} |
|||
|
|||
scope.$watch('entity', function () { |
|||
scope.updateView(); |
|||
}); |
|||
|
|||
scope.$watch('disabled', function () { |
|||
scope.updateView(); |
|||
}); |
|||
|
|||
$compile(element.contents())(scope); |
|||
} |
|||
|
|||
return { |
|||
restrict: "E", |
|||
require: "^ngModel", |
|||
link: linker, |
|||
scope: { |
|||
rootEntityId: '=', |
|||
entityType: '=', |
|||
entitySubtypes: '=', |
|||
excludeEntityId: '=?', |
|||
theForm: '=?', |
|||
tbRequired: '=?', |
|||
disabled:'=ngDisabled', |
|||
placeholderText: '@', |
|||
notFoundText: '@', |
|||
requiredText: '@' |
|||
} |
|||
}; |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
/** |
|||
* Copyright © 2016-2017 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. |
|||
*/ |
|||
.tb-related-entity-autocomplete { |
|||
.tb-not-found { |
|||
display: block; |
|||
line-height: 1.5; |
|||
height: 48px; |
|||
} |
|||
.tb-entity-item { |
|||
display: block; |
|||
height: 48px; |
|||
} |
|||
li { |
|||
height: auto !important; |
|||
white-space: normal !important; |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2017 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. |
|||
|
|||
--> |
|||
<md-autocomplete ng-required="tbRequired" |
|||
ng-disabled="disabled" |
|||
md-input-name="entity" |
|||
ng-model="entity" |
|||
md-selected-item="entity" |
|||
md-search-text="entitySearchText" |
|||
md-search-text-change="entitySearchTextChanged()" |
|||
md-items="item in fetchEntities(entitySearchText)" |
|||
md-item-text="item.name" |
|||
md-min-length="0" |
|||
md-floating-label="{{ placeholderText | translate }}" |
|||
md-menu-class="tb-related-entity-autocomplete"> |
|||
<md-item-template> |
|||
<div class="tb-entity-item"> |
|||
<span md-highlight-text="entitySearchText" md-highlight-flags="^i">{{item.name}}</span> |
|||
</div> |
|||
</md-item-template> |
|||
<md-not-found> |
|||
<div class="tb-not-found"> |
|||
<span>{{ notFoundText | translate:{entity: entitySearchText} }}</span> |
|||
</div> |
|||
</md-not-found> |
|||
<div ng-messages="theForm.entity.$error"> |
|||
<div ng-message="required">{{ requiredText | translate }}</div> |
|||
</div> |
|||
</md-autocomplete> |
|||
File diff suppressed because it is too large
@ -0,0 +1,277 @@ |
|||
/* |
|||
* Copyright © 2016-2017 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. |
|||
*/ |
|||
/* eslint-disable import/no-unresolved, import/default */ |
|||
|
|||
import dashboardLayoutTemplate from './dashboard-layout.tpl.html'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
/*@ngInject*/ |
|||
export default function DashboardLayout() { |
|||
return { |
|||
restrict: "E", |
|||
scope: true, |
|||
bindToController: { |
|||
layoutCtx: '=', |
|||
dashboardCtx: '=', |
|||
isEdit: '=', |
|||
isMobile: '=', |
|||
widgetEditMode: '=', |
|||
getStDiff: '&?' |
|||
}, |
|||
controller: DashboardLayoutController, |
|||
controllerAs: 'vm', |
|||
templateUrl: dashboardLayoutTemplate |
|||
}; |
|||
} |
|||
|
|||
/*@ngInject*/ |
|||
function DashboardLayoutController($scope, $rootScope, $translate, $window, hotkeys, itembuffer) { |
|||
|
|||
var vm = this; |
|||
|
|||
vm.noData = noData; |
|||
vm.addWidget = addWidget; |
|||
vm.editWidget = editWidget; |
|||
vm.exportWidget = exportWidget; |
|||
vm.widgetMouseDown = widgetMouseDown; |
|||
vm.widgetClicked = widgetClicked; |
|||
vm.prepareDashboardContextMenu = prepareDashboardContextMenu; |
|||
vm.prepareWidgetContextMenu = prepareWidgetContextMenu; |
|||
vm.removeWidget = removeWidget; |
|||
vm.pasteWidget = pasteWidget; |
|||
vm.pasteWidgetReference = pasteWidgetReference; |
|||
vm.dashboardInited = dashboardInited; |
|||
vm.dashboardInitFailed = dashboardInitFailed; |
|||
|
|||
vm.reload = function() { |
|||
if (vm.dashboardContainer) { |
|||
vm.dashboardContainer.reload(); |
|||
} |
|||
}; |
|||
|
|||
vm.setResizing = function(resizing) { |
|||
if (vm.dashboardContainer) { |
|||
vm.dashboardContainer.isResizing = resizing; |
|||
} |
|||
} |
|||
|
|||
vm.resetHighlight = function() { |
|||
if (vm.dashboardContainer) { |
|||
vm.dashboardContainer.resetHighlight(); |
|||
} |
|||
}; |
|||
|
|||
vm.highlightWidget = function(widget, delay) { |
|||
if (vm.dashboardContainer) { |
|||
vm.dashboardContainer.highlightWidget(widget, delay); |
|||
} |
|||
}; |
|||
|
|||
vm.selectWidget = function(widget, delay) { |
|||
if (vm.dashboardContainer) { |
|||
vm.dashboardContainer.selectWidget(widget, delay); |
|||
} |
|||
}; |
|||
|
|||
vm.dashboardInitComplete = false; |
|||
|
|||
initHotKeys(); |
|||
|
|||
$scope.$on('$destroy', function() { |
|||
vm.dashboardContainer = null; |
|||
}); |
|||
|
|||
$scope.$watch('vm.layoutCtx', function () { |
|||
if (vm.layoutCtx) { |
|||
vm.layoutCtx.ctrl = vm; |
|||
} |
|||
}); |
|||
|
|||
function noData() { |
|||
return vm.dashboardInitComplete && vm.layoutCtx && |
|||
vm.layoutCtx.widgets && vm.layoutCtx.widgets.length == 0; |
|||
} |
|||
|
|||
function addWidget($event) { |
|||
if (vm.dashboardCtx.onAddWidget) { |
|||
vm.dashboardCtx.onAddWidget($event, vm.layoutCtx); |
|||
} |
|||
} |
|||
|
|||
function editWidget($event, widget) { |
|||
if (vm.dashboardCtx.onEditWidget) { |
|||
vm.dashboardCtx.onEditWidget($event, vm.layoutCtx, widget); |
|||
} |
|||
} |
|||
|
|||
function exportWidget($event, widget) { |
|||
if (vm.dashboardCtx.onExportWidget) { |
|||
vm.dashboardCtx.onExportWidget($event, vm.layoutCtx, widget); |
|||
} |
|||
} |
|||
|
|||
function widgetMouseDown($event, widget) { |
|||
if (vm.dashboardCtx.onWidgetMouseDown) { |
|||
vm.dashboardCtx.onWidgetMouseDown($event, vm.layoutCtx, widget); |
|||
} |
|||
} |
|||
|
|||
function widgetClicked($event, widget) { |
|||
if (vm.dashboardCtx.onWidgetClicked) { |
|||
vm.dashboardCtx.onWidgetClicked($event, vm.layoutCtx, widget); |
|||
} |
|||
} |
|||
|
|||
function prepareDashboardContextMenu() { |
|||
if (vm.dashboardCtx.prepareDashboardContextMenu) { |
|||
return vm.dashboardCtx.prepareDashboardContextMenu(vm.layoutCtx); |
|||
} |
|||
} |
|||
|
|||
function prepareWidgetContextMenu(widget) { |
|||
if (vm.dashboardCtx.prepareWidgetContextMenu) { |
|||
return vm.dashboardCtx.prepareWidgetContextMenu(vm.layoutCtx, widget); |
|||
} |
|||
} |
|||
|
|||
function removeWidget($event, widget) { |
|||
if (vm.dashboardCtx.onRemoveWidget) { |
|||
vm.dashboardCtx.onRemoveWidget($event, vm.layoutCtx, widget); |
|||
} |
|||
} |
|||
|
|||
function dashboardInitFailed() { |
|||
var parentScope = $window.parent.angular.element($window.frameElement).scope(); |
|||
parentScope.$emit('widgetEditModeInited'); |
|||
parentScope.$apply(); |
|||
vm.dashboardInitComplete = true; |
|||
} |
|||
|
|||
function dashboardInited(dashboardContainer) { |
|||
vm.dashboardContainer = dashboardContainer; |
|||
vm.dashboardInitComplete = true; |
|||
} |
|||
|
|||
function isHotKeyAllowed(event) { |
|||
var target = event.target || event.srcElement; |
|||
var scope = angular.element(target).scope(); |
|||
return scope && scope.$parent !== $rootScope; |
|||
} |
|||
|
|||
function initHotKeys() { |
|||
$translate(['action.copy', 'action.paste', 'action.delete']).then(function (translations) { |
|||
hotkeys.bindTo($scope) |
|||
.add({ |
|||
combo: 'ctrl+c', |
|||
description: translations['action.copy'], |
|||
callback: function (event) { |
|||
if (isHotKeyAllowed(event) && |
|||
vm.isEdit && !vm.isEditingWidget && !vm.widgetEditMode) { |
|||
var widget = vm.dashboardContainer.getSelectedWidget(); |
|||
if (widget) { |
|||
event.preventDefault(); |
|||
copyWidget(event, widget); |
|||
} |
|||
} |
|||
} |
|||
}) |
|||
.add({ |
|||
combo: 'ctrl+r', |
|||
description: translations['action.copy-reference'], |
|||
callback: function (event) { |
|||
if (isHotKeyAllowed(event) && |
|||
vm.isEdit && !vm.isEditingWidget && !vm.widgetEditMode) { |
|||
var widget = vm.dashboardContainer.getSelectedWidget(); |
|||
if (widget) { |
|||
event.preventDefault(); |
|||
copyWidgetReference(event, widget); |
|||
} |
|||
} |
|||
} |
|||
}) |
|||
.add({ |
|||
combo: 'ctrl+v', |
|||
description: translations['action.paste'], |
|||
callback: function (event) { |
|||
if (isHotKeyAllowed(event) && |
|||
vm.isEdit && !vm.isEditingWidget && !vm.widgetEditMode) { |
|||
if (itembuffer.hasWidget()) { |
|||
event.preventDefault(); |
|||
pasteWidget(event); |
|||
} |
|||
} |
|||
} |
|||
}) |
|||
.add({ |
|||
combo: 'ctrl+i', |
|||
description: translations['action.paste-reference'], |
|||
callback: function (event) { |
|||
if (isHotKeyAllowed(event) && |
|||
vm.isEdit && !vm.isEditingWidget && !vm.widgetEditMode) { |
|||
if (itembuffer.canPasteWidgetReference(vm.dashboardCtx.dashboard, |
|||
vm.dashboardCtx.state, vm.layoutCtx.id)) { |
|||
event.preventDefault(); |
|||
pasteWidgetReference(event); |
|||
} |
|||
} |
|||
} |
|||
}) |
|||
|
|||
.add({ |
|||
combo: 'ctrl+x', |
|||
description: translations['action.delete'], |
|||
callback: function (event) { |
|||
if (isHotKeyAllowed(event) && |
|||
vm.isEdit && !vm.isEditingWidget && !vm.widgetEditMode) { |
|||
var widget = vm.dashboardContainer.getSelectedWidget(); |
|||
if (widget) { |
|||
event.preventDefault(); |
|||
vm.dashboardCtx.onRemoveWidget(event, vm.layoutCtx, widget); |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
function copyWidget($event, widget) { |
|||
if (vm.dashboardCtx.copyWidget) { |
|||
vm.dashboardCtx.copyWidget($event, vm.layoutCtx, widget); |
|||
} |
|||
} |
|||
|
|||
function copyWidgetReference($event, widget) { |
|||
if (vm.dashboardCtx.copyWidgetReference) { |
|||
vm.dashboardCtx.copyWidgetReference($event, vm.layoutCtx, widget); |
|||
} |
|||
} |
|||
|
|||
function pasteWidget($event) { |
|||
var pos = vm.dashboardContainer.getEventGridPosition($event); |
|||
if (vm.dashboardCtx.pasteWidget) { |
|||
vm.dashboardCtx.pasteWidget($event, vm.layoutCtx, pos); |
|||
} |
|||
} |
|||
|
|||
function pasteWidgetReference($event) { |
|||
var pos = vm.dashboardContainer.getEventGridPosition($event); |
|||
if (vm.dashboardCtx.pasteWidgetReference) { |
|||
vm.dashboardCtx.pasteWidgetReference($event, vm.layoutCtx, pos); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2017 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. |
|||
|
|||
--> |
|||
<md-content style="position: relative; width: 100%; height: 100%;" |
|||
ng-style="{'background-color': vm.layoutCtx.gridSettings.backgroundColor, |
|||
'background-image': 'url('+vm.layoutCtx.gridSettings.backgroundImageUrl+')', |
|||
'background-repeat': 'no-repeat', |
|||
'background-attachment': 'scroll', |
|||
'background-size': vm.layoutCtx.gridSettings.backgroundSizeMode || '100%', |
|||
'background-position': '0% 0%'}"> |
|||
<section ng-show="!loading && vm.noData()" layout-align="center center" |
|||
ng-style="{'color': vm.layoutCtx.gridSettings.color}" |
|||
style="text-transform: uppercase; display: flex; z-index: 1; pointer-events: none;" |
|||
class="md-headline tb-absolute-fill"> |
|||
<span translate ng-if="!vm.isEdit"> |
|||
dashboard.no-widgets |
|||
</span> |
|||
<md-button ng-if="vm.isEdit && !vm.widgetEditMode" class="tb-add-new-widget" ng-click="vm.addWidget({event: $event})"> |
|||
<md-icon aria-label="{{ 'action.add' | translate }}" class="material-icons tb-md-96">add</md-icon> |
|||
{{ 'dashboard.add-widget' | translate }} |
|||
</md-button> |
|||
</section> |
|||
<tb-dashboard |
|||
dashboard-style="{'background-color': vm.layoutCtx.gridSettings.backgroundColor, |
|||
'background-image': 'url('+vm.layoutCtx.gridSettings.backgroundImageUrl+')', |
|||
'background-repeat': 'no-repeat', |
|||
'background-attachment': 'scroll', |
|||
'background-size': vm.layoutCtx.gridSettings.backgroundSizeMode || '100%', |
|||
'background-position': '0% 0%'}" |
|||
widgets="vm.layoutCtx.widgets" |
|||
widget-layouts="vm.layoutCtx.widgetLayouts" |
|||
columns="vm.layoutCtx.gridSettings.columns" |
|||
margins="vm.layoutCtx.gridSettings.margins" |
|||
aliases-info="vm.dashboardCtx.aliasesInfo" |
|||
state-controller="vm.dashboardCtx.stateController" |
|||
dashboard-timewindow="vm.dashboardCtx.dashboardTimewindow" |
|||
is-edit="vm.isEdit" |
|||
is-mobile="vm.isMobile" |
|||
is-mobile-disabled="vm.widgetEditMode" |
|||
is-edit-action-enabled="vm.isEdit" |
|||
is-export-action-enabled="vm.isEdit && !vm.widgetEditMode" |
|||
is-remove-action-enabled="vm.isEdit && !vm.widgetEditMode" |
|||
on-edit-widget="vm.editWidget(event, widget)" |
|||
on-export-widget="vm.exportWidget(event, widget)" |
|||
on-widget-mouse-down="vm.widgetMouseDown(event, widget)" |
|||
on-widget-clicked="vm.widgetClicked(event, widget)" |
|||
prepare-dashboard-context-menu="vm.prepareDashboardContextMenu()" |
|||
prepare-widget-context-menu="vm.prepareWidgetContextMenu(widget)" |
|||
on-remove-widget="vm.removeWidget(event, widget)" |
|||
get-st-diff="vm.getStDiff()" |
|||
on-init="vm.dashboardInited(dashboard)" |
|||
on-init-failed="vm.dashboardInitFailed(e)" |
|||
ignore-loading="vm.layoutCtx.ignoreLoading"> |
|||
</tb-dashboard> |
|||
</md-content> |
|||
@ -0,0 +1,25 @@ |
|||
/* |
|||
* Copyright © 2016-2017 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 ManageDashboardLayoutsController from './manage-dashboard-layouts.controller'; |
|||
import SelectTargetLayoutController from './select-target-layout.controller'; |
|||
import DashboardLayoutDirective from './dashboard-layout.directive'; |
|||
|
|||
export default angular.module('thingsboard.dashboard.layouts', []) |
|||
.controller('ManageDashboardLayoutsController', ManageDashboardLayoutsController) |
|||
.controller('SelectTargetLayoutController', SelectTargetLayoutController) |
|||
.directive('tbDashboardLayout', DashboardLayoutDirective) |
|||
.name; |
|||
@ -0,0 +1,83 @@ |
|||
/* |
|||
* Copyright © 2016-2017 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. |
|||
*/ |
|||
|
|||
/* eslint-disable import/no-unresolved, import/default */ |
|||
|
|||
import dashboardSettingsTemplate from '../dashboard-settings.tpl.html'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
/*@ngInject*/ |
|||
export default function ManageDashboardLayoutsController($scope, $mdDialog, $document, dashboardUtils, layouts) { |
|||
|
|||
var vm = this; |
|||
|
|||
vm.openLayoutSettings = openLayoutSettings; |
|||
vm.cancel = cancel; |
|||
vm.save = save; |
|||
|
|||
vm.layouts = layouts; |
|||
vm.displayLayouts = { |
|||
main: angular.isDefined(vm.layouts.main), |
|||
right: angular.isDefined(vm.layouts.right) |
|||
} |
|||
|
|||
for (var l in vm.displayLayouts) { |
|||
if (!vm.layouts[l]) { |
|||
vm.layouts[l] = dashboardUtils.createDefaultLayoutData(); |
|||
} |
|||
} |
|||
|
|||
function openLayoutSettings($event, layoutId) { |
|||
var gridSettings = angular.copy(vm.layouts[layoutId].gridSettings); |
|||
$mdDialog.show({ |
|||
controller: 'DashboardSettingsController', |
|||
controllerAs: 'vm', |
|||
templateUrl: dashboardSettingsTemplate, |
|||
locals: { |
|||
settings: null, |
|||
gridSettings: gridSettings |
|||
}, |
|||
parent: angular.element($document[0].body), |
|||
skipHide: true, |
|||
fullscreen: true, |
|||
targetEvent: $event |
|||
}).then(function (data) { |
|||
var gridSettings = data.gridSettings; |
|||
if (gridSettings) { |
|||
dashboardUtils.updateLayoutSettings(vm.layouts[layoutId], gridSettings); |
|||
} |
|||
$scope.theForm.$setDirty(); |
|||
}, function () { |
|||
}); |
|||
} |
|||
|
|||
function cancel() { |
|||
$mdDialog.cancel(); |
|||
} |
|||
|
|||
function save() { |
|||
$scope.theForm.$setPristine(); |
|||
for (var l in vm.displayLayouts) { |
|||
if (!vm.displayLayouts[l]) { |
|||
if (vm.layouts[l]) { |
|||
delete vm.layouts[l]; |
|||
} |
|||
} |
|||
} |
|||
$mdDialog.hide(vm.layouts); |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2017 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. |
|||
|
|||
--> |
|||
<md-dialog aria-label="{{ 'layout.manage' | translate }}"> |
|||
<form name="theForm" ng-submit="vm.save()"> |
|||
<md-toolbar> |
|||
<div class="md-toolbar-tools"> |
|||
<h2 translate>{{ 'layout.manage' }}</h2> |
|||
<span flex></span> |
|||
<md-button class="md-icon-button" ng-click="vm.cancel()"> |
|||
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> |
|||
</md-button> |
|||
</div> |
|||
</md-toolbar> |
|||
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> |
|||
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> |
|||
<md-dialog-content> |
|||
<div class="md-dialog-content"> |
|||
<fieldset ng-disabled="loading"> |
|||
<div layout="row" layout-align="start center"> |
|||
<md-checkbox ng-disabled="true" flex aria-label="{{ 'layout.main' | translate }}" |
|||
ng-model="vm.displayLayouts.main">{{ 'layout.main' | translate }} |
|||
</md-checkbox> |
|||
<md-checkbox flex aria-label="{{ 'layout.right' | translate }}" |
|||
ng-model="vm.displayLayouts.right">{{ 'layout.right' | translate }} |
|||
</md-checkbox> |
|||
</div> |
|||
<div layout="row" layout-align="start center"> |
|||
<md-button flex ng-show="vm.displayLayouts.main" |
|||
class="tb-layout-button md-raised md-primary" layout="column" |
|||
ng-click="vm.openLayoutSettings($event, 'main')"> |
|||
<span translate>layout.main</span> |
|||
</md-button> |
|||
<md-button flex ng-show="vm.displayLayouts.right" |
|||
class="tb-layout-button md-raised md-primary" layout="column" |
|||
ng-click="vm.openLayoutSettings($event, 'right')"> |
|||
<span translate>layout.right</span> |
|||
</md-button> |
|||
</div> |
|||
</fieldset> |
|||
</div> |
|||
</md-dialog-content> |
|||
<md-dialog-actions layout="row"> |
|||
<span flex></span> |
|||
<md-button ng-disabled="loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary"> |
|||
{{ 'action.save' | translate }} |
|||
</md-button> |
|||
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button> |
|||
</md-dialog-actions> |
|||
</form> |
|||
</md-dialog> |
|||
@ -0,0 +1,33 @@ |
|||
/* |
|||
* Copyright © 2016-2017 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. |
|||
*/ |
|||
|
|||
/*@ngInject*/ |
|||
export default function SelectTargetLayoutController($scope, $mdDialog) { |
|||
|
|||
var vm = this; |
|||
|
|||
vm.cancel = cancel; |
|||
vm.selectLayout = selectLayout; |
|||
|
|||
function cancel() { |
|||
$mdDialog.cancel(); |
|||
} |
|||
|
|||
function selectLayout($event, layoutId) { |
|||
$scope.theForm.$setPristine(); |
|||
$mdDialog.hide(layoutId); |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2017 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. |
|||
|
|||
--> |
|||
<md-dialog aria-label="{{ 'layout.select' | translate }}"> |
|||
<form name="theForm" ng-submit="vm.save()"> |
|||
<md-toolbar> |
|||
<div class="md-toolbar-tools"> |
|||
<h2 translate>{{ 'layout.select' }}</h2> |
|||
<span flex></span> |
|||
<md-button class="md-icon-button" ng-click="vm.cancel()"> |
|||
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> |
|||
</md-button> |
|||
</div> |
|||
</md-toolbar> |
|||
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> |
|||
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> |
|||
<md-dialog-content> |
|||
<div class="md-dialog-content"> |
|||
<fieldset ng-disabled="loading"> |
|||
<div layout="row" layout-align="start center"> |
|||
<md-button flex class="tb-layout-button md-raised md-primary" layout="column" |
|||
ng-click="vm.selectLayout($event, 'main')"> |
|||
<span translate>layout.main</span> |
|||
</md-button> |
|||
<md-button flex class="tb-layout-button md-raised md-primary" layout="column" |
|||
ng-click="vm.selectLayout($event, 'right')"> |
|||
<span translate>layout.right</span> |
|||
</md-button> |
|||
</div> |
|||
</fieldset> |
|||
</div> |
|||
</md-dialog-content> |
|||
</form> |
|||
</md-dialog> |
|||
@ -0,0 +1,82 @@ |
|||
/* |
|||
* Copyright © 2016-2017 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. |
|||
*/ |
|||
|
|||
/*@ngInject*/ |
|||
export default function DashboardStateDialogController($scope, $mdDialog, $filter, dashboardUtils, isAdd, allStates, state) { |
|||
|
|||
var vm = this; |
|||
|
|||
vm.isAdd = isAdd; |
|||
vm.allStates = allStates; |
|||
vm.state = state; |
|||
|
|||
vm.stateIdTouched = false; |
|||
|
|||
if (vm.isAdd) { |
|||
vm.state = dashboardUtils.createDefaultState('', false); |
|||
vm.state.id = ''; |
|||
vm.prevStateId = ''; |
|||
} else { |
|||
vm.state = state; |
|||
vm.prevStateId = vm.state.id; |
|||
} |
|||
|
|||
vm.cancel = cancel; |
|||
vm.save = save; |
|||
|
|||
$scope.$watch("vm.state.name", function(newVal, prevVal) { |
|||
if (!angular.equals(newVal, prevVal) && vm.state.name != null) { |
|||
checkStateName(); |
|||
} |
|||
}); |
|||
|
|||
$scope.$watch("vm.state.id", function(newVal, prevVal) { |
|||
if (!angular.equals(newVal, prevVal) && vm.state.id != null) { |
|||
checkStateId(); |
|||
} |
|||
}); |
|||
|
|||
function checkStateName() { |
|||
if (!vm.stateIdTouched && vm.isAdd) { |
|||
vm.state.id = vm.state.name.toLowerCase().replace(/\W/g,"_"); |
|||
} |
|||
var result = $filter('filter')(vm.allStates, {name: vm.state.name}, true); |
|||
if (result && result.length && result[0].id !== vm.prevStateId) { |
|||
$scope.theForm.name.$setValidity('stateExists', false); |
|||
} else { |
|||
$scope.theForm.name.$setValidity('stateExists', true); |
|||
} |
|||
} |
|||
|
|||
function checkStateId() { |
|||
var result = $filter('filter')(vm.allStates, {id: vm.state.id}, true); |
|||
if (result && result.length && result[0].id !== vm.prevStateId) { |
|||
$scope.theForm.stateId.$setValidity('stateExists', false); |
|||
} else { |
|||
$scope.theForm.stateId.$setValidity('stateExists', true); |
|||
} |
|||
} |
|||
|
|||
function cancel() { |
|||
$mdDialog.cancel(); |
|||
} |
|||
|
|||
function save() { |
|||
$scope.theForm.$setPristine(); |
|||
vm.state.id = vm.state.id.trim(); |
|||
$mdDialog.hide(vm.state); |
|||
} |
|||
} |
|||
@ -0,0 +1,72 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2017 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. |
|||
|
|||
--> |
|||
<md-dialog class="dashboard-state" aria-label="{{'dashboard.state' | translate }}" style="min-width: 600px;"> |
|||
<form name="theForm" ng-submit="vm.save()"> |
|||
<md-toolbar> |
|||
<div class="md-toolbar-tools"> |
|||
<h2>{{ (vm.isAdd ? 'dashboard.add-state' : 'dashboard.edit-state') | translate }}</h2> |
|||
<span flex></span> |
|||
<md-button class="md-icon-button" ng-click="vm.cancel()"> |
|||
<ng-md-icon icon="close" aria-label="{{ 'action.close' | translate }}"></ng-md-icon> |
|||
</md-button> |
|||
</div> |
|||
</md-toolbar> |
|||
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> |
|||
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> |
|||
<md-dialog-content> |
|||
<div class="md-dialog-content"> |
|||
<md-content class="md-padding" layout="column"> |
|||
<fieldset ng-disabled="loading"> |
|||
<md-input-container class="md-block"> |
|||
<label translate>dashboard.state-name</label> |
|||
<input name="name" required ng-model="vm.state.name"> |
|||
<div ng-messages="theForm.name.$error"> |
|||
<div ng-message="required" translate>dashboard.state-name-required</div> |
|||
<div ng-message="stateExists" translate>dashboard.state-name-exists</div> |
|||
</div> |
|||
</md-input-container> |
|||
<md-input-container class="md-block"> |
|||
<label translate>dashboard.state-id</label> |
|||
<input name="stateId" ng-model="vm.state.id" |
|||
ng-change="vm.stateIdTouched = true" |
|||
ng-pattern="/^[a-zA-Z0-9_]*$/"> |
|||
<div ng-messages="theForm.stateId.$error"> |
|||
<div ng-message="required" translate>dashboard.state-id-required</div> |
|||
<div ng-message="stateExists" translate>dashboard.state-id-exists</div> |
|||
<div ng-message="pattern" translate>dashboard.invalid-state-id-format</div> |
|||
</div> |
|||
</md-input-container> |
|||
<md-checkbox flex aria-label="{{ 'dashboard.is-root-state' | translate }}" |
|||
ng-model="vm.state.root">{{ 'dashboard.is-root-state' | translate }} |
|||
</md-checkbox> |
|||
</fieldset> |
|||
</md-content> |
|||
</div> |
|||
</md-dialog-content> |
|||
<md-dialog-actions layout="row"> |
|||
<span flex></span> |
|||
<md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" |
|||
class="md-raised md-primary"> |
|||
{{ vm.isAdd ? 'Add' : 'Save' }} |
|||
</md-button> |
|||
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;"> |
|||
Cancel |
|||
</md-button> |
|||
</md-dialog-actions> |
|||
</form> |
|||
</md-dialog> |
|||
@ -0,0 +1,181 @@ |
|||
/* |
|||
* Copyright © 2016-2017 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. |
|||
*/ |
|||
|
|||
/*@ngInject*/ |
|||
export default function DefaultStateController($scope, $location, $state, $stateParams, $translate, types, dashboardUtils) { |
|||
|
|||
var vm = this; |
|||
|
|||
vm.inited = false; |
|||
|
|||
vm.openState = openState; |
|||
vm.updateState = updateState; |
|||
vm.navigatePrevState = navigatePrevState; |
|||
vm.getStateId = getStateId; |
|||
vm.getStateParams = getStateParams; |
|||
|
|||
vm.getStateName = getStateName; |
|||
|
|||
vm.displayStateSelection = displayStateSelection; |
|||
|
|||
function openState(id, params) { |
|||
if (vm.states && vm.states[id]) { |
|||
if (!params) { |
|||
params = {}; |
|||
} |
|||
var newState = { |
|||
id: id, |
|||
params: params |
|||
} |
|||
//append new state
|
|||
vm.stateObject[0] = newState; |
|||
gotoState(vm.stateObject[0].id, true); |
|||
} |
|||
} |
|||
|
|||
function updateState(id, params) { |
|||
if (vm.states && vm.states[id]) { |
|||
if (!params) { |
|||
params = {}; |
|||
} |
|||
var newState = { |
|||
id: id, |
|||
params: params |
|||
} |
|||
//replace with new state
|
|||
vm.stateObject[0] = newState; |
|||
gotoState(vm.stateObject[0].id, true); |
|||
} |
|||
} |
|||
|
|||
function navigatePrevState(index) { |
|||
if (index < vm.stateObject.length-1) { |
|||
vm.stateObject.splice(index+1, vm.stateObject.length-index-1); |
|||
gotoState(vm.stateObject[vm.stateObject.length-1].id, true); |
|||
} |
|||
} |
|||
|
|||
function getStateId() { |
|||
return vm.stateObject[vm.stateObject.length-1].id; |
|||
} |
|||
|
|||
function getStateParams() { |
|||
return vm.stateObject[vm.stateObject.length-1].params; |
|||
} |
|||
|
|||
function getStateName(id, state) { |
|||
var result = ''; |
|||
var translationId = types.translate.dashboardStatePrefix + id; |
|||
var translation = $translate.instant(translationId); |
|||
if (translation != translationId) { |
|||
result = translation; |
|||
} else { |
|||
result = state.name; |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
function parseState(stateJson) { |
|||
var result; |
|||
if (stateJson) { |
|||
try { |
|||
result = angular.fromJson(stateJson); |
|||
} catch (e) { |
|||
result = [ { id: null, params: {} } ]; |
|||
} |
|||
} |
|||
if (!result) { |
|||
result = []; |
|||
} |
|||
if (!result.length) { |
|||
result[0] = { id: null, params: {} } |
|||
} |
|||
if (!result[0].id) { |
|||
result[0].id = dashboardUtils.getRootStateId(vm.states); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
$scope.$watch('vm.states', function() { |
|||
if (vm.states) { |
|||
if (!vm.inited) { |
|||
vm.inited = true; |
|||
init(); |
|||
} |
|||
} |
|||
}); |
|||
|
|||
function displayStateSelection() { |
|||
return vm.states && Object.keys(vm.states).length > 1; |
|||
} |
|||
|
|||
function init() { |
|||
var initialState = $stateParams.state; |
|||
vm.stateObject = parseState(initialState); |
|||
|
|||
gotoState(vm.stateObject[0].id, false); |
|||
|
|||
$scope.$watchCollection(function(){ |
|||
return $state.params; |
|||
}, function(){ |
|||
var currentState = $state.params.state; |
|||
vm.stateObject = parseState(currentState); |
|||
}); |
|||
|
|||
$scope.$watch('vm.dashboardCtrl.dashboardCtx.state', function() { |
|||
if (vm.stateObject[0].id !== vm.dashboardCtrl.dashboardCtx.state) { |
|||
stopWatchStateObject(); |
|||
vm.stateObject[0].id = vm.dashboardCtrl.dashboardCtx.state; |
|||
updateLocation(); |
|||
watchStateObject(); |
|||
} |
|||
}); |
|||
watchStateObject(); |
|||
} |
|||
|
|||
function stopWatchStateObject() { |
|||
if (vm.stateObjectWatcher) { |
|||
vm.stateObjectWatcher(); |
|||
vm.stateObjectWatcher = null; |
|||
} |
|||
} |
|||
|
|||
function watchStateObject() { |
|||
vm.stateObjectWatcher = $scope.$watch('vm.stateObject', function(newVal, prevVal) { |
|||
if (!angular.equals(newVal, prevVal) && newVal) { |
|||
gotoState(vm.stateObject[0].id, true); |
|||
} |
|||
}, true); |
|||
} |
|||
|
|||
function gotoState(stateId, update) { |
|||
if (vm.dashboardCtrl.dashboardCtx.state != stateId) { |
|||
vm.dashboardCtrl.openDashboardState(stateId); |
|||
if (update) { |
|||
updateLocation(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
function updateLocation() { |
|||
if (vm.stateObject[0].id) { |
|||
$location.search({state : angular.toJson(vm.stateObject)}); |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2017 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. |
|||
|
|||
--> |
|||
<md-select ng-show="vm.displayStateSelection()" aria-label="{{ 'dashboard.state' | translate }}" ng-model="vm.stateObject[0].id"> |
|||
<md-option ng-repeat="(stateId, state) in vm.states" ng-value="stateId"> |
|||
{{vm.getStateName(stateId, state)}} |
|||
</md-option> |
|||
</md-select> |
|||
@ -0,0 +1,243 @@ |
|||
/* |
|||
* Copyright © 2016-2017 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 './entity-state-controller.scss'; |
|||
|
|||
/*@ngInject*/ |
|||
export default function EntityStateController($scope, $location, $state, $stateParams, $q, $translate, types, dashboardUtils, entityService) { |
|||
|
|||
var vm = this; |
|||
|
|||
vm.inited = false; |
|||
|
|||
vm.openState = openState; |
|||
vm.updateState = updateState; |
|||
vm.navigatePrevState = navigatePrevState; |
|||
vm.getStateId = getStateId; |
|||
vm.getStateParams = getStateParams; |
|||
|
|||
vm.getStateName = getStateName; |
|||
|
|||
vm.selectedStateIndex = -1; |
|||
|
|||
function openState(id, params) { |
|||
if (vm.states && vm.states[id]) { |
|||
resolveEntity(params).then( |
|||
function success(entityName) { |
|||
params.entityName = entityName; |
|||
var newState = { |
|||
id: id, |
|||
params: params |
|||
} |
|||
//append new state
|
|||
vm.stateObject.push(newState); |
|||
vm.selectedStateIndex = vm.stateObject.length-1; |
|||
gotoState(vm.stateObject[vm.stateObject.length-1].id, true); |
|||
} |
|||
); |
|||
} |
|||
} |
|||
|
|||
function updateState(id, params) { |
|||
if (vm.states && vm.states[id]) { |
|||
resolveEntity(params).then( |
|||
function success(entityName) { |
|||
params.entityName = entityName; |
|||
var newState = { |
|||
id: id, |
|||
params: params |
|||
} |
|||
//replace with new state
|
|||
vm.stateObject[vm.stateObject.length - 1] = newState; |
|||
gotoState(vm.stateObject[vm.stateObject.length - 1].id, true); |
|||
} |
|||
); |
|||
} |
|||
} |
|||
|
|||
function navigatePrevState(index) { |
|||
if (index < vm.stateObject.length-1) { |
|||
vm.stateObject.splice(index+1, vm.stateObject.length-index-1); |
|||
vm.selectedStateIndex = vm.stateObject.length-1; |
|||
gotoState(vm.stateObject[vm.stateObject.length-1].id, true); |
|||
} |
|||
} |
|||
|
|||
function getStateId() { |
|||
return vm.stateObject[vm.stateObject.length-1].id; |
|||
} |
|||
|
|||
function getStateParams() { |
|||
return vm.stateObject[vm.stateObject.length-1].params; |
|||
} |
|||
|
|||
function getStateName(index) { |
|||
var result = ''; |
|||
if (vm.stateObject[index]) { |
|||
var params = vm.stateObject[index].params; |
|||
if (params && params.entityName) { |
|||
result = params.entityName; |
|||
} else { |
|||
var id = vm.stateObject[index].id; |
|||
var translationId = types.translate.dashboardStatePrefix + id; |
|||
var translation = $translate.instant(translationId); |
|||
if (translation != translationId) { |
|||
result = translation; |
|||
} else { |
|||
result = vm.states[vm.stateObject[index].id].name; |
|||
} |
|||
} |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
function resolveEntity(params) { |
|||
var deferred = $q.defer(); |
|||
if (params && params.entityId && params.entityId.id && params.entityId.entityType) { |
|||
entityService.getEntity(params.entityId.entityType, params.entityId.id, {ignoreLoading: true, ignoreErrors: true}).then( |
|||
function success(entity) { |
|||
var entityName = entityService.entityName(params.entityId.entityType, entity); |
|||
deferred.resolve(entityName); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
} else { |
|||
deferred.reject(); |
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function parseState(stateJson) { |
|||
var result; |
|||
if (stateJson) { |
|||
try { |
|||
result = angular.fromJson(stateJson); |
|||
} catch (e) { |
|||
result = [ { id: null, params: {} } ]; |
|||
} |
|||
} |
|||
if (!result) { |
|||
result = []; |
|||
} |
|||
if (!result.length) { |
|||
result[0] = { id: null, params: {} } |
|||
} |
|||
if (!result[0].id) { |
|||
result[0].id = dashboardUtils.getRootStateId(vm.states); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
$scope.$watch('vm.states', function() { |
|||
if (vm.states) { |
|||
if (!vm.inited) { |
|||
vm.inited = true; |
|||
init(); |
|||
} |
|||
} |
|||
}); |
|||
|
|||
function init() { |
|||
var initialState = $stateParams.state; |
|||
vm.stateObject = parseState(initialState); |
|||
vm.selectedStateIndex = vm.stateObject.length-1; |
|||
gotoState(vm.stateObject[vm.stateObject.length-1].id, false); |
|||
|
|||
$scope.$watchCollection(function() { |
|||
return $state.params; |
|||
}, function(){ |
|||
var currentState = $state.params.state; |
|||
vm.stateObject = parseState(currentState); |
|||
}); |
|||
|
|||
$scope.$watch('vm.dashboardCtrl.dashboardCtx.state', function() { |
|||
if (vm.stateObject[vm.stateObject.length-1].id !== vm.dashboardCtrl.dashboardCtx.state) { |
|||
stopWatchStateObject(); |
|||
vm.stateObject[vm.stateObject.length-1].id = vm.dashboardCtrl.dashboardCtx.state; |
|||
updateLocation(); |
|||
watchStateObject(); |
|||
} |
|||
}); |
|||
|
|||
watchStateObject(); |
|||
|
|||
if (vm.dashboardCtrl.isMobile) { |
|||
watchSelectedStateIndex(); |
|||
} |
|||
|
|||
$scope.$watch('vm.dashboardCtrl.isMobile', function(newVal, prevVal) { |
|||
if (!angular.equals(newVal, prevVal)) { |
|||
if (vm.dashboardCtrl.isMobile) { |
|||
watchSelectedStateIndex(); |
|||
} else { |
|||
stopWatchSelectedStateIndex(); |
|||
} |
|||
} |
|||
}); |
|||
|
|||
} |
|||
|
|||
function stopWatchStateObject() { |
|||
if (vm.stateObjectWatcher) { |
|||
vm.stateObjectWatcher(); |
|||
vm.stateObjectWatcher = null; |
|||
} |
|||
} |
|||
|
|||
function watchStateObject() { |
|||
vm.stateObjectWatcher = $scope.$watch('vm.stateObject', function(newVal, prevVal) { |
|||
if (!angular.equals(newVal, prevVal) && newVal) { |
|||
vm.selectedStateIndex = vm.stateObject.length-1; |
|||
gotoState(vm.stateObject[vm.stateObject.length-1].id, true); |
|||
} |
|||
}, true); |
|||
} |
|||
|
|||
function stopWatchSelectedStateIndex() { |
|||
if (vm.selectedStateIndexWatcher) { |
|||
vm.selectedStateIndexWatcher(); |
|||
vm.selectedStateIndexWatcher = null; |
|||
} |
|||
} |
|||
|
|||
function watchSelectedStateIndex() { |
|||
vm.selectedStateIndexWatcher = $scope.$watch('vm.selectedStateIndex', function(newVal, prevVal) { |
|||
if (!angular.equals(newVal, prevVal)) { |
|||
navigatePrevState(vm.selectedStateIndex); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
function gotoState(stateId, update) { |
|||
if (vm.dashboardCtrl.dashboardCtx.state != stateId) { |
|||
vm.dashboardCtrl.openDashboardState(stateId); |
|||
if (update) { |
|||
updateLocation(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
function updateLocation() { |
|||
if (vm.stateObject[vm.stateObject.length-1].id) { |
|||
$location.search({state : angular.toJson(vm.stateObject)}); |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
/** |
|||
* Copyright © 2016-2017 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. |
|||
*/ |
|||
|
|||
.entity-state-controller { |
|||
.state-divider { |
|||
font-size: 28px; |
|||
padding-left: 15px; |
|||
padding-right: 15px; |
|||
} |
|||
.state-entry { |
|||
font-size: 22px; |
|||
outline: none; |
|||
} |
|||
md-select { |
|||
.md-text { |
|||
font-size: 22px; |
|||
font-weight: bold; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2017 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. |
|||
|
|||
--> |
|||
<div class="entity-state-controller"> |
|||
<div ng-if="!vm.dashboardCtrl.isMobile || vm.stateObject.length===1" layout="row" layout-align="start center"> |
|||
<div layout="row" layout-align="start center" ng-repeat="state in vm.stateObject track by $index"> |
|||
<span class='state-divider' ng-if="$index"> > </span> |
|||
<span class='state-entry' ng-style="{fontWeight: $last ? 'bold' : 'normal', |
|||
cursor: $last ? 'default' : 'pointer'}" ng-click="vm.navigatePrevState($index)"> |
|||
{{vm.getStateName($index)}} |
|||
</span> |
|||
</div> |
|||
</div> |
|||
<md-select ng-if="vm.dashboardCtrl.isMobile && vm.stateObject.length > 1" aria-label="{{ 'dashboard.state' | translate }}" ng-model="vm.selectedStateIndex"> |
|||
<md-option ng-repeat="state in vm.stateObject track by $index" ng-value="$index"> |
|||
{{vm.getStateName($index)}} |
|||
</md-option> |
|||
</md-select> |
|||
</div> |
|||
@ -0,0 +1,29 @@ |
|||
/* |
|||
* Copyright © 2016-2017 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 ManageDashboardStatesController from './manage-dashboard-states.controller'; |
|||
import DashboardStateDialogController from './dashboard-state-dialog.controller'; |
|||
import SelectTargetStateController from './select-target-state.controller'; |
|||
import StatesComponentDirective from './states-component.directive'; |
|||
import StatesControllerService from './states-controller.service'; |
|||
|
|||
export default angular.module('thingsboard.dashboard.states', []) |
|||
.controller('ManageDashboardStatesController', ManageDashboardStatesController) |
|||
.controller('DashboardStateDialogController', DashboardStateDialogController) |
|||
.controller('SelectTargetStateController', SelectTargetStateController) |
|||
.directive('tbStatesComponent', StatesComponentDirective) |
|||
.factory('statesControllerService', StatesControllerService) |
|||
.name; |
|||
@ -0,0 +1,198 @@ |
|||
/* |
|||
* Copyright © 2016-2017 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 './manage-dashboard-states.scss'; |
|||
|
|||
/* eslint-disable import/no-unresolved, import/default */ |
|||
|
|||
import dashboardStateDialogTemplate from './dashboard-state-dialog.tpl.html'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
/*@ngInject*/ |
|||
export default function ManageDashboardStatesController($scope, $mdDialog, $filter, $document, $translate, states) { |
|||
|
|||
var vm = this; |
|||
|
|||
vm.allStates = []; |
|||
for (var id in states) { |
|||
var state = states[id]; |
|||
state.id = id; |
|||
vm.allStates.push(state); |
|||
} |
|||
|
|||
vm.states = []; |
|||
vm.statesCount = 0; |
|||
|
|||
vm.query = { |
|||
order: 'name', |
|||
limit: 5, |
|||
page: 1, |
|||
search: null |
|||
}; |
|||
|
|||
vm.enterFilterMode = enterFilterMode; |
|||
vm.exitFilterMode = exitFilterMode; |
|||
vm.onReorder = onReorder; |
|||
vm.onPaginate = onPaginate; |
|||
vm.addState = addState; |
|||
vm.editState = editState; |
|||
vm.deleteState = deleteState; |
|||
|
|||
vm.cancel = cancel; |
|||
vm.save = save; |
|||
|
|||
$scope.$watch("vm.query.search", function(newVal, prevVal) { |
|||
if (!angular.equals(newVal, prevVal) && vm.query.search != null) { |
|||
updateStates(); |
|||
} |
|||
}); |
|||
|
|||
updateStates (); |
|||
|
|||
function updateStates () { |
|||
var result = $filter('orderBy')(vm.allStates, vm.query.order); |
|||
if (vm.query.search != null) { |
|||
result = $filter('filter')(result, {$: vm.query.search}); |
|||
} |
|||
vm.statesCount = result.length; |
|||
var startIndex = vm.query.limit * (vm.query.page - 1); |
|||
vm.states = result.slice(startIndex, startIndex + vm.query.limit); |
|||
} |
|||
|
|||
function enterFilterMode () { |
|||
vm.query.search = ''; |
|||
} |
|||
|
|||
function exitFilterMode () { |
|||
vm.query.search = null; |
|||
updateStates(); |
|||
} |
|||
|
|||
function onReorder () { |
|||
updateStates(); |
|||
} |
|||
|
|||
function onPaginate () { |
|||
updateStates(); |
|||
} |
|||
|
|||
function addState ($event) { |
|||
openStateDialog($event, null, true); |
|||
} |
|||
|
|||
function editState ($event, alertRule) { |
|||
if ($event) { |
|||
$event.stopPropagation(); |
|||
} |
|||
openStateDialog($event, alertRule, false); |
|||
} |
|||
|
|||
function openStateDialog($event, state, isAdd) { |
|||
var prevStateId = null; |
|||
if (!isAdd) { |
|||
prevStateId = state.id; |
|||
} |
|||
$mdDialog.show({ |
|||
controller: 'DashboardStateDialogController', |
|||
controllerAs: 'vm', |
|||
templateUrl: dashboardStateDialogTemplate, |
|||
parent: angular.element($document[0].body), |
|||
locals: {isAdd: isAdd, allStates: vm.allStates, state: angular.copy(state)}, |
|||
skipHide: true, |
|||
fullscreen: true, |
|||
targetEvent: $event |
|||
}).then(function (state) { |
|||
saveState(state, prevStateId); |
|||
updateStates(); |
|||
}); |
|||
} |
|||
|
|||
function getStateIndex(id) { |
|||
var result = $filter('filter')(vm.allStates, {id: id}); |
|||
if (result && result.length) { |
|||
return vm.allStates.indexOf(result[0]); |
|||
} |
|||
return -1; |
|||
} |
|||
|
|||
function saveState(state, prevStateId) { |
|||
if (prevStateId) { |
|||
var index = getStateIndex(prevStateId); |
|||
if (index > -1) { |
|||
vm.allStates[index] = state; |
|||
} |
|||
} else { |
|||
vm.allStates.push(state); |
|||
} |
|||
if (state.root) { |
|||
for (var i=0; i < vm.allStates.length; i++) { |
|||
var otherState = vm.allStates[i]; |
|||
if (otherState.id !== state.id) { |
|||
otherState.root = false; |
|||
} |
|||
} |
|||
} |
|||
$scope.theForm.$setDirty(); |
|||
} |
|||
|
|||
function deleteState ($event, state) { |
|||
if ($event) { |
|||
$event.stopPropagation(); |
|||
} |
|||
if (state) { |
|||
var title = $translate.instant('dashboard.delete-state-title'); |
|||
var content = $translate.instant('dashboard.delete-state-text', {stateName: state.name}); |
|||
var confirm = $mdDialog.confirm() |
|||
.targetEvent($event) |
|||
.title(title) |
|||
.htmlContent(content) |
|||
.ariaLabel(title) |
|||
.cancel($translate.instant('action.no')) |
|||
.ok($translate.instant('action.yes')); |
|||
|
|||
confirm._options.skipHide = true; |
|||
confirm._options.fullscreen = true; |
|||
|
|||
$mdDialog.show(confirm).then(function () { |
|||
var index = getStateIndex(state.id); |
|||
if (index > -1) { |
|||
vm.allStates.splice(index, 1); |
|||
} |
|||
$scope.theForm.$setDirty(); |
|||
updateStates(); |
|||
}); |
|||
|
|||
|
|||
} |
|||
} |
|||
|
|||
function cancel() { |
|||
$mdDialog.cancel(); |
|||
} |
|||
|
|||
function save() { |
|||
$scope.theForm.$setPristine(); |
|||
var savedStates = {}; |
|||
for (var i=0;i<vm.allStates.length;i++) { |
|||
var state = vm.allStates[i]; |
|||
var id = state.id; |
|||
delete state.id; |
|||
savedStates[id] = state; |
|||
} |
|||
$mdDialog.hide(savedStates); |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/** |
|||
* Copyright © 2016-2017 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. |
|||
*/ |
|||
|
|||
.manage-dashboard-states { |
|||
table.md-table { |
|||
tbody { |
|||
tr { |
|||
td { |
|||
&.tb-action-cell { |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
white-space: nowrap; |
|||
min-width: 100px; |
|||
max-width: 100px; |
|||
width: 100px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,127 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2017 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. |
|||
|
|||
--> |
|||
<md-dialog aria-label="{{ 'dashboard.manage-states' | translate }}" style="min-width: 600px;"> |
|||
<form name="theForm" ng-submit="vm.save()"> |
|||
<md-toolbar> |
|||
<div class="md-toolbar-tools"> |
|||
<h2 translate>{{ 'dashboard.manage-states' }}</h2> |
|||
<span flex></span> |
|||
<md-button class="md-icon-button" ng-click="vm.cancel()"> |
|||
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> |
|||
</md-button> |
|||
</div> |
|||
</md-toolbar> |
|||
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> |
|||
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> |
|||
<md-dialog-content> |
|||
<div class="md-dialog-content"> |
|||
<fieldset ng-disabled="loading"> |
|||
<div class="manage-dashboard-states" layout="column"> |
|||
<md-toolbar class="md-table-toolbar md-default" ng-show="vm.query.search === null"> |
|||
<div class="md-toolbar-tools"> |
|||
<span translate>dashboard.states</span> |
|||
<span flex></span> |
|||
<md-button class="md-icon-button" ng-click="vm.addState($event)"> |
|||
<md-icon>add</md-icon> |
|||
<md-tooltip md-direction="top"> |
|||
{{ 'dashboard.add-state' | translate }} |
|||
</md-tooltip> |
|||
</md-button> |
|||
<md-button class="md-icon-button" ng-click="vm.enterFilterMode()"> |
|||
<md-icon>search</md-icon> |
|||
<md-tooltip md-direction="top"> |
|||
{{ 'action.search' | translate }} |
|||
</md-tooltip> |
|||
</md-button> |
|||
</div> |
|||
</md-toolbar> |
|||
<md-toolbar class="md-table-toolbar md-default" ng-show="vm.query.search != null"> |
|||
<div class="md-toolbar-tools"> |
|||
<md-button class="md-icon-button" aria-label="{{ 'action.search' | translate }}"> |
|||
<md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">search</md-icon> |
|||
<md-tooltip md-direction="top"> |
|||
{{ 'dashboard.search-states' | translate }} |
|||
</md-tooltip> |
|||
</md-button> |
|||
<md-input-container md-theme="tb-search-input" flex> |
|||
<label> </label> |
|||
<input ng-model="vm.query.search" placeholder="{{ 'dashboard.search-states' | translate }}"/> |
|||
</md-input-container> |
|||
<md-button class="md-icon-button" aria-label="Close" ng-click="vm.exitFilterMode()"> |
|||
<md-icon aria-label="Close" class="material-icons">close</md-icon> |
|||
<md-tooltip md-direction="top"> |
|||
{{ 'action.close' | translate }} |
|||
</md-tooltip> |
|||
</md-button> |
|||
</div> |
|||
</md-toolbar> |
|||
<md-table-container> |
|||
<table md-table> |
|||
<thead md-head md-order="vm.query.order" md-on-reorder="vm.onReorder"> |
|||
<tr md-row> |
|||
<th md-column md-order-by="name"><span translate>dashboard.state-name</span></th> |
|||
<th md-column md-order-by="id"><span translate>dashboard.state-id</span></th> |
|||
<th md-column md-order-by="root"><span translate>dashboard.is-root-state</span></th> |
|||
<th md-column><span> </span></th> |
|||
</tr> |
|||
</thead> |
|||
<tbody md-body> |
|||
<tr md-row md-select="state" ng-disabled="state.root" md-select-id="id" md-auto-select ng-repeat="state in vm.states"> |
|||
<td md-cell>{{state.name}}</td> |
|||
<td md-cell>{{state.id}}</td> |
|||
<td md-cell> |
|||
<md-checkbox aria-label="{{'dashboard.is-root-state' | translate }}" |
|||
disabled ng-model="state.root"> |
|||
</md-checkbox> |
|||
</td> |
|||
<td md-cell class="tb-action-cell"> |
|||
<md-button class="md-icon-button" aria-label="{{ 'action.edit' | translate }}" |
|||
ng-click="vm.editState($event, state)"> |
|||
<md-icon aria-label="{{ 'action.edit' | translate }}" class="material-icons">edit</md-icon> |
|||
<md-tooltip md-direction="top"> |
|||
{{ 'dashboard.edit-state' | translate }} |
|||
</md-tooltip> |
|||
</md-button> |
|||
<md-button ng-show="!state.root" class="md-icon-button" aria-label="Delete" ng-click="vm.deleteState($event, state)"> |
|||
<md-icon aria-label="Delete" class="material-icons">delete</md-icon> |
|||
<md-tooltip md-direction="top"> |
|||
{{ 'dashboard.delete-state' | translate }} |
|||
</md-tooltip> |
|||
</md-button> |
|||
</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</md-table-container> |
|||
<md-table-pagination md-limit="vm.query.limit" md-limit-options="[5, 10, 15]" |
|||
md-page="vm.query.page" md-total="{{vm.statesCount}}" |
|||
md-on-paginate="vm.onPaginate" md-page-select> |
|||
</md-table-pagination> |
|||
</div> |
|||
</fieldset> |
|||
</div> |
|||
</md-dialog-content> |
|||
<md-dialog-actions layout="row"> |
|||
<span flex></span> |
|||
<md-button ng-disabled="loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary"> |
|||
{{ 'action.save' | translate }} |
|||
</md-button> |
|||
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button> |
|||
</md-dialog-actions> |
|||
</form> |
|||
</md-dialog> |
|||
@ -0,0 +1,35 @@ |
|||
/* |
|||
* Copyright © 2016-2017 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. |
|||
*/ |
|||
|
|||
/*@ngInject*/ |
|||
export default function SelectTargetStateController($scope, $mdDialog, dashboardUtils, states) { |
|||
|
|||
var vm = this; |
|||
vm.states = states; |
|||
vm.stateId = dashboardUtils.getRootStateId(vm.states); |
|||
|
|||
vm.cancel = cancel; |
|||
vm.save = save; |
|||
|
|||
function cancel() { |
|||
$mdDialog.cancel(); |
|||
} |
|||
|
|||
function save() { |
|||
$scope.theForm.$setPristine(); |
|||
$mdDialog.hide(vm.stateId); |
|||
} |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2017 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. |
|||
|
|||
--> |
|||
<md-dialog aria-label="{{ 'dashboard.select-state' | translate }}"> |
|||
<form name="theForm" ng-submit="vm.save()"> |
|||
<md-toolbar> |
|||
<div class="md-toolbar-tools"> |
|||
<h2 translate>{{ 'dashboard.select-state' }}</h2> |
|||
<span flex></span> |
|||
<md-button class="md-icon-button" ng-click="vm.cancel()"> |
|||
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> |
|||
</md-button> |
|||
</div> |
|||
</md-toolbar> |
|||
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> |
|||
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> |
|||
<md-dialog-content> |
|||
<div class="md-dialog-content"> |
|||
<fieldset ng-disabled="loading"> |
|||
<md-select required aria-label="{{ 'dashboard.state' | translate }}" ng-model="vm.stateId"> |
|||
<md-option ng-repeat="(stateId, state) in vm.states" ng-value="stateId"> |
|||
{{state.name}} |
|||
</md-option> |
|||
</md-select> |
|||
</fieldset> |
|||
</div> |
|||
</md-dialog-content> |
|||
<md-dialog-actions layout="row"> |
|||
<span flex></span> |
|||
<md-button ng-disabled="loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary"> |
|||
{{ 'action.save' | translate }} |
|||
</md-button> |
|||
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button> |
|||
</md-dialog-actions> |
|||
</form> |
|||
</md-dialog> |
|||
@ -0,0 +1,117 @@ |
|||
/* |
|||
* Copyright © 2016-2017 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. |
|||
*/ |
|||
|
|||
/*@ngInject*/ |
|||
export default function StatesComponent($compile, $templateCache, $controller, statesControllerService) { |
|||
|
|||
var linker = function (scope, element) { |
|||
|
|||
function destroyStateController() { |
|||
if (scope.statesController && angular.isFunction(scope.statesController.$onDestroy)) { |
|||
scope.statesController.$onDestroy(); |
|||
} |
|||
} |
|||
|
|||
function init() { |
|||
|
|||
var stateController = scope.dashboardCtrl.dashboardCtx.stateController; |
|||
|
|||
stateController.openState = function(id, params) { |
|||
if (scope.statesController) { |
|||
scope.statesController.openState(id, params); |
|||
} |
|||
} |
|||
|
|||
stateController.updateState = function(id, params) { |
|||
if (scope.statesController) { |
|||
scope.statesController.updateState(id, params); |
|||
} |
|||
} |
|||
|
|||
stateController.navigatePrevState = function(index) { |
|||
if (scope.statesController) { |
|||
scope.statesController.navigatePrevState(index); |
|||
} |
|||
} |
|||
|
|||
stateController.getStateId = function() { |
|||
if (scope.statesController) { |
|||
return scope.statesController.getStateId(); |
|||
} else { |
|||
return ''; |
|||
} |
|||
} |
|||
|
|||
stateController.getStateParams = function() { |
|||
if (scope.statesController) { |
|||
return scope.statesController.getStateParams(); |
|||
} else { |
|||
return {}; |
|||
} |
|||
} |
|||
} |
|||
|
|||
scope.$on('$destroy', function callOnDestroyHook() { |
|||
destroyStateController(); |
|||
}); |
|||
|
|||
scope.$watch('scope.dashboardCtrl', function() { |
|||
if (scope.dashboardCtrl.dashboardCtx) { |
|||
init(); |
|||
} |
|||
}) |
|||
|
|||
scope.$watch('statesControllerId', function(newValue) { |
|||
if (newValue) { |
|||
if (scope.statesController) { |
|||
destroyStateController(); |
|||
} |
|||
var statesControllerInfo = statesControllerService.getStateController(scope.statesControllerId); |
|||
if (!statesControllerInfo) { |
|||
//fallback to default
|
|||
statesControllerInfo = statesControllerService.getStateController('default'); |
|||
} |
|||
var template = $templateCache.get(statesControllerInfo.templateUrl); |
|||
element.html(template); |
|||
var locals = {}; |
|||
angular.extend(locals, {$scope: scope, $element: element}); |
|||
var controller = $controller(statesControllerInfo.controller, locals, true, 'vm'); |
|||
controller.instance = controller(); |
|||
scope.statesController = controller.instance; |
|||
scope.statesController.dashboardCtrl = scope.dashboardCtrl; |
|||
scope.statesController.states = scope.states; |
|||
$compile(element.contents())(scope); |
|||
} |
|||
}); |
|||
|
|||
scope.$watch('states', function() { |
|||
if (scope.statesController) { |
|||
scope.statesController.states = scope.states; |
|||
} |
|||
}); |
|||
|
|||
} |
|||
|
|||
return { |
|||
restrict: "E", |
|||
link: linker, |
|||
scope: { |
|||
statesControllerId: '=', |
|||
dashboardCtrl: '=', |
|||
states: '=' |
|||
} |
|||
}; |
|||
} |
|||
@ -0,0 +1,60 @@ |
|||
/* |
|||
* Copyright © 2016-2017 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. |
|||
*/ |
|||
|
|||
/* eslint-disable import/no-unresolved, import/default */ |
|||
|
|||
import defaultStateControllerTemplate from './default-state-controller.tpl.html'; |
|||
import entityStateControllerTemplate from './entity-state-controller.tpl.html'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
import DefaultStateController from './default-state-controller'; |
|||
import EntityStateController from './entity-state-controller'; |
|||
|
|||
/*@ngInject*/ |
|||
export default function StatesControllerService() { |
|||
|
|||
var statesControllers = {}; |
|||
statesControllers['default'] = { |
|||
controller: DefaultStateController, |
|||
templateUrl: defaultStateControllerTemplate |
|||
}; |
|||
statesControllers['entity'] = { |
|||
controller: EntityStateController, |
|||
templateUrl: entityStateControllerTemplate |
|||
}; |
|||
|
|||
var service = { |
|||
registerStatesController: registerStatesController, |
|||
getStateControllers: getStateControllers, |
|||
getStateController: getStateController |
|||
}; |
|||
|
|||
return service; |
|||
|
|||
function registerStatesController(id, stateControllerInfo) { |
|||
statesControllers[id] = stateControllerInfo; |
|||
} |
|||
|
|||
function getStateControllers() { |
|||
return statesControllers; |
|||
} |
|||
|
|||
function getStateController(id) { |
|||
return statesControllers[id]; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
/* |
|||
* Copyright © 2016-2017 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. |
|||
*/ |
|||
|
|||
/*@ngInject*/ |
|||
export default function ThingsboardMissingTranslateHandler($log, types) { |
|||
|
|||
return function (translationId) { |
|||
if (translationId && !translationId.startsWith(types.translate.dashboardStatePrefix)) { |
|||
$log.warn('Translation for ' + translationId + ' doesn\'t exist'); |
|||
} |
|||
}; |
|||
|
|||
} |
|||
Loading…
Reference in new issue