Commit d909192071880b7af2137333142bc62ece369ec1

Authored by Volodymyr Babak
2 parents 33fc8f0b a12a1ebe

Merge remote-tracking branch 'origin/master' into feature/entity-view

Showing 128 changed files with 3043 additions and 1377 deletions
... ... @@ -116,6 +116,7 @@ public class AppActor extends RuleChainManagerActor {
116 116 break;
117 117 case ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG:
118 118 onToDeviceSessionMsg((BasicActorSystemToDeviceSessionActorMsg) msg);
  119 + break;
119 120 default:
120 121 return false;
121 122 }
... ...
... ... @@ -76,7 +76,7 @@ public class DeviceController extends BaseController {
76 76 device.setTenantId(getCurrentUser().getTenantId());
77 77 if (getCurrentUser().getAuthority() == Authority.CUSTOMER_USER) {
78 78 if (device.getId() == null || device.getId().isNullUid() ||
79   - device.getCustomerId() == null || device.getCustomerId().isNullUid()) {
  79 + device.getCustomerId() == null || device.getCustomerId().isNullUid()) {
80 80 throw new ThingsboardException("You don't have permission to perform this operation!",
81 81 ThingsboardErrorCode.PERMISSION_DENIED);
82 82 } else {
... ...
... ... @@ -49,15 +49,15 @@ import org.thingsboard.server.common.data.kv.Aggregation;
49 49 import org.thingsboard.server.common.data.kv.AttributeKey;
50 50 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
51 51 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
52   -import org.thingsboard.server.common.data.kv.BaseTsKvQuery;
  52 +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
53 53 import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
54 54 import org.thingsboard.server.common.data.kv.BooleanDataEntry;
55 55 import org.thingsboard.server.common.data.kv.DoubleDataEntry;
56 56 import org.thingsboard.server.common.data.kv.KvEntry;
57 57 import org.thingsboard.server.common.data.kv.LongDataEntry;
  58 +import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
58 59 import org.thingsboard.server.common.data.kv.StringDataEntry;
59 60 import org.thingsboard.server.common.data.kv.TsKvEntry;
60   -import org.thingsboard.server.common.data.kv.TsKvQuery;
61 61 import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
62 62 import org.thingsboard.server.common.msg.core.TelemetryUploadRequest;
63 63 import org.thingsboard.server.common.transport.adaptor.JsonConverter;
... ... @@ -81,7 +81,6 @@ import java.util.LinkedHashMap;
81 81 import java.util.List;
82 82 import java.util.Map;
83 83 import java.util.Set;
84   -import java.util.UUID;
85 84 import java.util.concurrent.ExecutorService;
86 85 import java.util.concurrent.Executors;
87 86 import java.util.stream.Collectors;
... ... @@ -201,7 +200,7 @@ public class TelemetryController extends BaseController {
201 200 (result, entityId) -> {
202 201 // If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted
203 202 Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr);
204   - List<TsKvQuery> queries = toKeysList(keys).stream().map(key -> new BaseTsKvQuery(key, startTs, endTs, interval, limit, agg))
  203 + List<ReadTsKvQuery> queries = toKeysList(keys).stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, interval, limit, agg))
205 204 .collect(Collectors.toList());
206 205
207 206 Futures.addCallback(tsService.findAll(entityId, queries), getTsKvListCallback(result));
... ...
... ... @@ -22,6 +22,7 @@ import delight.nashornsandbox.NashornSandbox;
22 22 import delight.nashornsandbox.NashornSandboxes;
23 23 import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
24 24 import lombok.extern.slf4j.Slf4j;
  25 +import org.apache.commons.lang3.tuple.Pair;
25 26
26 27 import javax.annotation.PostConstruct;
27 28 import javax.annotation.PreDestroy;
... ... @@ -42,9 +43,10 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic
42 43 private ScriptEngine engine;
43 44 private ExecutorService monitorExecutorService;
44 45
45   - private Map<UUID, String> functionsMap = new ConcurrentHashMap<>();
46   -
47   - private Map<UUID,AtomicInteger> blackListedFunctions = new ConcurrentHashMap<>();
  46 + private final Map<UUID, String> functionsMap = new ConcurrentHashMap<>();
  47 + private final Map<UUID,AtomicInteger> blackListedFunctions = new ConcurrentHashMap<>();
  48 + private final Map<String, Pair<UUID, AtomicInteger>> scriptToId = new ConcurrentHashMap<>();
  49 + private final Map<UUID, AtomicInteger> scriptIdToCount = new ConcurrentHashMap<>();
48 50
49 51 @PostConstruct
50 52 public void init() {
... ... @@ -78,19 +80,27 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic
78 80
79 81 @Override
80 82 public ListenableFuture<UUID> eval(JsScriptType scriptType, String scriptBody, String... argNames) {
81   - UUID scriptId = UUID.randomUUID();
82   - String functionName = "invokeInternal_" + scriptId.toString().replace('-','_');
83   - String jsScript = generateJsScript(scriptType, functionName, scriptBody, argNames);
84   - try {
85   - if (useJsSandbox()) {
86   - sandbox.eval(jsScript);
87   - } else {
88   - engine.eval(jsScript);
  83 + Pair<UUID, AtomicInteger> deduplicated = deduplicate(scriptType, scriptBody);
  84 + UUID scriptId = deduplicated.getLeft();
  85 + AtomicInteger duplicateCount = deduplicated.getRight();
  86 +
  87 + if(duplicateCount.compareAndSet(0, 1)) {
  88 + String functionName = "invokeInternal_" + scriptId.toString().replace('-', '_');
  89 + String jsScript = generateJsScript(scriptType, functionName, scriptBody, argNames);
  90 + try {
  91 + if (useJsSandbox()) {
  92 + sandbox.eval(jsScript);
  93 + } else {
  94 + engine.eval(jsScript);
  95 + }
  96 + functionsMap.put(scriptId, functionName);
  97 + } catch (Exception e) {
  98 + duplicateCount.decrementAndGet();
  99 + log.warn("Failed to compile JS script: {}", e.getMessage(), e);
  100 + return Futures.immediateFailedFuture(e);
89 101 }
90   - functionsMap.put(scriptId, functionName);
91   - } catch (Exception e) {
92   - log.warn("Failed to compile JS script: {}", e.getMessage(), e);
93   - return Futures.immediateFailedFuture(e);
  102 + } else {
  103 + duplicateCount.incrementAndGet();
94 104 }
95 105 return Futures.immediateFuture(scriptId);
96 106 }
... ... @@ -122,6 +132,13 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic
122 132
123 133 @Override
124 134 public ListenableFuture<Void> release(UUID scriptId) {
  135 + AtomicInteger count = scriptIdToCount.get(scriptId);
  136 + if(count != null) {
  137 + if(count.decrementAndGet() > 0) {
  138 + return Futures.immediateFuture(null);
  139 + }
  140 + }
  141 +
125 142 String functionName = functionsMap.get(scriptId);
126 143 if (functionName != null) {
127 144 try {
... ... @@ -156,4 +173,16 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic
156 173 throw new RuntimeException("No script factory implemented for scriptType: " + scriptType);
157 174 }
158 175 }
  176 +
  177 + private Pair<UUID, AtomicInteger> deduplicate(JsScriptType scriptType, String scriptBody) {
  178 + Pair<UUID, AtomicInteger> precomputed = Pair.of(UUID.randomUUID(), new AtomicInteger());
  179 +
  180 + Pair<UUID, AtomicInteger> pair = scriptToId.computeIfAbsent(deduplicateKey(scriptType, scriptBody), i -> precomputed);
  181 + AtomicInteger duplicateCount = scriptIdToCount.computeIfAbsent(pair.getLeft(), i -> pair.getRight());
  182 + return Pair.of(pair.getLeft(), duplicateCount);
  183 + }
  184 +
  185 + private String deduplicateKey(JsScriptType scriptType, String scriptBody) {
  186 + return scriptType + "_" + scriptBody;
  187 + }
159 188 }
... ...
... ... @@ -45,7 +45,7 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S
45 45 try {
46 46 this.scriptId = this.sandboxService.eval(JsScriptType.RULE_NODE_SCRIPT, script, argNames).get();
47 47 } catch (Exception e) {
48   - throw new IllegalArgumentException("Can't compile script: " + e.getMessage());
  48 + throw new IllegalArgumentException("Can't compile script: " + e.getMessage(), e);
49 49 }
50 50 }
51 51
... ...
... ... @@ -24,24 +24,28 @@ import org.springframework.beans.factory.annotation.Autowired;
24 24 import org.springframework.context.annotation.Lazy;
25 25 import org.springframework.stereotype.Service;
26 26 import org.springframework.util.StringUtils;
  27 +import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
27 28 import org.thingsboard.rule.engine.api.util.DonAsynchron;
  29 +import org.thingsboard.server.actors.service.ActorService;
28 30 import org.thingsboard.server.common.data.DataConstants;
29 31 import org.thingsboard.server.common.data.EntityType;
30 32 import org.thingsboard.server.common.data.id.DeviceId;
31 33 import org.thingsboard.server.common.data.id.EntityId;
32 34 import org.thingsboard.server.common.data.id.EntityIdFactory;
  35 +import org.thingsboard.server.common.data.id.TenantId;
33 36 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
34 37 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
35   -import org.thingsboard.server.common.data.kv.BaseTsKvQuery;
  38 +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
36 39 import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
37 40 import org.thingsboard.server.common.data.kv.BooleanDataEntry;
38 41 import org.thingsboard.server.common.data.kv.DataType;
39 42 import org.thingsboard.server.common.data.kv.DoubleDataEntry;
40 43 import org.thingsboard.server.common.data.kv.KvEntry;
41 44 import org.thingsboard.server.common.data.kv.LongDataEntry;
  45 +import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
42 46 import org.thingsboard.server.common.data.kv.StringDataEntry;
43 47 import org.thingsboard.server.common.data.kv.TsKvEntry;
44   -import org.thingsboard.server.common.data.kv.TsKvQuery;
  48 +import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
45 49 import org.thingsboard.server.common.msg.cluster.ServerAddress;
46 50 import org.thingsboard.server.dao.attributes.AttributesService;
47 51 import org.thingsboard.server.dao.timeseries.TimeseriesService;
... ... @@ -101,6 +105,10 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
101 105 @Lazy
102 106 private DeviceStateService stateService;
103 107
  108 + @Autowired
  109 + @Lazy
  110 + private ActorService actorService;
  111 +
104 112 private ExecutorService tsCallBackExecutor;
105 113 private ExecutorService wsCallBackExecutor;
106 114
... ... @@ -204,6 +212,13 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
204 212 }
205 213
206 214 @Override
  215 + public void onSharedAttributesUpdate(TenantId tenantId, DeviceId deviceId, Set<AttributeKvEntry> attributes) {
  216 + DeviceAttributesEventNotificationMsg notificationMsg = DeviceAttributesEventNotificationMsg.onUpdate(tenantId,
  217 + deviceId, DataConstants.SHARED_SCOPE, new ArrayList<>(attributes));
  218 + actorService.onMsg(new SendToClusterMsg(deviceId, notificationMsg));
  219 + }
  220 +
  221 + @Override
207 222 public void onNewRemoteSubscription(ServerAddress serverAddress, byte[] data) {
208 223 ClusterAPIProtos.SubscriptionProto proto;
209 224 try {
... ... @@ -355,9 +370,9 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
355 370 e -> log.error("Failed to fetch missed updates.", e), tsCallBackExecutor);
356 371 } else if (subscription.getType() == TelemetryFeature.TIMESERIES) {
357 372 long curTs = System.currentTimeMillis();
358   - List<TsKvQuery> queries = new ArrayList<>();
  373 + List<ReadTsKvQuery> queries = new ArrayList<>();
359 374 subscription.getKeyStates().entrySet().forEach(e -> {
360   - queries.add(new BaseTsKvQuery(e.getKey(), e.getValue() + 1L, curTs));
  375 + queries.add(new BaseReadTsKvQuery(e.getKey(), e.getValue() + 1L, curTs));
361 376 });
362 377
363 378 DonAsynchron.withCallback(tsService.findAll(entityId, queries),
... ...
... ... @@ -30,10 +30,10 @@ import org.thingsboard.server.common.data.id.EntityId;
30 30 import org.thingsboard.server.common.data.id.EntityIdFactory;
31 31 import org.thingsboard.server.common.data.kv.Aggregation;
32 32 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
33   -import org.thingsboard.server.common.data.kv.BaseTsKvQuery;
  33 +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
34 34 import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
  35 +import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
35 36 import org.thingsboard.server.common.data.kv.TsKvEntry;
36   -import org.thingsboard.server.common.data.kv.TsKvQuery;
37 37 import org.thingsboard.server.dao.attributes.AttributesService;
38 38 import org.thingsboard.server.dao.timeseries.TimeseriesService;
39 39 import org.thingsboard.server.service.security.AccessValidator;
... ... @@ -251,7 +251,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
251 251 }
252 252 EntityId entityId = EntityIdFactory.getByTypeAndId(cmd.getEntityType(), cmd.getEntityId());
253 253 List<String> keys = new ArrayList<>(getKeys(cmd).orElse(Collections.emptySet()));
254   - List<TsKvQuery> queries = keys.stream().map(key -> new BaseTsKvQuery(key, cmd.getStartTs(), cmd.getEndTs(), cmd.getInterval(), getLimit(cmd.getLimit()), getAggregation(cmd.getAgg())))
  254 + List<ReadTsKvQuery> queries = keys.stream().map(key -> new BaseReadTsKvQuery(key, cmd.getStartTs(), cmd.getEndTs(), cmd.getInterval(), getLimit(cmd.getLimit()), getAggregation(cmd.getAgg())))
255 255 .collect(Collectors.toList());
256 256
257 257 FutureCallback<List<TsKvEntry>> callback = new FutureCallback<List<TsKvEntry>>() {
... ... @@ -337,7 +337,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
337 337 log.debug("[{}] fetching timeseries data for last {} ms for keys: ({}) for device : {}", sessionId, cmd.getTimeWindow(), cmd.getKeys(), entityId);
338 338 startTs = cmd.getStartTs();
339 339 long endTs = cmd.getStartTs() + cmd.getTimeWindow();
340   - List<TsKvQuery> queries = keys.stream().map(key -> new BaseTsKvQuery(key, startTs, endTs, cmd.getInterval(),
  340 + List<ReadTsKvQuery> queries = keys.stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, cmd.getInterval(),
341 341 getLimit(cmd.getLimit()), getAggregation(cmd.getAgg()))).collect(Collectors.toList());
342 342
343 343 final FutureCallback<List<TsKvEntry>> callback = getSubscriptionCallback(sessionRef, cmd, sessionId, entityId, startTs, keys);
... ...
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.kv;
  17 +
  18 +import lombok.Data;
  19 +
  20 +@Data
  21 +public class BaseDeleteTsKvQuery extends BaseTsKvQuery implements DeleteTsKvQuery {
  22 +
  23 + private final Boolean rewriteLatestIfDeleted;
  24 +
  25 + public BaseDeleteTsKvQuery(String key, long startTs, long endTs, boolean rewriteLatestIfDeleted) {
  26 + super(key, startTs, endTs);
  27 + this.rewriteLatestIfDeleted = rewriteLatestIfDeleted;
  28 + }
  29 +
  30 + public BaseDeleteTsKvQuery(String key, long startTs, long endTs) {
  31 + this(key, startTs, endTs, false);
  32 + }
  33 +
  34 +
  35 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.kv;
  17 +
  18 +import lombok.Data;
  19 +
  20 +@Data
  21 +public class BaseReadTsKvQuery extends BaseTsKvQuery implements ReadTsKvQuery {
  22 +
  23 + private final long interval;
  24 + private final int limit;
  25 + private final Aggregation aggregation;
  26 + private final String orderBy;
  27 +
  28 + public BaseReadTsKvQuery(String key, long startTs, long endTs, long interval, int limit, Aggregation aggregation) {
  29 + this(key, startTs, endTs, interval, limit, aggregation, "DESC");
  30 + }
  31 +
  32 + public BaseReadTsKvQuery(String key, long startTs, long endTs, long interval, int limit, Aggregation aggregation,
  33 + String orderBy) {
  34 + super(key, startTs, endTs);
  35 + this.interval = interval;
  36 + this.limit = limit;
  37 + this.aggregation = aggregation;
  38 + this.orderBy = orderBy;
  39 + }
  40 +
  41 + public BaseReadTsKvQuery(String key, long startTs, long endTs) {
  42 + this(key, startTs, endTs, endTs - startTs, 1, Aggregation.AVG, "DESC");
  43 + }
  44 +
  45 +}
... ...
... ... @@ -23,21 +23,11 @@ public class BaseTsKvQuery implements TsKvQuery {
23 23 private final String key;
24 24 private final long startTs;
25 25 private final long endTs;
26   - private final long interval;
27   - private final int limit;
28   - private final Aggregation aggregation;
29 26
30   - public BaseTsKvQuery(String key, long startTs, long endTs, long interval, int limit, Aggregation aggregation) {
  27 + public BaseTsKvQuery(String key, long startTs, long endTs) {
31 28 this.key = key;
32 29 this.startTs = startTs;
33 30 this.endTs = endTs;
34   - this.interval = interval;
35   - this.limit = limit;
36   - this.aggregation = aggregation;
37   - }
38   -
39   - public BaseTsKvQuery(String key, long startTs, long endTs) {
40   - this(key, startTs, endTs, endTs-startTs, 1, Aggregation.AVG);
41 31 }
42 32
43 33 }
... ...
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.kv;
  17 +
  18 +public interface DeleteTsKvQuery extends TsKvQuery {
  19 +
  20 + Boolean getRewriteLatestIfDeleted();
  21 +
  22 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.kv;
  17 +
  18 +public interface ReadTsKvQuery extends TsKvQuery {
  19 +
  20 + long getInterval();
  21 +
  22 + int getLimit();
  23 +
  24 + Aggregation getAggregation();
  25 +
  26 + String getOrderBy();
  27 +
  28 +}
... ...
... ... @@ -23,10 +23,4 @@ public interface TsKvQuery {
23 23
24 24 long getEndTs();
25 25
26   - long getInterval();
27   -
28   - int getLimit();
29   -
30   - Aggregation getAggregation();
31   -
32 26 }
... ...
... ... @@ -16,9 +16,7 @@
16 16 package org.thingsboard.server.dao.relation;
17 17
18 18 import com.google.common.base.Function;
19   -import com.google.common.util.concurrent.AsyncFunction;
20   -import com.google.common.util.concurrent.Futures;
21   -import com.google.common.util.concurrent.ListenableFuture;
  19 +import com.google.common.util.concurrent.*;
22 20 import lombok.extern.slf4j.Slf4j;
23 21 import org.springframework.beans.factory.annotation.Autowired;
24 22 import org.springframework.cache.Cache;
... ... @@ -41,7 +39,6 @@ import org.thingsboard.server.dao.exception.DataValidationException;
41 39
42 40 import javax.annotation.Nullable;
43 41 import java.util.ArrayList;
44   -import java.util.Arrays;
45 42 import java.util.Collections;
46 43 import java.util.HashSet;
47 44 import java.util.List;
... ... @@ -94,10 +91,10 @@ public class BaseRelationService implements RelationService {
94 91
95 92 @Caching(evict = {
96 93 @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.to, #relation.type, #relation.typeGroup}"),
97   - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup}"),
98   - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup}"),
99   - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup}"),
100   - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup}")
  94 + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup, 'FROM'}"),
  95 + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup, 'FROM'}"),
  96 + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup, 'TO'}"),
  97 + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup, 'TO'}")
101 98 })
102 99 @Override
103 100 public boolean saveRelation(EntityRelation relation) {
... ... @@ -108,10 +105,10 @@ public class BaseRelationService implements RelationService {
108 105
109 106 @Caching(evict = {
110 107 @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.to, #relation.type, #relation.typeGroup}"),
111   - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup}"),
112   - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup}"),
113   - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup}"),
114   - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup}")
  108 + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup, 'FROM'}"),
  109 + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup, 'FROM'}"),
  110 + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup, 'TO'}"),
  111 + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup, 'TO'}")
115 112 })
116 113 @Override
117 114 public ListenableFuture<Boolean> saveRelationAsync(EntityRelation relation) {
... ... @@ -122,10 +119,10 @@ public class BaseRelationService implements RelationService {
122 119
123 120 @Caching(evict = {
124 121 @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.to, #relation.type, #relation.typeGroup}"),
125   - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup}"),
126   - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup}"),
127   - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup}"),
128   - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup}")
  122 + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup, 'FROM'}"),
  123 + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup, 'FROM'}"),
  124 + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup, 'TO'}"),
  125 + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup, 'TO'}")
129 126 })
130 127 @Override
131 128 public boolean deleteRelation(EntityRelation relation) {
... ... @@ -136,10 +133,10 @@ public class BaseRelationService implements RelationService {
136 133
137 134 @Caching(evict = {
138 135 @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.to, #relation.type, #relation.typeGroup}"),
139   - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup}"),
140   - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup}"),
141   - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup}"),
142   - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup}")
  136 + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup, 'FROM'}"),
  137 + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup, 'FROM'}"),
  138 + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup, 'TO'}"),
  139 + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup, 'TO'}")
143 140 })
144 141 @Override
145 142 public ListenableFuture<Boolean> deleteRelationAsync(EntityRelation relation) {
... ... @@ -150,10 +147,10 @@ public class BaseRelationService implements RelationService {
150 147
151 148 @Caching(evict = {
152 149 @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #to, #relationType, #typeGroup}"),
153   - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup}"),
154   - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #typeGroup}"),
155   - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #typeGroup}"),
156   - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup}")
  150 + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup, 'FROM'}"),
  151 + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #typeGroup, 'FROM'}"),
  152 + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #typeGroup, 'TO'}"),
  153 + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup, 'TO'}")
157 154 })
158 155 @Override
159 156 public boolean deleteRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) {
... ... @@ -164,10 +161,10 @@ public class BaseRelationService implements RelationService {
164 161
165 162 @Caching(evict = {
166 163 @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #to, #relationType, #typeGroup}"),
167   - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup}"),
168   - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #typeGroup}"),
169   - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #typeGroup}"),
170   - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup}")
  164 + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup, 'FROM'}"),
  165 + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #typeGroup, 'FROM'}"),
  166 + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #typeGroup, 'TO'}"),
  167 + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup, 'TO'}")
171 168 })
172 169 @Override
173 170 public ListenableFuture<Boolean> deleteRelationAsync(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) {
... ... @@ -250,30 +247,36 @@ public class BaseRelationService implements RelationService {
250 247 fromTypeAndTypeGroup.add(relation.getFrom());
251 248 fromTypeAndTypeGroup.add(relation.getType());
252 249 fromTypeAndTypeGroup.add(relation.getTypeGroup());
  250 + fromTypeAndTypeGroup.add(EntitySearchDirection.FROM.name());
253 251 cache.evict(fromTypeAndTypeGroup);
254 252
255 253 List<Object> fromAndTypeGroup = new ArrayList<>();
256 254 fromAndTypeGroup.add(relation.getFrom());
257 255 fromAndTypeGroup.add(relation.getTypeGroup());
  256 + fromAndTypeGroup.add(EntitySearchDirection.FROM.name());
258 257 cache.evict(fromAndTypeGroup);
259 258
260 259 List<Object> toAndTypeGroup = new ArrayList<>();
261 260 toAndTypeGroup.add(relation.getTo());
262 261 toAndTypeGroup.add(relation.getTypeGroup());
  262 + toAndTypeGroup.add(EntitySearchDirection.TO.name());
263 263 cache.evict(toAndTypeGroup);
264 264
265 265 List<Object> toTypeAndTypeGroup = new ArrayList<>();
266   - fromTypeAndTypeGroup.add(relation.getTo());
267   - fromTypeAndTypeGroup.add(relation.getType());
268   - fromTypeAndTypeGroup.add(relation.getTypeGroup());
  266 + toTypeAndTypeGroup.add(relation.getTo());
  267 + toTypeAndTypeGroup.add(relation.getType());
  268 + toTypeAndTypeGroup.add(relation.getTypeGroup());
  269 + toTypeAndTypeGroup.add(EntitySearchDirection.TO.name());
269 270 cache.evict(toTypeAndTypeGroup);
270 271 }
271 272
272   - @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #typeGroup}")
  273 + @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #typeGroup, 'FROM'}")
273 274 @Override
274 275 public List<EntityRelation> findByFrom(EntityId from, RelationTypeGroup typeGroup) {
  276 + validate(from);
  277 + validateTypeGroup(typeGroup);
275 278 try {
276   - return findByFromAsync(from, typeGroup).get();
  279 + return relationDao.findAllByFrom(from, typeGroup).get();
277 280 } catch (InterruptedException | ExecutionException e) {
278 281 throw new RuntimeException(e);
279 282 }
... ... @@ -284,7 +287,29 @@ public class BaseRelationService implements RelationService {
284 287 log.trace("Executing findByFrom [{}][{}]", from, typeGroup);
285 288 validate(from);
286 289 validateTypeGroup(typeGroup);
287   - return relationDao.findAllByFrom(from, typeGroup);
  290 +
  291 + List<Object> fromAndTypeGroup = new ArrayList<>();
  292 + fromAndTypeGroup.add(from);
  293 + fromAndTypeGroup.add(typeGroup);
  294 + fromAndTypeGroup.add(EntitySearchDirection.FROM.name());
  295 +
  296 + Cache cache = cacheManager.getCache(RELATIONS_CACHE);
  297 + List<EntityRelation> fromCache = cache.get(fromAndTypeGroup, List.class);
  298 + if (fromCache != null) {
  299 + return Futures.immediateFuture(fromCache);
  300 + } else {
  301 + ListenableFuture<List<EntityRelation>> relationsFuture = relationDao.findAllByFrom(from, typeGroup);
  302 + Futures.addCallback(relationsFuture,
  303 + new FutureCallback<List<EntityRelation>>() {
  304 + @Override
  305 + public void onSuccess(@Nullable List<EntityRelation> result) {
  306 + cache.putIfAbsent(fromAndTypeGroup, result);
  307 + }
  308 + @Override
  309 + public void onFailure(Throwable t) {}
  310 + });
  311 + return relationsFuture;
  312 + }
288 313 }
289 314
290 315 @Override
... ... @@ -305,7 +330,7 @@ public class BaseRelationService implements RelationService {
305 330 });
306 331 }
307 332
308   - @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup}")
  333 + @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup, 'FROM'}")
309 334 @Override
310 335 public List<EntityRelation> findByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup) {
311 336 try {
... ... @@ -324,11 +349,13 @@ public class BaseRelationService implements RelationService {
324 349 return relationDao.findAllByFromAndType(from, relationType, typeGroup);
325 350 }
326 351
327   - @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #typeGroup}")
  352 + @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #typeGroup, 'TO'}")
328 353 @Override
329 354 public List<EntityRelation> findByTo(EntityId to, RelationTypeGroup typeGroup) {
  355 + validate(to);
  356 + validateTypeGroup(typeGroup);
330 357 try {
331   - return findByToAsync(to, typeGroup).get();
  358 + return relationDao.findAllByTo(to, typeGroup).get();
332 359 } catch (InterruptedException | ExecutionException e) {
333 360 throw new RuntimeException(e);
334 361 }
... ... @@ -339,7 +366,29 @@ public class BaseRelationService implements RelationService {
339 366 log.trace("Executing findByTo [{}][{}]", to, typeGroup);
340 367 validate(to);
341 368 validateTypeGroup(typeGroup);
342   - return relationDao.findAllByTo(to, typeGroup);
  369 +
  370 + List<Object> toAndTypeGroup = new ArrayList<>();
  371 + toAndTypeGroup.add(to);
  372 + toAndTypeGroup.add(typeGroup);
  373 + toAndTypeGroup.add(EntitySearchDirection.TO.name());
  374 +
  375 + Cache cache = cacheManager.getCache(RELATIONS_CACHE);
  376 + List<EntityRelation> fromCache = cache.get(toAndTypeGroup, List.class);
  377 + if (fromCache != null) {
  378 + return Futures.immediateFuture(fromCache);
  379 + } else {
  380 + ListenableFuture<List<EntityRelation>> relationsFuture = relationDao.findAllByTo(to, typeGroup);
  381 + Futures.addCallback(relationsFuture,
  382 + new FutureCallback<List<EntityRelation>>() {
  383 + @Override
  384 + public void onSuccess(@Nullable List<EntityRelation> result) {
  385 + cache.putIfAbsent(toAndTypeGroup, result);
  386 + }
  387 + @Override
  388 + public void onFailure(Throwable t) {}
  389 + });
  390 + return relationsFuture;
  391 + }
343 392 }
344 393
345 394 @Override
... ... @@ -371,7 +420,7 @@ public class BaseRelationService implements RelationService {
371 420 });
372 421 }
373 422
374   - @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup}")
  423 + @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup, 'TO'}")
375 424 @Override
376 425 public List<EntityRelation> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup) {
377 426 try {
... ...
... ... @@ -31,9 +31,10 @@ import org.thingsboard.server.common.data.UUIDConverter;
31 31 import org.thingsboard.server.common.data.id.EntityId;
32 32 import org.thingsboard.server.common.data.kv.Aggregation;
33 33 import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
  34 +import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
  35 +import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
34 36 import org.thingsboard.server.common.data.kv.StringDataEntry;
35 37 import org.thingsboard.server.common.data.kv.TsKvEntry;
36   -import org.thingsboard.server.common.data.kv.TsKvQuery;
37 38 import org.thingsboard.server.dao.DaoUtil;
38 39 import org.thingsboard.server.dao.model.sql.TsKvEntity;
39 40 import org.thingsboard.server.dao.model.sql.TsKvLatestCompositeKey;
... ... @@ -102,7 +103,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
102 103 }
103 104
104 105 @Override
105   - public ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, List<TsKvQuery> queries) {
  106 + public ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, List<ReadTsKvQuery> queries) {
106 107 List<ListenableFuture<List<TsKvEntry>>> futures = queries
107 108 .stream()
108 109 .map(query -> findAllAsync(entityId, query))
... ... @@ -121,7 +122,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
121 122 }, service);
122 123 }
123 124
124   - private ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, TsKvQuery query) {
  125 + private ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, ReadTsKvQuery query) {
125 126 if (query.getAggregation() == Aggregation.NONE) {
126 127 return findAllAsyncWithLimit(entityId, query);
127 128 } else {
... ... @@ -228,7 +229,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
228 229 });
229 230 }
230 231
231   - private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, TsKvQuery query) {
  232 + private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, ReadTsKvQuery query) {
232 233 return Futures.immediateFuture(
233 234 DaoUtil.convertDataList(
234 235 tsKvRepository.findAllWithLimit(
... ... @@ -306,6 +307,36 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
306 307 });
307 308 }
308 309
  310 + @Override
  311 + public ListenableFuture<Void> remove(EntityId entityId, DeleteTsKvQuery query) {
  312 + return service.submit(() -> {
  313 + tsKvRepository.delete(
  314 + fromTimeUUID(entityId.getId()),
  315 + entityId.getEntityType(),
  316 + query.getKey(),
  317 + query.getStartTs(),
  318 + query.getEndTs());
  319 + return null;
  320 + });
  321 + }
  322 +
  323 + @Override
  324 + public ListenableFuture<Void> removeLatest(EntityId entityId, DeleteTsKvQuery query) {
  325 + TsKvLatestEntity latestEntity = new TsKvLatestEntity();
  326 + latestEntity.setEntityType(entityId.getEntityType());
  327 + latestEntity.setEntityId(fromTimeUUID(entityId.getId()));
  328 + latestEntity.setKey(query.getKey());
  329 + return service.submit(() -> {
  330 + tsKvLatestRepository.delete(latestEntity);
  331 + return null;
  332 + });
  333 + }
  334 +
  335 + @Override
  336 + public ListenableFuture<Void> removePartition(EntityId entityId, DeleteTsKvQuery query) {
  337 + return service.submit(() -> null);
  338 + }
  339 +
309 340 @PreDestroy
310 341 void onDestroy() {
311 342 if (insertService != null) {
... ...
... ... @@ -16,10 +16,12 @@
16 16 package org.thingsboard.server.dao.sql.timeseries;
17 17
18 18 import org.springframework.data.domain.Pageable;
  19 +import org.springframework.data.jpa.repository.Modifying;
19 20 import org.springframework.data.jpa.repository.Query;
20 21 import org.springframework.data.repository.CrudRepository;
21 22 import org.springframework.data.repository.query.Param;
22 23 import org.springframework.scheduling.annotation.Async;
  24 +import org.springframework.transaction.annotation.Transactional;
23 25 import org.thingsboard.server.common.data.EntityType;
24 26 import org.thingsboard.server.dao.model.sql.TsKvCompositeKey;
25 27 import org.thingsboard.server.dao.model.sql.TsKvEntity;
... ... @@ -41,6 +43,17 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite
41 43 @Param("endTs") long endTs,
42 44 Pageable pageable);
43 45
  46 + @Transactional
  47 + @Modifying
  48 + @Query("DELETE FROM TsKvEntity tskv WHERE tskv.entityId = :entityId " +
  49 + "AND tskv.entityType = :entityType AND tskv.key = :entityKey " +
  50 + "AND tskv.ts > :startTs AND tskv.ts < :endTs")
  51 + void delete(@Param("entityId") String entityId,
  52 + @Param("entityType") EntityType entityType,
  53 + @Param("entityKey") String key,
  54 + @Param("startTs") long startTs,
  55 + @Param("endTs") long endTs);
  56 +
44 57 @Async
45 58 @Query("SELECT new TsKvEntity(MAX(tskv.strValue), MAX(tskv.longValue), MAX(tskv.doubleValue)) FROM TsKvEntity tskv " +
46 59 "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " +
... ... @@ -56,30 +69,30 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite
56 69 "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " +
57 70 "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs")
58 71 CompletableFuture<TsKvEntity> findMin(@Param("entityId") String entityId,
59   - @Param("entityType") EntityType entityType,
60   - @Param("entityKey") String entityKey,
61   - @Param("startTs") long startTs,
62   - @Param("endTs") long endTs);
  72 + @Param("entityType") EntityType entityType,
  73 + @Param("entityKey") String entityKey,
  74 + @Param("startTs") long startTs,
  75 + @Param("endTs") long endTs);
63 76
64 77 @Async
65 78 @Query("SELECT new TsKvEntity(COUNT(tskv.booleanValue), COUNT(tskv.strValue), COUNT(tskv.longValue), COUNT(tskv.doubleValue)) FROM TsKvEntity tskv " +
66 79 "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " +
67 80 "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs")
68 81 CompletableFuture<TsKvEntity> findCount(@Param("entityId") String entityId,
69   - @Param("entityType") EntityType entityType,
70   - @Param("entityKey") String entityKey,
71   - @Param("startTs") long startTs,
72   - @Param("endTs") long endTs);
  82 + @Param("entityType") EntityType entityType,
  83 + @Param("entityKey") String entityKey,
  84 + @Param("startTs") long startTs,
  85 + @Param("endTs") long endTs);
73 86
74 87 @Async
75 88 @Query("SELECT new TsKvEntity(AVG(tskv.longValue), AVG(tskv.doubleValue)) FROM TsKvEntity tskv " +
76 89 "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " +
77 90 "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs")
78 91 CompletableFuture<TsKvEntity> findAvg(@Param("entityId") String entityId,
79   - @Param("entityType") EntityType entityType,
80   - @Param("entityKey") String entityKey,
81   - @Param("startTs") long startTs,
82   - @Param("endTs") long endTs);
  92 + @Param("entityType") EntityType entityType,
  93 + @Param("entityKey") String entityKey,
  94 + @Param("startTs") long startTs,
  95 + @Param("endTs") long endTs);
83 96
84 97
85 98 @Async
... ... @@ -87,8 +100,8 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite
87 100 "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " +
88 101 "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs")
89 102 CompletableFuture<TsKvEntity> findSum(@Param("entityId") String entityId,
90   - @Param("entityType") EntityType entityType,
91   - @Param("entityKey") String entityKey,
92   - @Param("startTs") long startTs,
93   - @Param("endTs") long endTs);
  103 + @Param("entityType") EntityType entityType,
  104 + @Param("entityKey") String entityKey,
  105 + @Param("startTs") long startTs,
  106 + @Param("endTs") long endTs);
94 107 }
... ...
... ... @@ -25,9 +25,10 @@ import org.thingsboard.server.common.data.EntityType;
25 25 import org.thingsboard.server.common.data.EntityView;
26 26 import org.thingsboard.server.common.data.id.EntityId;
27 27 import org.thingsboard.server.common.data.id.EntityViewId;
28   -import org.thingsboard.server.common.data.kv.BaseTsKvQuery;
  28 +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
  29 +import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
  30 +import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
29 31 import org.thingsboard.server.common.data.kv.TsKvEntry;
30   -import org.thingsboard.server.common.data.kv.TsKvQuery;
31 32 import org.thingsboard.server.dao.entityview.EntityViewService;
32 33 import org.thingsboard.server.dao.exception.IncorrectParameterException;
33 34 import org.thingsboard.server.dao.service.Validator;
... ... @@ -46,6 +47,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
46 47 public class BaseTimeseriesService implements TimeseriesService {
47 48
48 49 public static final int INSERTS_PER_ENTRY = 3;
  50 + public static final int DELETES_PER_ENTRY = INSERTS_PER_ENTRY;
49 51
50 52 @Autowired
51 53 private TimeseriesDao timeseriesDao;
... ... @@ -54,9 +56,9 @@ public class BaseTimeseriesService implements TimeseriesService {
54 56 private EntityViewService entityViewService;
55 57
56 58 @Override
57   - public ListenableFuture<List<TsKvEntry>> findAll(EntityId entityId, List<TsKvQuery> queries) {
  59 + public ListenableFuture<List<TsKvEntry>> findAll(EntityId entityId, List<ReadTsKvQuery> queries) {
58 60 validate(entityId);
59   - queries.forEach(query -> validate(query));
  61 + queries.forEach(BaseTimeseriesService::validate);
60 62 if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW)) {
61 63 EntityView entityView = entityViewService.findEntityViewById((EntityViewId) entityId);
62 64 return timeseriesDao.findAllAsync(entityView.getEntityId(), updateQueriesForEntityView(entityView, queries));
... ... @@ -129,8 +131,8 @@ public class BaseTimeseriesService implements TimeseriesService {
129 131 futures.add(timeseriesDao.save(entityId, tsKvEntry, ttl));
130 132 }
131 133
132   - private List<TsKvQuery> updateQueriesForEntityView(EntityView entityView, List<TsKvQuery> queries) {
133   - List<TsKvQuery> newQueries = new ArrayList<>();
  134 + private List<ReadTsKvQuery> updateQueriesForEntityView(EntityView entityView, List<ReadTsKvQuery> queries) {
  135 + List<ReadTsKvQuery> newQueries = new ArrayList<>();
134 136 entityView.getKeys().getTimeseries()
135 137 .forEach(viewKey -> queries
136 138 .forEach(query -> {
... ... @@ -156,27 +158,53 @@ public class BaseTimeseriesService implements TimeseriesService {
156 158 .forEach(key -> {
157 159 if (key.equals(viewKey)) {
158 160 newKeys.add(key);
159   - }}));
  161 + }
  162 + }));
160 163 return newKeys;
161 164 }
162 165
  166 + @Override
  167 + public ListenableFuture<List<Void>> remove(EntityId entityId, List<DeleteTsKvQuery> deleteTsKvQueries) {
  168 + validate(entityId);
  169 + deleteTsKvQueries.forEach(BaseTimeseriesService::validate);
  170 + List<ListenableFuture<Void>> futures = Lists.newArrayListWithExpectedSize(deleteTsKvQueries.size() * DELETES_PER_ENTRY);
  171 + for (DeleteTsKvQuery tsKvQuery : deleteTsKvQueries) {
  172 + deleteAndRegisterFutures(futures, entityId, tsKvQuery);
  173 + }
  174 + return Futures.allAsList(futures);
  175 + }
  176 +
  177 + private void deleteAndRegisterFutures(List<ListenableFuture<Void>> futures, EntityId entityId, DeleteTsKvQuery query) {
  178 + futures.add(timeseriesDao.remove(entityId, query));
  179 + futures.add(timeseriesDao.removeLatest(entityId, query));
  180 + futures.add(timeseriesDao.removePartition(entityId, query));
  181 + }
  182 +
163 183 private static void validate(EntityId entityId) {
164 184 Validator.validateEntityId(entityId, "Incorrect entityId " + entityId);
165 185 }
166 186
167   - private static void validate(TsKvQuery query) {
  187 + private static void validate(ReadTsKvQuery query) {
168 188 if (query == null) {
169   - throw new IncorrectParameterException("TsKvQuery can't be null");
  189 + throw new IncorrectParameterException("ReadTsKvQuery can't be null");
170 190 } else if (isBlank(query.getKey())) {
171   - throw new IncorrectParameterException("Incorrect TsKvQuery. Key can't be empty");
  191 + throw new IncorrectParameterException("Incorrect ReadTsKvQuery. Key can't be empty");
172 192 } else if (query.getAggregation() == null) {
173   - throw new IncorrectParameterException("Incorrect TsKvQuery. Aggregation can't be empty");
  193 + throw new IncorrectParameterException("Incorrect ReadTsKvQuery. Aggregation can't be empty");
  194 + }
  195 + }
  196 +
  197 + private static void validate(DeleteTsKvQuery query) {
  198 + if (query == null) {
  199 + throw new IncorrectParameterException("DeleteTsKvQuery can't be null");
  200 + } else if (isBlank(query.getKey())) {
  201 + throw new IncorrectParameterException("Incorrect DeleteTsKvQuery. Key can't be empty");
174 202 }
175 203 }
176 204
177   - private static TsKvQuery updateQuery(Long startTs, Long endTs, String viewKey, TsKvQuery query) {
  205 + private static ReadTsKvQuery updateQuery(Long startTs, Long endTs, String viewKey, ReadTsKvQuery query) {
178 206 return startTs <= query.getStartTs() && endTs >= query.getEndTs() ? query :
179   - new BaseTsKvQuery(viewKey, startTs, endTs, query.getInterval(), query.getLimit(), query.getAggregation());
  207 + new BaseReadTsKvQuery(viewKey, startTs, endTs, query.getInterval(), query.getLimit(), query.getAggregation());
180 208 }
181 209
182 210 private static void checkForNonEntityView(EntityId entityId) throws Exception {
... ...
... ... @@ -20,6 +20,7 @@ import com.datastax.driver.core.PreparedStatement;
20 20 import com.datastax.driver.core.ResultSet;
21 21 import com.datastax.driver.core.ResultSetFuture;
22 22 import com.datastax.driver.core.Row;
  23 +import com.datastax.driver.core.Statement;
23 24 import com.datastax.driver.core.querybuilder.QueryBuilder;
24 25 import com.datastax.driver.core.querybuilder.Select;
25 26 import com.google.common.base.Function;
... ... @@ -34,16 +35,17 @@ import org.springframework.core.env.Environment;
34 35 import org.springframework.stereotype.Component;
35 36 import org.thingsboard.server.common.data.id.EntityId;
36 37 import org.thingsboard.server.common.data.kv.Aggregation;
37   -import org.thingsboard.server.common.data.kv.BaseTsKvQuery;
  38 +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
38 39 import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
39 40 import org.thingsboard.server.common.data.kv.BooleanDataEntry;
40 41 import org.thingsboard.server.common.data.kv.DataType;
  42 +import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
41 43 import org.thingsboard.server.common.data.kv.DoubleDataEntry;
42 44 import org.thingsboard.server.common.data.kv.KvEntry;
43 45 import org.thingsboard.server.common.data.kv.LongDataEntry;
  46 +import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
44 47 import org.thingsboard.server.common.data.kv.StringDataEntry;
45 48 import org.thingsboard.server.common.data.kv.TsKvEntry;
46   -import org.thingsboard.server.common.data.kv.TsKvQuery;
47 49 import org.thingsboard.server.dao.model.ModelConstants;
48 50 import org.thingsboard.server.dao.nosql.CassandraAbstractAsyncDao;
49 51 import org.thingsboard.server.dao.util.NoSqlDao;
... ... @@ -54,11 +56,11 @@ import javax.annotation.PreDestroy;
54 56 import java.time.Instant;
55 57 import java.time.LocalDateTime;
56 58 import java.time.ZoneOffset;
  59 +import java.util.ArrayList;
57 60 import java.util.Arrays;
  61 +import java.util.Collections;
58 62 import java.util.List;
59   -import java.util.ArrayList;
60 63 import java.util.Optional;
61   -import java.util.Collections;
62 64 import java.util.stream.Collectors;
63 65
64 66 import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
... ... @@ -76,8 +78,8 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
76 78 public static final String GENERATED_QUERY_FOR_ENTITY_TYPE_AND_ENTITY_ID = "Generated query [{}] for entityType {} and entityId {}";
77 79 public static final String SELECT_PREFIX = "SELECT ";
78 80 public static final String EQUALS_PARAM = " = ? ";
79   -
80   -
  81 + public static final String ASC_ORDER = "ASC";
  82 + public static final String DESC_ORDER = "DESC";
81 83 private static List<Long> FIXED_PARTITION = Arrays.asList(new Long[]{0L});
82 84
83 85 @Autowired
... ... @@ -96,9 +98,12 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
96 98 private PreparedStatement latestInsertStmt;
97 99 private PreparedStatement[] saveStmts;
98 100 private PreparedStatement[] saveTtlStmts;
99   - private PreparedStatement[] fetchStmts;
  101 + private PreparedStatement[] fetchStmtsAsc;
  102 + private PreparedStatement[] fetchStmtsDesc;
100 103 private PreparedStatement findLatestStmt;
101 104 private PreparedStatement findAllLatestStmt;
  105 + private PreparedStatement deleteStmt;
  106 + private PreparedStatement deletePartitionStmt;
102 107
103 108 private boolean isInstall() {
104 109 return environment.acceptsProfiles("install");
... ... @@ -108,7 +113,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
108 113 public void init() {
109 114 super.startExecutor();
110 115 if (!isInstall()) {
111   - getFetchStmt(Aggregation.NONE);
  116 + getFetchStmt(Aggregation.NONE, DESC_ORDER);
112 117 Optional<TsPartitionDate> partition = TsPartitionDate.parse(partitioning);
113 118 if (partition.isPresent()) {
114 119 tsFormat = partition.get();
... ... @@ -125,7 +130,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
125 130 }
126 131
127 132 @Override
128   - public ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, List<TsKvQuery> queries) {
  133 + public ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, List<ReadTsKvQuery> queries) {
129 134 List<ListenableFuture<List<TsKvEntry>>> futures = queries.stream().map(query -> findAllAsync(entityId, query)).collect(Collectors.toList());
130 135 return Futures.transform(Futures.allAsList(futures), new Function<List<List<TsKvEntry>>, List<TsKvEntry>>() {
131 136 @Nullable
... ... @@ -142,7 +147,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
142 147 }
143 148
144 149
145   - private ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, TsKvQuery query) {
  150 + private ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, ReadTsKvQuery query) {
146 151 if (query.getAggregation() == Aggregation.NONE) {
147 152 return findAllAsyncWithLimit(entityId, query);
148 153 } else {
... ... @@ -152,7 +157,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
152 157 while (stepTs < query.getEndTs()) {
153 158 long startTs = stepTs;
154 159 long endTs = stepTs + step;
155   - TsKvQuery subQuery = new BaseTsKvQuery(query.getKey(), startTs, endTs, step, 1, query.getAggregation());
  160 + ReadTsKvQuery subQuery = new BaseReadTsKvQuery(query.getKey(), startTs, endTs, step, 1, query.getAggregation(), query.getOrderBy());
156 161 futures.add(findAndAggregateAsync(entityId, subQuery, toPartitionTs(startTs), toPartitionTs(endTs)));
157 162 stepTs = endTs;
158 163 }
... ... @@ -171,7 +176,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
171 176 return tsFormat.getTruncateUnit().equals(TsPartitionDate.EPOCH_START);
172 177 }
173 178
174   - private ListenableFuture<List<Long>> getPartitionsFuture(TsKvQuery query, EntityId entityId, long minPartition, long maxPartition) {
  179 + private ListenableFuture<List<Long>> getPartitionsFuture(ReadTsKvQuery query, EntityId entityId, long minPartition, long maxPartition) {
175 180 if (isFixedPartitioning()) { //no need to fetch partitions from DB
176 181 return Futures.immediateFuture(FIXED_PARTITION);
177 182 }
... ... @@ -179,11 +184,9 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
179 184 return Futures.transform(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor);
180 185 }
181 186
182   - private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, TsKvQuery query) {
183   -
  187 + private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, ReadTsKvQuery query) {
184 188 long minPartition = toPartitionTs(query.getStartTs());
185 189 long maxPartition = toPartitionTs(query.getEndTs());
186   -
187 190 final ListenableFuture<List<Long>> partitionsListFuture = getPartitionsFuture(query, entityId, minPartition, maxPartition);
188 191 final SimpleListenableFuture<List<TsKvEntry>> resultFuture = new SimpleListenableFuture<>();
189 192
... ... @@ -212,7 +215,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
212 215 if (cursor.isFull() || !cursor.hasNextPartition()) {
213 216 resultFuture.set(cursor.getData());
214 217 } else {
215   - PreparedStatement proto = getFetchStmt(Aggregation.NONE);
  218 + PreparedStatement proto = getFetchStmt(Aggregation.NONE, cursor.getOrderBy());
216 219 BoundStatement stmt = proto.bind();
217 220 stmt.setString(0, cursor.getEntityType());
218 221 stmt.setUUID(1, cursor.getEntityId());
... ... @@ -237,14 +240,12 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
237 240 }
238 241 }
239 242
240   - private ListenableFuture<Optional<TsKvEntry>> findAndAggregateAsync(EntityId entityId, TsKvQuery query, long minPartition, long maxPartition) {
  243 + private ListenableFuture<Optional<TsKvEntry>> findAndAggregateAsync(EntityId entityId, ReadTsKvQuery query, long minPartition, long maxPartition) {
241 244 final Aggregation aggregation = query.getAggregation();
242 245 final String key = query.getKey();
243 246 final long startTs = query.getStartTs();
244 247 final long endTs = query.getEndTs();
245 248 final long ts = startTs + (endTs - startTs) / 2;
246   -
247   -
248 249 ListenableFuture<List<Long>> partitionsListFuture = getPartitionsFuture(query, entityId, minPartition, maxPartition);
249 250 ListenableFuture<List<ResultSet>> aggregationChunks = Futures.transformAsync(partitionsListFuture,
250 251 getFetchChunksAsyncFunction(entityId, key, aggregation, startTs, endTs), readResultsProcessingExecutor);
... ... @@ -260,7 +261,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
260 261 private AsyncFunction<List<Long>, List<ResultSet>> getFetchChunksAsyncFunction(EntityId entityId, String key, Aggregation aggregation, long startTs, long endTs) {
261 262 return partitions -> {
262 263 try {
263   - PreparedStatement proto = getFetchStmt(aggregation);
  264 + PreparedStatement proto = getFetchStmt(aggregation, DESC_ORDER);
264 265 List<ResultSetFuture> futures = new ArrayList<>(partitions.size());
265 266 for (Long partition : partitions) {
266 267 log.trace("Fetching data for partition [{}] for entityType {} and entityId {}", partition, entityId.getEntityType(), entityId.getId());
... ... @@ -363,6 +364,204 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
363 364 return getFuture(executeAsyncWrite(stmt), rs -> null);
364 365 }
365 366
  367 + @Override
  368 + public ListenableFuture<Void> remove(EntityId entityId, DeleteTsKvQuery query) {
  369 + long minPartition = toPartitionTs(query.getStartTs());
  370 + long maxPartition = toPartitionTs(query.getEndTs());
  371 +
  372 + ResultSetFuture partitionsFuture = fetchPartitions(entityId, query.getKey(), minPartition, maxPartition);
  373 +
  374 + final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();
  375 + final ListenableFuture<List<Long>> partitionsListFuture = Futures.transform(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor);
  376 +
  377 + Futures.addCallback(partitionsListFuture, new FutureCallback<List<Long>>() {
  378 + @Override
  379 + public void onSuccess(@Nullable List<Long> partitions) {
  380 + QueryCursor cursor = new QueryCursor(entityId.getEntityType().name(), entityId.getId(), query, partitions);
  381 + deleteAsync(cursor, resultFuture);
  382 + }
  383 +
  384 + @Override
  385 + public void onFailure(Throwable t) {
  386 + log.error("[{}][{}] Failed to fetch partitions for interval {}-{}", entityId.getEntityType().name(), entityId.getId(), minPartition, maxPartition, t);
  387 + }
  388 + }, readResultsProcessingExecutor);
  389 + return resultFuture;
  390 + }
  391 +
  392 + private void deleteAsync(final QueryCursor cursor, final SimpleListenableFuture<Void> resultFuture) {
  393 + if (!cursor.hasNextPartition()) {
  394 + resultFuture.set(null);
  395 + } else {
  396 + PreparedStatement proto = getDeleteStmt();
  397 + BoundStatement stmt = proto.bind();
  398 + stmt.setString(0, cursor.getEntityType());
  399 + stmt.setUUID(1, cursor.getEntityId());
  400 + stmt.setString(2, cursor.getKey());
  401 + stmt.setLong(3, cursor.getNextPartition());
  402 + stmt.setLong(4, cursor.getStartTs());
  403 + stmt.setLong(5, cursor.getEndTs());
  404 +
  405 + Futures.addCallback(executeAsyncWrite(stmt), new FutureCallback<ResultSet>() {
  406 + @Override
  407 + public void onSuccess(@Nullable ResultSet result) {
  408 + deleteAsync(cursor, resultFuture);
  409 + }
  410 +
  411 + @Override
  412 + public void onFailure(Throwable t) {
  413 + log.error("[{}][{}] Failed to delete data for query {}-{}", stmt, t);
  414 + }
  415 + }, readResultsProcessingExecutor);
  416 + }
  417 + }
  418 +
  419 + private PreparedStatement getDeleteStmt() {
  420 + if (deleteStmt == null) {
  421 + deleteStmt = prepare("DELETE FROM " + ModelConstants.TS_KV_CF +
  422 + " WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM
  423 + + "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM
  424 + + "AND " + ModelConstants.KEY_COLUMN + EQUALS_PARAM
  425 + + "AND " + ModelConstants.PARTITION_COLUMN + EQUALS_PARAM
  426 + + "AND " + ModelConstants.TS_COLUMN + " > ? "
  427 + + "AND " + ModelConstants.TS_COLUMN + " <= ?");
  428 + }
  429 + return deleteStmt;
  430 + }
  431 +
  432 + @Override
  433 + public ListenableFuture<Void> removeLatest(EntityId entityId, DeleteTsKvQuery query) {
  434 + ListenableFuture<TsKvEntry> latestEntryFuture = findLatest(entityId, query.getKey());
  435 +
  436 + ListenableFuture<Boolean> booleanFuture = Futures.transformAsync(latestEntryFuture, latestEntry -> {
  437 + long ts = latestEntry.getTs();
  438 + if (ts >= query.getStartTs() && ts <= query.getEndTs()) {
  439 + return Futures.immediateFuture(true);
  440 + } else {
  441 + log.trace("Won't be deleted latest value for [{}], key - {}", entityId, query.getKey());
  442 + }
  443 + return Futures.immediateFuture(false);
  444 + }, readResultsProcessingExecutor);
  445 +
  446 + ListenableFuture<Void> removedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
  447 + if (isRemove) {
  448 + return deleteLatest(entityId, query.getKey());
  449 + }
  450 + return Futures.immediateFuture(null);
  451 + }, readResultsProcessingExecutor);
  452 +
  453 + if (query.getRewriteLatestIfDeleted()) {
  454 + ListenableFuture<Void> savedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
  455 + if (isRemove) {
  456 + return getNewLatestEntryFuture(entityId, query);
  457 + }
  458 + return Futures.immediateFuture(null);
  459 + }, readResultsProcessingExecutor);
  460 +
  461 + return Futures.transformAsync(Futures.allAsList(Arrays.asList(savedLatestFuture, removedLatestFuture)),
  462 + list -> Futures.immediateFuture(null), readResultsProcessingExecutor);
  463 + }
  464 + return removedLatestFuture;
  465 + }
  466 +
  467 + private ListenableFuture<Void> getNewLatestEntryFuture(EntityId entityId, DeleteTsKvQuery query) {
  468 + long startTs = 0;
  469 + long endTs = query.getStartTs() - 1;
  470 + ReadTsKvQuery findNewLatestQuery = new BaseReadTsKvQuery(query.getKey(), startTs, endTs, endTs - startTs, 1,
  471 + Aggregation.NONE, DESC_ORDER);
  472 + ListenableFuture<List<TsKvEntry>> future = findAllAsync(entityId, findNewLatestQuery);
  473 +
  474 + return Futures.transformAsync(future, entryList -> {
  475 + if (entryList.size() == 1) {
  476 + return saveLatest(entityId, entryList.get(0));
  477 + } else {
  478 + log.trace("Could not find new latest value for [{}], key - {}", entityId, query.getKey());
  479 + }
  480 + return Futures.immediateFuture(null);
  481 + }, readResultsProcessingExecutor);
  482 + }
  483 +
  484 + private ListenableFuture<Void> deleteLatest(EntityId entityId, String key) {
  485 + Statement delete = QueryBuilder.delete().all().from(ModelConstants.TS_KV_LATEST_CF)
  486 + .where(eq(ModelConstants.ENTITY_TYPE_COLUMN, entityId.getEntityType()))
  487 + .and(eq(ModelConstants.ENTITY_ID_COLUMN, entityId.getId()))
  488 + .and(eq(ModelConstants.KEY_COLUMN, key));
  489 + log.debug("Remove request: {}", delete.toString());
  490 + return getFuture(executeAsyncWrite(delete), rs -> null);
  491 + }
  492 +
  493 + @Override
  494 + public ListenableFuture<Void> removePartition(EntityId entityId, DeleteTsKvQuery query) {
  495 + long minPartition = toPartitionTs(query.getStartTs());
  496 + long maxPartition = toPartitionTs(query.getEndTs());
  497 + if (minPartition == maxPartition) {
  498 + return Futures.immediateFuture(null);
  499 + } else {
  500 + ResultSetFuture partitionsFuture = fetchPartitions(entityId, query.getKey(), minPartition, maxPartition);
  501 +
  502 + final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();
  503 + final ListenableFuture<List<Long>> partitionsListFuture = Futures.transform(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor);
  504 +
  505 + Futures.addCallback(partitionsListFuture, new FutureCallback<List<Long>>() {
  506 + @Override
  507 + public void onSuccess(@Nullable List<Long> partitions) {
  508 + int index = 0;
  509 + if (minPartition != query.getStartTs()) {
  510 + index = 1;
  511 + }
  512 + List<Long> partitionsToDelete = new ArrayList<>();
  513 + for (int i = index; i < partitions.size() - 1; i++) {
  514 + partitionsToDelete.add(partitions.get(i));
  515 + }
  516 + QueryCursor cursor = new QueryCursor(entityId.getEntityType().name(), entityId.getId(), query, partitionsToDelete);
  517 + deletePartitionAsync(cursor, resultFuture);
  518 + }
  519 +
  520 + @Override
  521 + public void onFailure(Throwable t) {
  522 + log.error("[{}][{}] Failed to fetch partitions for interval {}-{}", entityId.getEntityType().name(), entityId.getId(), minPartition, maxPartition, t);
  523 + }
  524 + }, readResultsProcessingExecutor);
  525 + return resultFuture;
  526 + }
  527 + }
  528 +
  529 + private void deletePartitionAsync(final QueryCursor cursor, final SimpleListenableFuture<Void> resultFuture) {
  530 + if (!cursor.hasNextPartition()) {
  531 + resultFuture.set(null);
  532 + } else {
  533 + PreparedStatement proto = getDeletePartitionStmt();
  534 + BoundStatement stmt = proto.bind();
  535 + stmt.setString(0, cursor.getEntityType());
  536 + stmt.setUUID(1, cursor.getEntityId());
  537 + stmt.setLong(2, cursor.getNextPartition());
  538 + stmt.setString(3, cursor.getKey());
  539 +
  540 + Futures.addCallback(executeAsyncWrite(stmt), new FutureCallback<ResultSet>() {
  541 + @Override
  542 + public void onSuccess(@Nullable ResultSet result) {
  543 + deletePartitionAsync(cursor, resultFuture);
  544 + }
  545 +
  546 + @Override
  547 + public void onFailure(Throwable t) {
  548 + log.error("[{}][{}] Failed to delete data for query {}-{}", stmt, t);
  549 + }
  550 + }, readResultsProcessingExecutor);
  551 + }
  552 + }
  553 +
  554 + private PreparedStatement getDeletePartitionStmt() {
  555 + if (deletePartitionStmt == null) {
  556 + deletePartitionStmt = prepare("DELETE FROM " + ModelConstants.TS_KV_PARTITIONS_CF +
  557 + " WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM
  558 + + "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM
  559 + + "AND " + ModelConstants.PARTITION_COLUMN + EQUALS_PARAM
  560 + + "AND " + ModelConstants.KEY_COLUMN + EQUALS_PARAM);
  561 + }
  562 + return deletePartitionStmt;
  563 + }
  564 +
366 565 private List<TsKvEntry> convertResultToTsKvEntryList(List<Row> rows) {
367 566 List<TsKvEntry> entries = new ArrayList<>(rows.size());
368 567 if (!rows.isEmpty()) {
... ... @@ -458,28 +657,43 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
458 657 return saveTtlStmts[dataType.ordinal()];
459 658 }
460 659
461   - private PreparedStatement getFetchStmt(Aggregation aggType) {
462   - if (fetchStmts == null) {
463   - fetchStmts = new PreparedStatement[Aggregation.values().length];
464   - for (Aggregation type : Aggregation.values()) {
465   - if (type == Aggregation.SUM && fetchStmts[Aggregation.AVG.ordinal()] != null) {
466   - fetchStmts[type.ordinal()] = fetchStmts[Aggregation.AVG.ordinal()];
467   - } else if (type == Aggregation.AVG && fetchStmts[Aggregation.SUM.ordinal()] != null) {
468   - fetchStmts[type.ordinal()] = fetchStmts[Aggregation.SUM.ordinal()];
469   - } else {
470   - fetchStmts[type.ordinal()] = prepare(SELECT_PREFIX +
471   - String.join(", ", ModelConstants.getFetchColumnNames(type)) + " FROM " + ModelConstants.TS_KV_CF
472   - + " WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM
473   - + "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM
474   - + "AND " + ModelConstants.KEY_COLUMN + EQUALS_PARAM
475   - + "AND " + ModelConstants.PARTITION_COLUMN + EQUALS_PARAM
476   - + "AND " + ModelConstants.TS_COLUMN + " > ? "
477   - + "AND " + ModelConstants.TS_COLUMN + " <= ?"
478   - + (type == Aggregation.NONE ? " ORDER BY " + ModelConstants.TS_COLUMN + " DESC LIMIT ?" : ""));
  660 + private PreparedStatement getFetchStmt(Aggregation aggType, String orderBy) {
  661 + switch (orderBy) {
  662 + case ASC_ORDER:
  663 + if (fetchStmtsAsc == null) {
  664 + fetchStmtsAsc = initFetchStmt(orderBy);
  665 + }
  666 + return fetchStmtsAsc[aggType.ordinal()];
  667 + case DESC_ORDER:
  668 + if (fetchStmtsDesc == null) {
  669 + fetchStmtsDesc = initFetchStmt(orderBy);
479 670 }
  671 + return fetchStmtsDesc[aggType.ordinal()];
  672 + default:
  673 + throw new RuntimeException("Not supported" + orderBy + "order!");
  674 + }
  675 + }
  676 +
  677 + private PreparedStatement[] initFetchStmt(String orderBy) {
  678 + PreparedStatement[] fetchStmts = new PreparedStatement[Aggregation.values().length];
  679 + for (Aggregation type : Aggregation.values()) {
  680 + if (type == Aggregation.SUM && fetchStmts[Aggregation.AVG.ordinal()] != null) {
  681 + fetchStmts[type.ordinal()] = fetchStmts[Aggregation.AVG.ordinal()];
  682 + } else if (type == Aggregation.AVG && fetchStmts[Aggregation.SUM.ordinal()] != null) {
  683 + fetchStmts[type.ordinal()] = fetchStmts[Aggregation.SUM.ordinal()];
  684 + } else {
  685 + fetchStmts[type.ordinal()] = prepare(SELECT_PREFIX +
  686 + String.join(", ", ModelConstants.getFetchColumnNames(type)) + " FROM " + ModelConstants.TS_KV_CF
  687 + + " WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM
  688 + + "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM
  689 + + "AND " + ModelConstants.KEY_COLUMN + EQUALS_PARAM
  690 + + "AND " + ModelConstants.PARTITION_COLUMN + EQUALS_PARAM
  691 + + "AND " + ModelConstants.TS_COLUMN + " > ? "
  692 + + "AND " + ModelConstants.TS_COLUMN + " <= ?"
  693 + + (type == Aggregation.NONE ? " ORDER BY " + ModelConstants.TS_COLUMN + " " + orderBy + " LIMIT ?" : ""));
480 694 }
481 695 }
482   - return fetchStmts[aggType.ordinal()];
  696 + return fetchStmts;
483 697 }
484 698
485 699 private PreparedStatement getLatestStmt() {
... ...
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.timeseries;
  17 +
  18 +import lombok.Getter;
  19 +import org.thingsboard.server.common.data.kv.TsKvQuery;
  20 +
  21 +import java.util.List;
  22 +import java.util.UUID;
  23 +
  24 +public class QueryCursor {
  25 +
  26 + @Getter
  27 + protected final String entityType;
  28 + @Getter
  29 + protected final UUID entityId;
  30 + @Getter
  31 + protected final String key;
  32 + @Getter
  33 + private final long startTs;
  34 + @Getter
  35 + private final long endTs;
  36 +
  37 + final List<Long> partitions;
  38 + private int partitionIndex;
  39 +
  40 + public QueryCursor(String entityType, UUID entityId, TsKvQuery baseQuery, List<Long> partitions) {
  41 + this.entityType = entityType;
  42 + this.entityId = entityId;
  43 + this.key = baseQuery.getKey();
  44 + this.startTs = baseQuery.getStartTs();
  45 + this.endTs = baseQuery.getEndTs();
  46 + this.partitions = partitions;
  47 + this.partitionIndex = partitions.size() - 1;
  48 + }
  49 +
  50 + public boolean hasNextPartition() {
  51 + return partitionIndex >= 0;
  52 + }
  53 +
  54 + public long getNextPartition() {
  55 + long partition = partitions.get(partitionIndex);
  56 + partitionIndex--;
  57 + return partition;
  58 + }
  59 +
  60 +}
... ...
... ... @@ -17,8 +17,9 @@ package org.thingsboard.server.dao.timeseries;
17 17
18 18 import com.google.common.util.concurrent.ListenableFuture;
19 19 import org.thingsboard.server.common.data.id.EntityId;
  20 +import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
  21 +import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
20 22 import org.thingsboard.server.common.data.kv.TsKvEntry;
21   -import org.thingsboard.server.common.data.kv.TsKvQuery;
22 23
23 24 import java.util.List;
24 25
... ... @@ -27,7 +28,7 @@ import java.util.List;
27 28 */
28 29 public interface TimeseriesDao {
29 30
30   - ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, List<TsKvQuery> queries);
  31 + ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, List<ReadTsKvQuery> queries);
31 32
32 33 ListenableFuture<TsKvEntry> findLatest(EntityId entityId, String key);
33 34
... ... @@ -38,4 +39,10 @@ public interface TimeseriesDao {
38 39 ListenableFuture<Void> savePartition(EntityId entityId, long tsKvEntryTs, String key, long ttl);
39 40
40 41 ListenableFuture<Void> saveLatest(EntityId entityId, TsKvEntry tsKvEntry);
  42 +
  43 + ListenableFuture<Void> remove(EntityId entityId, DeleteTsKvQuery query);
  44 +
  45 + ListenableFuture<Void> removeLatest(EntityId entityId, DeleteTsKvQuery query);
  46 +
  47 + ListenableFuture<Void> removePartition(EntityId entityId, DeleteTsKvQuery query);
41 48 }
... ...
... ... @@ -17,8 +17,9 @@ package org.thingsboard.server.dao.timeseries;
17 17
18 18 import com.google.common.util.concurrent.ListenableFuture;
19 19 import org.thingsboard.server.common.data.id.EntityId;
  20 +import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
  21 +import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
20 22 import org.thingsboard.server.common.data.kv.TsKvEntry;
21   -import org.thingsboard.server.common.data.kv.TsKvQuery;
22 23
23 24 import java.util.Collection;
24 25 import java.util.List;
... ... @@ -28,7 +29,7 @@ import java.util.List;
28 29 */
29 30 public interface TimeseriesService {
30 31
31   - ListenableFuture<List<TsKvEntry>> findAll(EntityId entityId, List<TsKvQuery> queries);
  32 + ListenableFuture<List<TsKvEntry>> findAll(EntityId entityId, List<ReadTsKvQuery> queries);
32 33
33 34 ListenableFuture<List<TsKvEntry>> findLatest(EntityId entityId, Collection<String> keys);
34 35
... ... @@ -37,4 +38,6 @@ public interface TimeseriesService {
37 38 ListenableFuture<List<Void>> save(EntityId entityId, TsKvEntry tsKvEntry);
38 39
39 40 ListenableFuture<List<Void>> save(EntityId entityId, List<TsKvEntry> tsKvEntry, long ttl);
  41 +
  42 + ListenableFuture<List<Void>> remove(EntityId entityId, List<DeleteTsKvQuery> queries);
40 43 }
... ...
... ... @@ -16,57 +16,53 @@
16 16 package org.thingsboard.server.dao.timeseries;
17 17
18 18 import lombok.Getter;
  19 +import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
19 20 import org.thingsboard.server.common.data.kv.TsKvEntry;
20   -import org.thingsboard.server.common.data.kv.TsKvQuery;
21 21
22 22 import java.util.ArrayList;
23 23 import java.util.List;
24 24 import java.util.UUID;
25 25
  26 +import static org.thingsboard.server.dao.timeseries.CassandraBaseTimeseriesDao.DESC_ORDER;
  27 +
26 28 /**
27 29 * Created by ashvayka on 21.02.17.
28 30 */
29   -public class TsKvQueryCursor {
30   - @Getter
31   - private final String entityType;
32   - @Getter
33   - private final UUID entityId;
34   - @Getter
35   - private final String key;
36   - @Getter
37   - private final long startTs;
38   - @Getter
39   - private final long endTs;
40   - private final List<Long> partitions;
  31 +public class TsKvQueryCursor extends QueryCursor {
  32 +
41 33 @Getter
42 34 private final List<TsKvEntry> data;
  35 + @Getter
  36 + private String orderBy;
43 37
44 38 private int partitionIndex;
45 39 private int currentLimit;
46 40
47   - public TsKvQueryCursor(String entityType, UUID entityId, TsKvQuery baseQuery, List<Long> partitions) {
48   - this.entityType = entityType;
49   - this.entityId = entityId;
50   - this.key = baseQuery.getKey();
51   - this.startTs = baseQuery.getStartTs();
52   - this.endTs = baseQuery.getEndTs();
53   - this.partitions = partitions;
54   - this.partitionIndex = partitions.size() - 1;
  41 + public TsKvQueryCursor(String entityType, UUID entityId, ReadTsKvQuery baseQuery, List<Long> partitions) {
  42 + super(entityType, entityId, baseQuery, partitions);
  43 + this.orderBy = baseQuery.getOrderBy();
  44 + this.partitionIndex = isDesc() ? partitions.size() - 1 : 0;
55 45 this.data = new ArrayList<>();
56 46 this.currentLimit = baseQuery.getLimit();
57 47 }
58 48
  49 + @Override
59 50 public boolean hasNextPartition() {
60   - return partitionIndex >= 0;
  51 + return isDesc() ? partitionIndex >= 0 : partitionIndex <= partitions.size() - 1;
61 52 }
62 53
63 54 public boolean isFull() {
64 55 return currentLimit <= 0;
65 56 }
66 57
  58 + @Override
67 59 public long getNextPartition() {
68 60 long partition = partitions.get(partitionIndex);
69   - partitionIndex--;
  61 + if (isDesc()) {
  62 + partitionIndex--;
  63 + } else {
  64 + partitionIndex++;
  65 + }
70 66 return partition;
71 67 }
72 68
... ... @@ -78,4 +74,8 @@ public class TsKvQueryCursor {
78 74 currentLimit -= newData.size();
79 75 data.addAll(newData);
80 76 }
  77 +
  78 + private boolean isDesc() {
  79 + return orderBy.equals(DESC_ORDER);
  80 + }
81 81 }
... ...
... ... @@ -227,6 +227,13 @@ public abstract class BaseRelationServiceTest extends AbstractServiceTest {
227 227 Assert.assertTrue(relations.contains(relationA));
228 228 Assert.assertTrue(relations.contains(relationB));
229 229 Assert.assertTrue(relations.contains(relationC));
  230 +
  231 + //Test from cache
  232 + relations = relationService.findByQuery(query).get();
  233 + Assert.assertEquals(3, relations.size());
  234 + Assert.assertTrue(relations.contains(relationA));
  235 + Assert.assertTrue(relations.contains(relationB));
  236 + Assert.assertTrue(relations.contains(relationC));
230 237 }
231 238
232 239 @Test
... ... @@ -253,6 +260,12 @@ public abstract class BaseRelationServiceTest extends AbstractServiceTest {
253 260 Assert.assertEquals(2, relations.size());
254 261 Assert.assertTrue(relations.contains(relationAB));
255 262 Assert.assertTrue(relations.contains(relationBC));
  263 +
  264 + //Test from cache
  265 + relations = relationService.findByQuery(query).get();
  266 + Assert.assertEquals(2, relations.size());
  267 + Assert.assertTrue(relations.contains(relationAB));
  268 + Assert.assertTrue(relations.contains(relationBC));
256 269 }
257 270
258 271
... ...
... ... @@ -21,7 +21,8 @@ import org.junit.Assert;
21 21 import org.junit.Test;
22 22 import org.thingsboard.server.common.data.id.DeviceId;
23 23 import org.thingsboard.server.common.data.kv.Aggregation;
24   -import org.thingsboard.server.common.data.kv.BaseTsKvQuery;
  24 +import org.thingsboard.server.common.data.kv.BaseDeleteTsKvQuery;
  25 +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
25 26 import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
26 27 import org.thingsboard.server.common.data.kv.BooleanDataEntry;
27 28 import org.thingsboard.server.common.data.kv.DoubleDataEntry;
... ... @@ -53,6 +54,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
53 54 private static final String BOOLEAN_KEY = "booleanKey";
54 55
55 56 private static final long TS = 42L;
  57 + private static final String DESC_ORDER = "DESC";
56 58
57 59 KvEntry stringKvEntry = new StringDataEntry(STRING_KEY, "value");
58 60 KvEntry longKvEntry = new LongDataEntry(LONG_KEY, Long.MAX_VALUE);
... ... @@ -101,6 +103,26 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
101 103 }
102 104
103 105 @Test
  106 + public void testDeleteDeviceTsData() throws Exception {
  107 + DeviceId deviceId = new DeviceId(UUIDs.timeBased());
  108 +
  109 + saveEntries(deviceId, 10000);
  110 + saveEntries(deviceId, 20000);
  111 + saveEntries(deviceId, 30000);
  112 + saveEntries(deviceId, 40000);
  113 +
  114 + tsService.remove(deviceId, Collections.singletonList(
  115 + new BaseDeleteTsKvQuery(STRING_KEY, 15000, 45000))).get();
  116 +
  117 + List<TsKvEntry> list = tsService.findAll(deviceId, Collections.singletonList(
  118 + new BaseReadTsKvQuery(STRING_KEY, 5000, 45000, 10000, 10, Aggregation.NONE))).get();
  119 + Assert.assertEquals(1, list.size());
  120 +
  121 + List<TsKvEntry> latest = tsService.findLatest(deviceId, Collections.singletonList(STRING_KEY)).get();
  122 + Assert.assertEquals(null, latest.get(0).getValueAsString());
  123 + }
  124 +
  125 + @Test
104 126 public void testFindDeviceTsData() throws Exception {
105 127 DeviceId deviceId = new DeviceId(UUIDs.timeBased());
106 128 List<TsKvEntry> entries = new ArrayList<>();
... ... @@ -114,7 +136,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
114 136 entries.add(save(deviceId, 45000, 500));
115 137 entries.add(save(deviceId, 55000, 600));
116 138
117   - List<TsKvEntry> list = tsService.findAll(deviceId, Collections.singletonList(new BaseTsKvQuery(LONG_KEY, 0,
  139 + List<TsKvEntry> list = tsService.findAll(deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
118 140 60000, 20000, 3, Aggregation.NONE))).get();
119 141 assertEquals(3, list.size());
120 142 assertEquals(55000, list.get(0).getTs());
... ... @@ -126,7 +148,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
126 148 assertEquals(35000, list.get(2).getTs());
127 149 assertEquals(java.util.Optional.of(400L), list.get(2).getLongValue());
128 150
129   - list = tsService.findAll(deviceId, Collections.singletonList(new BaseTsKvQuery(LONG_KEY, 0,
  151 + list = tsService.findAll(deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
130 152 60000, 20000, 3, Aggregation.AVG))).get();
131 153 assertEquals(3, list.size());
132 154 assertEquals(10000, list.get(0).getTs());
... ... @@ -138,7 +160,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
138 160 assertEquals(50000, list.get(2).getTs());
139 161 assertEquals(java.util.Optional.of(550L), list.get(2).getLongValue());
140 162
141   - list = tsService.findAll(deviceId, Collections.singletonList(new BaseTsKvQuery(LONG_KEY, 0,
  163 + list = tsService.findAll(deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
142 164 60000, 20000, 3, Aggregation.SUM))).get();
143 165
144 166 assertEquals(3, list.size());
... ... @@ -151,7 +173,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
151 173 assertEquals(50000, list.get(2).getTs());
152 174 assertEquals(java.util.Optional.of(1100L), list.get(2).getLongValue());
153 175
154   - list = tsService.findAll(deviceId, Collections.singletonList(new BaseTsKvQuery(LONG_KEY, 0,
  176 + list = tsService.findAll(deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
155 177 60000, 20000, 3, Aggregation.MIN))).get();
156 178
157 179 assertEquals(3, list.size());
... ... @@ -164,7 +186,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
164 186 assertEquals(50000, list.get(2).getTs());
165 187 assertEquals(java.util.Optional.of(500L), list.get(2).getLongValue());
166 188
167   - list = tsService.findAll(deviceId, Collections.singletonList(new BaseTsKvQuery(LONG_KEY, 0,
  189 + list = tsService.findAll(deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
168 190 60000, 20000, 3, Aggregation.MAX))).get();
169 191
170 192 assertEquals(3, list.size());
... ... @@ -177,7 +199,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
177 199 assertEquals(50000, list.get(2).getTs());
178 200 assertEquals(java.util.Optional.of(600L), list.get(2).getLongValue());
179 201
180   - list = tsService.findAll(deviceId, Collections.singletonList(new BaseTsKvQuery(LONG_KEY, 0,
  202 + list = tsService.findAll(deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
181 203 60000, 20000, 3, Aggregation.COUNT))).get();
182 204
183 205 assertEquals(3, list.size());
... ...
... ... @@ -40,6 +40,8 @@ public final class MqttClientConfig {
40 40 private Class<? extends Channel> channelClass = NioSocketChannel.class;
41 41
42 42 private boolean reconnect = true;
  43 + private long reconnectDelay = 1L;
  44 + private int maxBytesInMessage = 8092;
43 45
44 46 public MqttClientConfig() {
45 47 this(null);
... ... @@ -146,4 +148,38 @@ public final class MqttClientConfig {
146 148 public void setReconnect(boolean reconnect) {
147 149 this.reconnect = reconnect;
148 150 }
  151 +
  152 + public long getReconnectDelay() {
  153 + return reconnectDelay;
  154 + }
  155 +
  156 + /**
  157 + * Sets the reconnect delay in seconds. Defaults to 1 second.
  158 + * @param reconnectDelay
  159 + * @throws IllegalArgumentException if reconnectDelay is smaller than 1.
  160 + */
  161 + public void setReconnectDelay(long reconnectDelay) {
  162 + if (reconnectDelay <= 0) {
  163 + throw new IllegalArgumentException("reconnectDelay must be > 0");
  164 + }
  165 + this.reconnectDelay = reconnectDelay;
  166 + }
  167 +
  168 + public int getMaxBytesInMessage() {
  169 + return maxBytesInMessage;
  170 + }
  171 +
  172 + /**
  173 + * Sets the maximum number of bytes in the message for the {@link io.netty.handler.codec.mqtt.MqttDecoder}.
  174 + * Default value is 8092 as specified by Netty. The absolute maximum size is 256MB as set by the MQTT spec.
  175 + *
  176 + * @param maxBytesInMessage
  177 + * @throws IllegalArgumentException if maxBytesInMessage is smaller than 1 or greater than 256_000_000.
  178 + */
  179 + public void setMaxBytesInMessage(int maxBytesInMessage) {
  180 + if (maxBytesInMessage <= 0 || maxBytesInMessage > 256_000_000) {
  181 + throw new IllegalArgumentException("maxBytesInMessage must be > 0 or < 256_000_000");
  182 + }
  183 + this.maxBytesInMessage = maxBytesInMessage;
  184 + }
149 185 }
... ...
... ... @@ -155,7 +155,7 @@ final class MqttClientImpl implements MqttClient {
155 155 if (reconnect) {
156 156 this.reconnect = true;
157 157 }
158   - eventLoop.schedule((Runnable) () -> connect(host, port, reconnect), 1L, TimeUnit.SECONDS);
  158 + eventLoop.schedule((Runnable) () -> connect(host, port, reconnect), clientConfig.getReconnectDelay(), TimeUnit.SECONDS);
159 159 }
160 160 }
161 161
... ... @@ -512,7 +512,7 @@ final class MqttClientImpl implements MqttClient {
512 512 ch.pipeline().addLast(sslContext.newHandler(ch.alloc(), host, port));
513 513 }
514 514
515   - ch.pipeline().addLast("mqttDecoder", new MqttDecoder());
  515 + ch.pipeline().addLast("mqttDecoder", new MqttDecoder(clientConfig.getMaxBytesInMessage()));
516 516 ch.pipeline().addLast("mqttEncoder", MqttEncoder.INSTANCE);
517 517 ch.pipeline().addLast("idleStateHandler", new IdleStateHandler(MqttClientImpl.this.clientConfig.getTimeoutSeconds(), MqttClientImpl.this.clientConfig.getTimeoutSeconds(), 0));
518 518 ch.pipeline().addLast("mqttPingHandler", new MqttPingHandler(MqttClientImpl.this.clientConfig.getTimeoutSeconds()));
... ...
... ... @@ -16,11 +16,14 @@
16 16 package org.thingsboard.rule.engine.api;
17 17
18 18 import com.google.common.util.concurrent.FutureCallback;
  19 +import org.thingsboard.server.common.data.id.DeviceId;
19 20 import org.thingsboard.server.common.data.id.EntityId;
  21 +import org.thingsboard.server.common.data.id.TenantId;
20 22 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
21 23 import org.thingsboard.server.common.data.kv.TsKvEntry;
22 24
23 25 import java.util.List;
  26 +import java.util.Set;
24 27
25 28 /**
26 29 * Created by ashvayka on 02.04.18.
... ... @@ -41,4 +44,6 @@ public interface RuleEngineTelemetryService {
41 44
42 45 void saveAttrAndNotify(EntityId entityId, String scope, String key, boolean value, FutureCallback<Void> callback);
43 46
  47 + void onSharedAttributesUpdate(TenantId tenantId, DeviceId deviceId, Set<AttributeKvEntry> attributes);
  48 +
44 49 }
... ...
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.rule.engine.metadata;
  17 +
  18 +import com.fasterxml.jackson.core.JsonGenerator;
  19 +import com.fasterxml.jackson.core.JsonParser;
  20 +import com.fasterxml.jackson.databind.ObjectMapper;
  21 +import com.fasterxml.jackson.databind.node.ArrayNode;
  22 +import com.fasterxml.jackson.databind.node.ObjectNode;
  23 +import com.google.common.util.concurrent.ListenableFuture;
  24 +import lombok.extern.slf4j.Slf4j;
  25 +import org.thingsboard.rule.engine.api.*;
  26 +import org.thingsboard.rule.engine.api.util.DonAsynchron;
  27 +import org.thingsboard.rule.engine.api.util.TbNodeUtils;
  28 +import org.thingsboard.server.common.data.kv.BaseTsKvQuery;
  29 +import org.thingsboard.server.common.data.kv.TsKvEntry;
  30 +import org.thingsboard.server.common.data.kv.TsKvQuery;
  31 +import org.thingsboard.server.common.data.plugin.ComponentType;
  32 +import org.thingsboard.server.common.msg.TbMsg;
  33 +
  34 +import java.util.List;
  35 +import java.util.concurrent.ExecutionException;
  36 +import java.util.concurrent.TimeUnit;
  37 +import java.util.stream.Collectors;
  38 +
  39 +import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS;
  40 +import static org.thingsboard.rule.engine.metadata.TbGetTelemetryNodeConfiguration.FETCH_MODE_ALL;
  41 +import static org.thingsboard.rule.engine.metadata.TbGetTelemetryNodeConfiguration.MAX_FETCH_SIZE;
  42 +import static org.thingsboard.server.common.data.kv.Aggregation.NONE;
  43 +
  44 +/**
  45 + * Created by mshvayka on 04.09.18.
  46 + */
  47 +@Slf4j
  48 +@RuleNode(type = ComponentType.ENRICHMENT,
  49 + name = "originator telemetry",
  50 + configClazz = TbGetTelemetryNodeConfiguration.class,
  51 + nodeDescription = "Add Message Originator Telemetry for selected time range into Message Metadata\n",
  52 + nodeDetails = "The node allows you to select fetch mode <b>FIRST/LAST/ALL</b> to fetch telemetry of certain time range that are added into Message metadata without any prefix. " +
  53 + "If selected fetch mode <b>ALL</b> Telemetry will be added like array into Message Metadata where <b>key</b> is Timestamp and <b>value</b> is value of Telemetry. " +
  54 + "If selected fetch mode <b>FIRST</b> or <b>LAST</b> Telemetry will be added like string without Timestamp",
  55 + uiResources = {"static/rulenode/rulenode-core-config.js"},
  56 + configDirective = "tbEnrichmentNodeGetTelemetryFromDatabase")
  57 +public class TbGetTelemetryNode implements TbNode {
  58 +
  59 + private TbGetTelemetryNodeConfiguration config;
  60 + private List<String> tsKeyNames;
  61 + private long startTsOffset;
  62 + private long endTsOffset;
  63 + private int limit;
  64 + private ObjectMapper mapper;
  65 +
  66 + @Override
  67 + public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
  68 + this.config = TbNodeUtils.convert(configuration, TbGetTelemetryNodeConfiguration.class);
  69 + tsKeyNames = config.getLatestTsKeyNames();
  70 + startTsOffset = TimeUnit.valueOf(config.getStartIntervalTimeUnit()).toMillis(config.getStartInterval());
  71 + endTsOffset = TimeUnit.valueOf(config.getEndIntervalTimeUnit()).toMillis(config.getEndInterval());
  72 + limit = config.getFetchMode().equals(FETCH_MODE_ALL) ? MAX_FETCH_SIZE : 1;
  73 + mapper = new ObjectMapper();
  74 + mapper.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, false);
  75 + mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
  76 + }
  77 +
  78 + @Override
  79 + public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException, TbNodeException {
  80 + if (tsKeyNames.isEmpty()) {
  81 + ctx.tellFailure(msg, new IllegalStateException("Telemetry is not selected!"));
  82 + } else {
  83 + try {
  84 + List<TsKvQuery> queries = buildQueries();
  85 + ListenableFuture<List<TsKvEntry>> list = ctx.getTimeseriesService().findAll(msg.getOriginator(), queries);
  86 + DonAsynchron.withCallback(list, data -> {
  87 + process(data, msg);
  88 + TbMsg newMsg = ctx.newMsg(msg.getType(), msg.getOriginator(), msg.getMetaData(), msg.getData());
  89 + ctx.tellNext(newMsg, SUCCESS);
  90 + }, error -> ctx.tellFailure(msg, error), ctx.getDbCallbackExecutor());
  91 + } catch (Exception e) {
  92 + ctx.tellFailure(msg, e);
  93 + }
  94 + }
  95 + }
  96 +
  97 + //TODO: handle direction;
  98 + private List<TsKvQuery> buildQueries() {
  99 + long ts = System.currentTimeMillis();
  100 + long startTs = ts - startTsOffset;
  101 + long endTs = ts - endTsOffset;
  102 +
  103 + return tsKeyNames.stream()
  104 + .map(key -> new BaseTsKvQuery(key, startTs, endTs, 1, limit, NONE))
  105 + .collect(Collectors.toList());
  106 + }
  107 +
  108 + private void process(List<TsKvEntry> entries, TbMsg msg) {
  109 + ObjectNode resultNode = mapper.createObjectNode();
  110 + if (limit == MAX_FETCH_SIZE) {
  111 + entries.forEach(entry -> processArray(resultNode, entry));
  112 + } else {
  113 + entries.forEach(entry -> processSingle(resultNode, entry));
  114 + }
  115 +
  116 + for (String key : tsKeyNames) {
  117 + if(resultNode.has(key)){
  118 + msg.getMetaData().putValue(key, resultNode.get(key).toString());
  119 + }
  120 + }
  121 + }
  122 +
  123 + private void processSingle(ObjectNode node, TsKvEntry entry) {
  124 + node.put(entry.getKey(), entry.getValueAsString());
  125 + }
  126 +
  127 + private void processArray(ObjectNode node, TsKvEntry entry) {
  128 + if(node.has(entry.getKey())){
  129 + ArrayNode arrayNode = (ArrayNode) node.get(entry.getKey());
  130 + ObjectNode obj = buildNode(entry);
  131 + arrayNode.add(obj);
  132 + }else {
  133 + ArrayNode arrayNode = mapper.createArrayNode();
  134 + ObjectNode obj = buildNode(entry);
  135 + arrayNode.add(obj);
  136 + node.set(entry.getKey(), arrayNode);
  137 + }
  138 + }
  139 +
  140 + private ObjectNode buildNode(TsKvEntry entry) {
  141 + ObjectNode obj = mapper.createObjectNode()
  142 + .put("ts", entry.getTs());
  143 + switch (entry.getDataType()) {
  144 + case STRING:
  145 + obj.put("value", entry.getValueAsString());
  146 + break;
  147 + case LONG:
  148 + obj.put("value", entry.getLongValue().get());
  149 + break;
  150 + case BOOLEAN:
  151 + obj.put("value", entry.getBooleanValue().get());
  152 + break;
  153 + case DOUBLE:
  154 + obj.put("value", entry.getDoubleValue().get());
  155 + break;
  156 + }
  157 + return obj;
  158 + }
  159 +
  160 + @Override
  161 + public void destroy() {
  162 +
  163 + }
  164 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.rule.engine.metadata;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.rule.engine.api.NodeConfiguration;
  20 +
  21 +import java.util.Collections;
  22 +import java.util.List;
  23 +import java.util.concurrent.TimeUnit;
  24 +
  25 +/**
  26 + * Created by mshvayka on 04.09.18.
  27 + */
  28 +@Data
  29 +public class TbGetTelemetryNodeConfiguration implements NodeConfiguration<TbGetTelemetryNodeConfiguration> {
  30 +
  31 + public static final String FETCH_MODE_FIRST = "FIRST";
  32 + public static final String FETCH_MODE_LAST = "LAST";
  33 + public static final String FETCH_MODE_ALL = "ALL";
  34 + public static final int MAX_FETCH_SIZE = 1000;
  35 +
  36 + private int startInterval;
  37 + private int endInterval;
  38 + private String startIntervalTimeUnit;
  39 + private String endIntervalTimeUnit;
  40 + private String fetchMode; //FIRST, LAST, LATEST
  41 +
  42 + private List<String> latestTsKeyNames;
  43 +
  44 +
  45 +
  46 + @Override
  47 + public TbGetTelemetryNodeConfiguration defaultConfiguration() {
  48 + TbGetTelemetryNodeConfiguration configuration = new TbGetTelemetryNodeConfiguration();
  49 + configuration.setLatestTsKeyNames(Collections.emptyList());
  50 + configuration.setFetchMode("FIRST");
  51 + configuration.setStartIntervalTimeUnit(TimeUnit.MINUTES.name());
  52 + configuration.setStartInterval(2);
  53 + configuration.setEndIntervalTimeUnit(TimeUnit.MINUTES.name());
  54 + configuration.setEndInterval(1);
  55 + return configuration;
  56 + }
  57 +}
... ...
... ... @@ -17,12 +17,15 @@ package org.thingsboard.rule.engine.telemetry;
17 17
18 18 import com.google.gson.JsonParser;
19 19 import lombok.extern.slf4j.Slf4j;
20   -import org.thingsboard.rule.engine.api.util.TbNodeUtils;
21 20 import org.thingsboard.rule.engine.api.RuleNode;
22 21 import org.thingsboard.rule.engine.api.TbContext;
23 22 import org.thingsboard.rule.engine.api.TbNode;
24 23 import org.thingsboard.rule.engine.api.TbNodeConfiguration;
25 24 import org.thingsboard.rule.engine.api.TbNodeException;
  25 +import org.thingsboard.rule.engine.api.util.TbNodeUtils;
  26 +import org.thingsboard.server.common.data.DataConstants;
  27 +import org.thingsboard.server.common.data.EntityType;
  28 +import org.thingsboard.server.common.data.id.DeviceId;
26 29 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
27 30 import org.thingsboard.server.common.data.plugin.ComponentType;
28 31 import org.thingsboard.server.common.msg.TbMsg;
... ... @@ -62,6 +65,9 @@ public class TbMsgAttributesNode implements TbNode {
62 65 String src = msg.getData();
63 66 Set<AttributeKvEntry> attributes = JsonConverter.convertToAttributes(new JsonParser().parse(src)).getAttributes();
64 67 ctx.getTelemetryService().saveAndNotify(msg.getOriginator(), config.getScope(), new ArrayList<>(attributes), new TelemetryNodeCallback(ctx, msg));
  68 + if (msg.getOriginator().getEntityType() == EntityType.DEVICE && DataConstants.SHARED_SCOPE.equals(config.getScope())) {
  69 + ctx.getTelemetryService().onSharedAttributesUpdate(ctx.getTenantId(), new DeviceId(msg.getOriginator().getId()), attributes);
  70 + }
65 71 }
66 72
67 73 @Override
... ...
1   -!function(e){function t(r){if(n[r])return n[r].exports;var a=n[r]={exports:{},id:r,loaded:!1};return e[r].call(a.exports,a,a.exports,t),a.loaded=!0,a.exports}var n={};return t.m=e,t.c=n,t.p="/static/",t(0)}(function(e){for(var t in e)if(Object.prototype.hasOwnProperty.call(e,t))switch(typeof e[t]){case"function":break;case"object":e[t]=function(t){var n=t.slice(1),r=e[t[0]];return function(e,t,a){r.apply(this,[e,t,a].concat(n))}}(e[t]);break;default:e[t]=e[e[t]]}return e}([function(e,t,n){e.exports=n(76)},function(e,t){},1,1,1,function(e,t){e.exports=' <section ng-form name=attributesConfigForm layout=column> <md-input-container class=md-block> <label translate>attribute.attributes-scope</label> <md-select ng-model=configuration.scope ng-disabled=$root.loading> <md-option ng-repeat="scope in types.attributesScope" ng-value=scope.value> {{scope.name | translate}} </md-option> </md-select> </md-input-container> </section> '},function(e,t){e.exports=" <section class=tb-alarm-config ng-form name=alarmConfigForm layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.alarm-details-builder</label> <tb-js-func ng-model=configuration.alarmDetailsBuildJs function-name=Details function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row style=padding-bottom:15px> <md-button ng-click=testDetailsBuildJs($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-details-function' | translate }} </md-button> </div> <md-input-container class=md-block> <label translate>tb.rulenode.alarm-type</label> <input ng-required=true name=alarmType ng-model=configuration.alarmType> <div ng-messages=alarmConfigForm.alarmType.$error> <div ng-message=required translate>tb.rulenode.alarm-type-required</div> </div> </md-input-container> </section> "},function(e,t){e.exports=" <section class=tb-alarm-config ng-form name=alarmConfigForm layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.alarm-details-builder</label> <tb-js-func ng-model=configuration.alarmDetailsBuildJs function-name=Details function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row style=padding-bottom:15px> <md-button ng-click=testDetailsBuildJs($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-details-function' | translate }} </md-button> </div> <section layout=column layout-gt-sm=row> <md-input-container flex class=md-block> <label translate>tb.rulenode.alarm-type</label> <input ng-required=true name=alarmType ng-model=configuration.alarmType> <div ng-messages=alarmConfigForm.alarmType.$error> <div ng-message=required translate>tb.rulenode.alarm-type-required</div> </div> </md-input-container> <md-input-container flex class=md-block> <label translate>tb.rulenode.alarm-severity</label> <md-select required name=severity ng-model=configuration.severity> <md-option ng-repeat=\"(severityKey, severity) in types.alarmSeverity\" ng-value=severityKey> {{ severity.name | translate}} </md-option> </md-select> <div ng-messages=alarmConfigForm.severity.$error> <div ng-message=required translate>tb.rulenode.alarm-severity-required</div> </div> </md-input-container> </section> <md-checkbox aria-label=\"{{ 'tb.rulenode.propagate' | translate }}\" ng-model=configuration.propagate>{{ 'tb.rulenode.propagate' | translate }} </md-checkbox> </section> "},function(e,t){e.exports=" <section class=tb-generator-config ng-form name=generatorConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.message-count</label> <input ng-required=true type=number step=1 name=messageCount ng-model=configuration.msgCount min=0> <div ng-messages=generatorConfigForm.messageCount.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.message-count-required</div> <div ng-message=min translate>tb.rulenode.min-message-count-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.period-seconds</label> <input ng-required=true type=number step=1 name=periodInSeconds ng-model=configuration.periodInSeconds min=1> <div ng-messages=generatorConfigForm.periodInSeconds.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.period-seconds-required</div> <div ng-message=min translate>tb.rulenode.min-period-seconds-message</div> </div> </md-input-container> <div layout=column> <label class=tb-small>{{ 'tb.rulenode.originator' | translate }}</label> <tb-entity-select the-form=generatorConfigForm tb-required=false ng-model=originator> </tb-entity-select> </div> <label translate class=\"tb-title no-padding\">tb.rulenode.generate</label> <tb-js-func ng-model=configuration.jsScript function-name=Generate function-args=\"{{ ['prevMsg', 'prevMetadata', 'prevMsgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-generator-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=' <section ng-form name=kafkaConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.topic-pattern</label> <input ng-required=true name=topicPattern ng-model=configuration.topicPattern> <div ng-messages=kafkaConfigForm.topicPattern.$error> <div ng-message=required translate>tb.rulenode.topic-pattern-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.bootstrap-servers</label> <input ng-required=true name=bootstrapServers ng-model=configuration.bootstrapServers> <div ng-messages=kafkaConfigForm.bootstrapServers.$error> <div ng-message=required translate>tb.rulenode.bootstrap-servers-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.retries</label> <input type=number step=1 name=retries ng-model=configuration.retries min=0> <div ng-messages=kafkaConfigForm.retries.$error> <div ng-message=min translate>tb.rulenode.min-retries-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.batch-size-bytes</label> <input type=number step=1 name=batchSize ng-model=configuration.batchSize min=0> <div ng-messages=kafkaConfigForm.batchSize.$error> <div ng-message=min translate>tb.rulenode.min-batch-size-bytes-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.linger-ms</label> <input type=number step=1 name=linger ng-model=configuration.linger min=0> <div ng-messages=kafkaConfigForm.linger.$error> <div ng-message=min translate>tb.rulenode.min-linger-ms-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.buffer-memory-bytes</label> <input type=number step=1 name=bufferMemory ng-model=configuration.bufferMemory min=0> <div ng-messages=kafkaConfigForm.bufferMemory.$error> <div ng-message=min translate>tb.rulenode.min-buffer-memory-bytes-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.acks</label> <md-select ng-model=configuration.acks ng-disabled=$root.loading> <md-option ng-repeat="ackValue in ackValues" ng-value=ackValue> {{ ackValue }} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.key-serializer</label> <input ng-required=true name=keySerializer ng-model=configuration.keySerializer> <div ng-messages=kafkaConfigForm.keySerializer.$error> <div ng-message=required translate>tb.rulenode.key-serializer-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.value-serializer</label> <input ng-required=true name=valueSerializer ng-model=configuration.valueSerializer> <div ng-messages=kafkaConfigForm.valueSerializer.$error> <div ng-message=required translate>tb.rulenode.value-serializer-required</div> </div> </md-input-container> <label translate class=tb-title>tb.rulenode.other-properties</label> <tb-kv-map-config ng-model=configuration.otherProperties ng-required=false key-text="\'tb.rulenode.key\'" key-required-text="\'tb.rulenode.key-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.to-string</label> <tb-js-func ng-model=configuration.jsScript function-name=ToString function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-to-string-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=' <section class=tb-mqtt-config ng-form name=mqttConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.topic-pattern</label> <input ng-required=true name=topicPattern ng-model=configuration.topicPattern> <div ng-messages=mqttConfigForm.topicPattern.$error> <div translate ng-message=required>tb.rulenode.topic-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.mqtt-topic-pattern-hint</div> </md-input-container> <div flex layout=column layout-gt-sm=row> <md-input-container flex=60 class=md-block> <label translate>tb.rulenode.host</label> <input ng-required=true name=host ng-model=configuration.host> <div ng-messages=mqttConfigForm.host.$error> <div translate ng-message=required>tb.rulenode.host-required</div> </div> </md-input-container> <md-input-container flex=40 class=md-block> <label translate>tb.rulenode.port</label> <input type=number step=1 min=1 max=65535 ng-required=true name=port ng-model=configuration.port> <div ng-messages=mqttConfigForm.port.$error> <div translate ng-message=required>tb.rulenode.port-required</div> <div translate ng-message=min>tb.rulenode.port-range</div> <div translate ng-message=max>tb.rulenode.port-range</div> </div> </md-input-container> <md-input-container flex=40 class=md-block> <label translate>tb.rulenode.connect-timeout</label> <input type=number step=1 min=1 max=200 ng-required=true name=connectTimeoutSec ng-model=configuration.connectTimeoutSec> <div ng-messages=mqttConfigForm.connectTimeoutSec.$error> <div translate ng-message=required>tb.rulenode.connect-timeout-required</div> <div translate ng-message=min>tb.rulenode.connect-timeout-range</div> <div translate ng-message=max>tb.rulenode.connect-timeout-range</div> </div> </md-input-container> </div> <md-input-container class=md-block> <label translate>tb.rulenode.client-id</label> <input name=clientId ng-model=configuration.clientId> </md-input-container> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.clean-session\' | translate }}" ng-model=configuration.cleanSession> {{ \'tb.rulenode.clean-session\' | translate }} </md-checkbox> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.enable-ssl\' | translate }}" ng-model=configuration.ssl> {{ \'tb.rulenode.enable-ssl\' | translate }} </md-checkbox> <md-expansion-panel-group class=tb-credentials-panel-group ng-class="{\'disabled\': $root.loading || readonly}" md-component-id=credentialsPanelGroup> <md-expansion-panel md-component-id=credentialsPanel> <md-expansion-panel-collapsed> <div class=tb-panel-title>{{ \'tb.rulenode.credentials\' | translate }}</div> <div class=tb-panel-prompt>{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}</div> <span flex></span> <md-expansion-panel-icon></md-expansion-panel-icon> </md-expansion-panel-collapsed> <md-expansion-panel-expanded> <md-expansion-panel-header ng-click="$mdExpansionPanel(\'credentialsPanel\').collapse()"> <div class=tb-panel-title>{{ \'tb.rulenode.credentials\' | translate }}</div> <div class=tb-panel-prompt>{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}</div> <span flex></span> <md-expansion-panel-icon></md-expansion-panel-icon> </md-expansion-panel-header> <md-expansion-panel-content> <div layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.credentials-type</label> <md-select ng-required=true name=credentialsType ng-model=configuration.credentials.type ng-disabled="$root.loading || readonly" ng-change=credentialsTypeChanged()> <md-option ng-repeat="(credentialsType, credentialsValue) in ruleNodeTypes.mqttCredentialTypes" ng-value=credentialsValue.value> {{credentialsValue.name | translate}} </md-option> </md-select> <div ng-messages=mqttConfigForm.credentialsType.$error> <div translate ng-message=required>tb.rulenode.credentials-type-required</div> </div> </md-input-container> <section flex layout=column ng-if="configuration.credentials.type == ruleNodeTypes.mqttCredentialTypes.basic.value"> <md-input-container class=md-block> <label translate>tb.rulenode.username</label> <input ng-required=true name=mqttUsername ng-model=configuration.credentials.username> <div ng-messages=mqttConfigForm.mqttUsername.$error> <div translate ng-message=required>tb.rulenode.username-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.password</label> <input type=password ng-required=true name=mqttPassword ng-model=configuration.credentials.password> <div ng-messages=mqttConfigForm.mqttPassword.$error> <div translate ng-message=required>tb.rulenode.password-required</div> </div> </md-input-container> </section> <section flex layout=column ng-if="configuration.credentials.type == ruleNodeTypes.mqttCredentialTypes[\'cert.PEM\'].value" class=dropdown-section> <div class=tb-container ng-class="configuration.credentials.caCertFileName ? \'ng-valid\' : \'ng-invalid\'"> <label class=tb-label translate>tb.rulenode.ca-cert</label> <div flow-init={singleFile:true} flow-file-added="certFileAdded($file, \'caCert\')" class=tb-file-select-container> <div class=tb-file-clear-container> <md-button ng-click="clearCertFile(\'caCert\')" class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'action.remove\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.remove\' | translate }}" class=material-icons>close</md-icon> </md-button> </div> <div class="alert tb-flow-drop" flow-drop> <label for=caCertSelect translate>tb.rulenode.drop-file</label> <input class=file-input flow-btn id=caCertSelect> </div> </div> </div> <div class=dropdown-messages> <div ng-if=!configuration.credentials.caCertFileName class=tb-error-message translate>tb.rulenode.no-file</div> <div ng-if=configuration.credentials.caCertFileName>{{configuration.credentials.caCertFileName}}</div> </div> <div class=tb-container ng-class="configuration.credentials.certFileName ? \'ng-valid\' : \'ng-invalid\'"> <label class=tb-label translate>tb.rulenode.cert</label> <div flow-init={singleFile:true} flow-file-added="certFileAdded($file, \'Cert\')" class=tb-file-select-container> <div class=tb-file-clear-container> <md-button ng-click="clearCertFile(\'Cert\')" class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'action.remove\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.remove\' | translate }}" class=material-icons>close</md-icon> </md-button> </div> <div class="alert tb-flow-drop" flow-drop> <label for=CertSelect translate>tb.rulenode.drop-file</label> <input class=file-input flow-btn id=CertSelect> </div> </div> </div> <div class=dropdown-messages> <div ng-if=!configuration.credentials.certFileName class=tb-error-message translate>tb.rulenode.no-file</div> <div ng-if=configuration.credentials.certFileName>{{configuration.credentials.certFileName}}</div> </div> <div class=tb-container ng-class="configuration.credentials.privateKeyFileName ? \'ng-valid\' : \'ng-invalid\'"> <label class=tb-label translate>tb.rulenode.private-key</label> <div flow-init={singleFile:true} flow-file-added="certFileAdded($file, \'privateKey\')" class=tb-file-select-container> <div class=tb-file-clear-container> <md-button ng-click="clearCertFile(\'privateKey\')" class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'action.remove\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.remove\' | translate }}" class=material-icons>close</md-icon> </md-button> </div> <div class="alert tb-flow-drop" flow-drop> <label for=privateKeySelect translate>tb.rulenode.drop-file</label> <input class=file-input flow-btn id=privateKeySelect> </div> </div> </div> <div class=dropdown-messages> <div ng-if=!configuration.credentials.privateKeyFileName class=tb-error-message translate>tb.rulenode.no-file</div> <div ng-if=configuration.credentials.privateKeyFileName>{{configuration.credentials.privateKeyFileName}}</div> </div> <md-input-container class=md-block> <label translate>tb.rulenode.private-key-password</label> <input type=password name=privateKeyPassword ng-model=configuration.credentials.password> </md-input-container> </section> </div> </md-expansion-panel-content> </md-expansion-panel-expanded> </md-expansion-panel> </md-expansion-panel-group> </section>'},function(e,t){e.exports=" <section ng-form name=msgDelayConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.period-seconds</label> <input ng-required=true type=number step=1 name=periodInSeconds ng-model=configuration.periodInSeconds min=0> <div ng-messages=msgDelayConfigForm.periodInSeconds.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.period-seconds-required</div> <div ng-message=min translate>tb.rulenode.min-period-0-seconds-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.max-pending-messages</label> <input ng-required=true type=number step=1 name=maxPendingMsgs ng-model=configuration.maxPendingMsgs min=1 max=100000> <div ng-messages=msgDelayConfigForm.maxPendingMsgs.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.max-pending-messages-required</div> <div ng-message=min translate>tb.rulenode.max-pending-messages-range</div> <div ng-message=max translate>tb.rulenode.max-pending-messages-range</div> </div> </md-input-container> </section> "},function(e,t){e.exports=' <section ng-form name=rabbitMqConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.exchange-name-pattern</label> <input name=exchangeNamePattern ng-model=configuration.exchangeNamePattern> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.routing-key-pattern</label> <input name=routingKeyPattern ng-model=configuration.routingKeyPattern> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.message-properties</label> <md-select ng-model=configuration.messageProperties ng-disabled="$root.loading || readonly"> <md-option ng-repeat="property in messageProperties" ng-value=property> {{ property }} </md-option> </md-select> </md-input-container> <div layout-gt-sm=row> <md-input-container class=md-block flex=100 flex-gt-sm=60> <label translate>tb.rulenode.host</label> <input ng-required=true name=host ng-model=configuration.host> <div ng-messages=rabbitMqConfigForm.host.$error> <div ng-message=required translate>tb.rulenode.host-required</div> </div> </md-input-container> <md-input-container class=md-block flex=100 flex-gt-sm=40> <label translate>tb.rulenode.port</label> <input ng-required=true type=number step=1 name=port ng-model=configuration.port min=0 max=65535> <div ng-messages=rabbitMqConfigForm.port.$error> <div ng-message=required translate>tb.rulenode.port-required</div> <div ng-message=min translate>tb.rulenode.port-range</div> <div ng-message=max translate>tb.rulenode.port-range</div> </div> </md-input-container> </div> <md-input-container class=md-block> <label translate>tb.rulenode.virtual-host</label> <input name=virtualHost ng-model=configuration.virtualHost> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.username</label> <input name=virtualHost ng-model=configuration.username> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.password</label> <input name=virtualHost type=password ng-model=configuration.password> </md-input-container> <md-input-container class=md-block> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.automatic-recovery\' | translate }}" ng-model=ruleNode.automaticRecoveryEnabled>{{ \'tb.rulenode.automatic-recovery\' | translate }} </md-checkbox> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.connection-timeout-ms</label> <input type=number step=1 name=connectionTimeout ng-model=configuration.connectionTimeout min=0> <div ng-messages=rabbitMqConfigForm.connectionTimeout.$error> <div ng-message=min translate>tb.rulenode.min-connection-timeout-ms-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.handshake-timeout-ms</label> <input type=number step=1 name=handshakeTimeout ng-model=configuration.handshakeTimeout min=0> <div ng-messages=rabbitMqConfigForm.handshakeTimeout.$error> <div ng-message=min translate>tb.rulenode.min-handshake-timeout-ms-message</div> </div> </md-input-container> <label translate class=tb-title>tb.rulenode.client-properties</label> <tb-kv-map-config ng-model=configuration.clientProperties ng-required=false key-text="\'tb.rulenode.key\'" key-required-text="\'tb.rulenode.key-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=' <section ng-form name=restApiCallConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.endpoint-url-pattern</label> <input ng-required=true name=endpointUrlPattern ng-model=configuration.restEndpointUrlPattern> <div ng-messages=restApiCallConfigForm.endpointUrlPattern.$error> <div ng-message=required translate>tb.rulenode.endpoint-url-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.endpoint-url-pattern-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.request-method</label> <md-select ng-model=configuration.requestMethod ng-disabled=$root.loading> <md-option ng-repeat="type in ruleNodeTypes.httpRequestType" ng-value=type> {{ type }} </md-option> </md-select> </md-input-container> <label translate class=tb-title>tb.rulenode.headers</label> <div class=tb-hint translate>tb.rulenode.headers-hint</div> <tb-kv-map-config ng-model=configuration.headers ng-required=false key-text="\'tb.rulenode.header\'" key-required-text="\'tb.rulenode.header-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=" <section ng-form name=rpcReplyConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.request-id-metadata-attribute</label> <input name=requestIdMetaDataAttribute ng-model=configuration.requestIdMetaDataAttribute> </md-input-container> </section> "},function(e,t){e.exports=" <section ng-form name=rpcRequestConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.timeout-sec</label> <input ng-required=true type=number step=1 name=timeoutInSeconds ng-model=configuration.timeoutInSeconds min=0> <div ng-messages=rpcRequestConfigForm.timeoutInSeconds.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.timeout-required</div> <div ng-message=min translate>tb.rulenode.min-timeout-message</div> </div> </md-input-container> </section> "},function(e,t){e.exports=' <section ng-form name=sendEmailConfigForm layout=column> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.use-system-smtp-settings\' | translate }}" ng-model=configuration.useSystemSmtpSettings> {{ \'tb.rulenode.use-system-smtp-settings\' | translate }} </md-checkbox> <section layout=column ng-if=!configuration.useSystemSmtpSettings> <md-input-container class=md-block> <label translate>tb.rulenode.smtp-protocol</label> <md-select ng-disabled="$root.loading || readonly" ng-model=configuration.smtpProtocol> <md-option ng-repeat="smtpProtocol in smtpProtocols" value={{smtpProtocol}}> {{smtpProtocol.toUpperCase()}} </md-option> </md-select> </md-input-container> <div layout-gt-sm=row> <md-input-container class=md-block flex=100 flex-gt-sm=60> <label translate>tb.rulenode.smtp-host</label> <input ng-required=true name=smtpHost ng-model=configuration.smtpHost> <div ng-messages=sendEmailConfigForm.smtpHost.$error> <div translate ng-message=required>tb.rulenode.smtp-host-required</div> </div> </md-input-container> <md-input-container class=md-block flex=100 flex-gt-sm=40> <label translate>tb.rulenode.smtp-port</label> <input type=number step=1 min=1 max=65535 ng-required=true name=port ng-model=configuration.smtpPort> <div ng-messages=sendEmailConfigForm.port.$error> <div translate ng-message=required>tb.rulenode.smtp-port-required</div> <div translate ng-message=min>tb.rulenode.smtp-port-range</div> <div translate ng-message=max>tb.rulenode.smtp-port-range</div> </div> </md-input-container> </div> <md-input-container class=md-block> <label translate>tb.rulenode.timeout-msec</label> <input type=number step=1 min=0 ng-required=true name=timeout ng-model=configuration.timeout> <div ng-messages=sendEmailConfigForm.timeout.$error> <div translate ng-message=required>tb.rulenode.timeout-required</div> <div translate ng-message=min>tb.rulenode.min-timeout-msec-message</div> </div> </md-input-container> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.enable-tls\' | translate }}" ng-model=configuration.enableTls>{{ \'tb.rulenode.enable-tls\' | translate }}</md-checkbox> <md-input-container class=md-block> <label translate>tb.rulenode.username</label> <input name=username placeholder="{{ \'tb.rulenode.enter-username\' | translate }}" ng-model=configuration.username> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.password</label> <input name=password placeholder="{{ \'tb.rulenode.enter-password\' | translate }}" type=password ng-model=configuration.password> </md-input-container> </section> </section> '},function(e,t){e.exports=" <section ng-form name=snsConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.topic-arn-pattern</label> <input ng-required=true name=topicArnPattern ng-model=configuration.topicArnPattern> <div ng-messages=snsConfigForm.topicArnPattern.$error> <div ng-message=required translate>tb.rulenode.topic-arn-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.topic-arn-pattern-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-access-key-id</label> <input ng-required=true name=accessKeyId ng-model=configuration.accessKeyId> <div ng-messages=snsConfigForm.accessKeyId.$error> <div ng-message=required translate>tb.rulenode.aws-access-key-id-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-secret-access-key</label> <input ng-required=true name=secretAccessKey ng-model=configuration.secretAccessKey> <div ng-messages=snsConfigForm.secretAccessKey.$error> <div ng-message=required translate>tb.rulenode.aws-secret-access-key-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-region</label> <input ng-required=true name=region ng-model=configuration.region> <div ng-messages=snsConfigForm.region.$error> <div ng-message=required translate>tb.rulenode.aws-region-required</div> </div> </md-input-container> </section> "},function(e,t){e.exports=' <section ng-form name=sqsConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.queue-type</label> <md-select ng-model=configuration.queueType ng-disabled="$root.loading || readonly"> <md-option ng-repeat="type in ruleNodeTypes.sqsQueueType" ng-value=type.value> {{ type.name | translate }} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.queue-url-pattern</label> <input ng-required=true name=queueUrlPattern ng-model=configuration.queueUrlPattern> <div ng-messages=sqsConfigForm.queueUrlPattern.$error> <div ng-message=required translate>tb.rulenode.queue-url-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.queue-url-pattern-hint</div> </md-input-container> <md-input-container class=md-block ng-if="configuration.queueType == ruleNodeTypes.sqsQueueType.STANDARD.value"> <label translate>tb.rulenode.delay-seconds</label> <input type=number step=1 name=delaySeconds ng-model=configuration.delaySeconds min=0 max=900> <div ng-messages=sqsConfigForm.delaySeconds.$error> <div ng-message=min translate>tb.rulenode.min-delay-seconds-message</div> <div ng-message=max translate>tb.rulenode.max-delay-seconds-message</div> </div> </md-input-container> <label translate class=tb-title>tb.rulenode.message-attributes</label> <div class=tb-hint translate>tb.rulenode.message-attributes-hint</div> <tb-kv-map-config ng-model=configuration.messageAttributes ng-required=false key-text="\'tb.rulenode.name\'" key-required-text="\'tb.rulenode.name-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> <md-input-container class=md-block> <label translate>tb.rulenode.aws-access-key-id</label> <input ng-required=true name=accessKeyId ng-model=configuration.accessKeyId> <div ng-messages=snsConfigForm.accessKeyId.$error> <div ng-message=required translate>tb.rulenode.aws-access-key-id-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-secret-access-key</label> <input ng-required=true name=secretAccessKey ng-model=configuration.secretAccessKey> <div ng-messages=snsConfigForm.secretAccessKey.$error> <div ng-message=required translate>tb.rulenode.aws-secret-access-key-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-region</label> <input ng-required=true name=region ng-model=configuration.region> <div ng-messages=snsConfigForm.region.$error> <div ng-message=required translate>tb.rulenode.aws-region-required</div> </div> </md-input-container> </section> '},function(e,t){e.exports=" <section ng-form name=timeseriesConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.default-ttl</label> <input ng-required=true type=number step=1 name=defaultTTL ng-model=configuration.defaultTTL min=0> <div ng-messages=timeseriesConfigForm.defaultTTL.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.default-ttl-required</div> <div ng-message=min translate>tb.rulenode.min-default-ttl-message</div> </div> </md-input-container> </section> "},function(e,t){e.exports=' <section layout=column> <div layout=row> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=query.direction> <md-option ng-repeat="direction in types.entitySearchDirection" ng-value=direction> {{ (\'relation.search-direction.\' + direction) | translate}} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.max-relation-level</label> <input name=maxRelationLevel type=number min=1 step=1 placeholder="{{ \'tb.rulenode.unlimited-level\' | translate }}" ng-model=query.maxLevel aria-label="{{ \'tb.rulenode.max-relation-level\' | translate }}"> </md-input-container> </div> <div class=md-caption style=color:rgba(0,0,0,.57) translate>relation.relation-type</div> <tb-relation-type-autocomplete flex hide-label ng-model=query.relationType tb-required=false> </tb-relation-type-autocomplete> <div class="md-caption tb-required" style=color:rgba(0,0,0,.57) translate>device.device-types</div> <tb-entity-subtype-list tb-required=true entity-type=types.entityType.device ng-model=query.deviceTypes> </tb-entity-subtype-list> </section> ';
2   -},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title tb-required\">tb.rulenode.attr-mapping</label> <md-checkbox aria-label=\"{{ 'tb.rulenode.latest-telemetry' | translate }}\" ng-model=configuration.telemetry>{{ 'tb.rulenode.latest-telemetry' | translate }} </md-checkbox> <tb-kv-map-config ng-model=configuration.attrMapping ng-required=true required-text=\"'tb.rulenode.attr-mapping-required'\" key-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry' : 'tb.rulenode.source-attribute'\" key-required-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry-required' : 'tb.rulenode.source-attribute-required'\" val-text=\"'tb.rulenode.target-attribute'\" val-required-text=\"'tb.rulenode.target-attribute-required'\"> </tb-kv-map-config> </section> "},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title tb-required">tb.rulenode.device-relations-query</label> <tb-device-relations-query-config style=padding-bottom:15px ng-model=configuration.deviceRelationsQuery> </tb-device-relations-query-config> <label translate class="tb-title no-padding">tb.rulenode.client-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.clientAttributeNames placeholder="{{\'tb.rulenode.client-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.shared-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.sharedAttributeNames placeholder="{{\'tb.rulenode.shared-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.server-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.serverAttributeNames placeholder="{{\'tb.rulenode.server-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.latest-timeseries</label> <md-chips ng-required=false readonly=readonly ng-model=configuration.latestTsKeyNames placeholder="{{\'tb.rulenode.latest-timeseries\' | translate}}" md-separator-keys=separatorKeys> </md-chips> </section> '},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding">tb.rulenode.client-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.clientAttributeNames placeholder="{{\'tb.rulenode.client-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.shared-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.sharedAttributeNames placeholder="{{\'tb.rulenode.shared-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.server-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.serverAttributeNames placeholder="{{\'tb.rulenode.server-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.latest-timeseries</label> <md-chips ng-required=false readonly=readonly ng-model=configuration.latestTsKeyNames placeholder="{{\'tb.rulenode.latest-timeseries\' | translate}}" md-separator-keys=separatorKeys> </md-chips> </section> '},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title tb-required">tb.rulenode.fields-mapping</label> <tb-kv-map-config ng-model=configuration.fieldsMapping ng-required=true required-text="\'tb.rulenode.fields-mapping-required\'" key-text="\'tb.rulenode.source-field\'" key-required-text="\'tb.rulenode.source-field-required\'" val-text="\'tb.rulenode.target-attribute\'" val-required-text="\'tb.rulenode.target-attribute-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title tb-required\">tb.rulenode.relations-query</label> <tb-relations-query-config style=padding-bottom:15px ng-model=configuration.relationsQuery> </tb-relations-query-config> <label translate class=\"tb-title tb-required\">tb.rulenode.attr-mapping</label> <md-checkbox aria-label=\"{{ 'tb.rulenode.latest-telemetry' | translate }}\" ng-model=configuration.telemetry>{{ 'tb.rulenode.latest-telemetry' | translate }} </md-checkbox> <tb-kv-map-config ng-model=configuration.attrMapping ng-required=true required-text=\"'tb.rulenode.attr-mapping-required'\" key-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry' : 'tb.rulenode.source-attribute'\" key-required-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry-required' : 'tb.rulenode.source-attribute-required'\" val-text=\"'tb.rulenode.target-attribute'\" val-required-text=\"'tb.rulenode.target-attribute-required'\"> </tb-kv-map-config> </section> "},22,function(e,t){e.exports=" <section ng-form name=checkRelationConfigForm> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=configuration.direction> <md-option ng-repeat=\"direction in types.entitySearchDirection\" ng-value=direction> {{ ('relation.search-direction.' + direction) | translate}} </md-option> </md-select> </md-input-container> <div layout=row class=tb-entity-select> <tb-entity-type-select style=min-width:100px the-form=checkRelationConfigForm tb-required=true ng-model=configuration.entityType> </tb-entity-type-select> <tb-entity-autocomplete flex ng-if=configuration.entityType the-form=checkRelationConfigForm tb-required=true entity-type=configuration.entityType ng-model=configuration.entityId> </tb-entity-autocomplete> </div> <tb-relation-type-autocomplete hide-label ng-model=configuration.relationType tb-required=true> </tb-relation-type-autocomplete> </section> "},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding" ng-class="{\'tb-required\': required}">tb.rulenode.message-types-filter</label> <md-chips id=message_type_chips ng-required=required readonly=readonly ng-model=messageTypes md-autocomplete-snap md-transform-chip=transformMessageTypeChip($chip) md-require-match=false> <md-autocomplete id=message_type md-no-cache=true md-selected-item=selectedMessageType md-search-text=messageTypeSearchText md-items="item in messageTypesSearch(messageTypeSearchText)" md-item-text=item.name md-min-length=0 placeholder="{{\'tb.rulenode.message-type\' | translate }}" md-menu-class=tb-message-type-autocomplete> <span md-highlight-text=messageTypeSearchText md-highlight-flags=^i>{{item}}</span> <md-not-found> <div class=tb-not-found> <div class=tb-no-entries ng-if="!messageTypeSearchText || !messageTypeSearchText.length"> <span translate>tb.rulenode.no-message-types-found</span> </div> <div ng-if="messageTypeSearchText && messageTypeSearchText.length"> <span translate translate-values=\'{ messageType: "{{messageTypeSearchText | truncate:true:6:&apos;...&apos;}}" }\'>tb.rulenode.no-message-type-matching</span> <span> <a translate ng-click="createMessageType($event, \'#message_type_chips\')">tb.rulenode.create-new-message-type</a> </span> </div> </div> </md-not-found> </md-autocomplete> <md-chip-template> <span>{{$chip.name}}</span> </md-chip-template> </md-chips> <div class=tb-error-messages ng-messages=ngModelCtrl.$error role=alert> <div translate ng-message=messageTypes class=tb-error-message>tb.rulenode.message-types-required</div> </div> </section>'},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding" class=required>tb.rulenode.originator-types-filter</label> <tb-entity-type-list flex ng-model=configuration.originatorTypes allowed-entity-types=allowedEntityTypes ignore-authority-filter=true tb-required=true> </tb-entity-type-list> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.filter</label> <tb-js-func ng-model=configuration.jsScript function-name=Filter function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-filter-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.switch</label> <tb-js-func ng-model=configuration.jsScript function-name=Switch function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-switch-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=' <section class=tb-kv-map-config layout=column> <div class=header flex layout=row> <span class=cell flex translate>{{ keyText }}</span> <span class=cell flex translate>{{ valText }}</span> <span ng-show=!disabled style=width:52px>&nbsp</span> </div> <div class=body> <div class=row ng-form name=kvForm flex layout=row layout-align="start center" ng-repeat="keyVal in kvList track by $index"> <md-input-container class="cell md-block" flex md-no-float> <input placeholder="{{ keyText | translate }}" ng-required=true name=key ng-model=keyVal.key> <div ng-messages=kvForm.key.$error> <div translate ng-message=required>{{keyRequiredText}}</div> </div> </md-input-container> <md-input-container class="cell md-block" flex md-no-float> <input placeholder="{{ valText | translate }}" ng-required=true name=value ng-model=keyVal.value> <div ng-messages=kvForm.value.$error> <div translate ng-message=required>{{valRequiredText}}</div> </div> </md-input-container> <md-button ng-show=!disabled ng-disabled=loading class="md-icon-button md-primary" ng-click=removeKeyVal($index) aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'tb.key-val.remove-entry\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.delete\' | translate }}" class=material-icons> close </md-icon> </md-button> </div> </div> <div class=tb-error-messages ng-messages=ngModelCtrl.$error role=alert> <div translate ng-message=kvMap class=tb-error-message>{{requiredText}}</div> </div> <div> <md-button ng-show=!disabled ng-disabled=loading class="md-primary md-raised" ng-click=addKeyVal() aria-label="{{ \'action.add\' | translate }}"> <md-tooltip md-direction=top> {{ \'tb.key-val.add-entry\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.add\' | translate }}" class=material-icons> add </md-icon> {{ \'action.add\' | translate }} </md-button> </div> </section> '},function(e,t){e.exports=" <section layout=column> <div layout=row> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=query.direction> <md-option ng-repeat=\"direction in types.entitySearchDirection\" ng-value=direction> {{ ('relation.search-direction.' + direction) | translate}} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.max-relation-level</label> <input name=maxRelationLevel type=number min=1 step=1 placeholder=\"{{ 'tb.rulenode.unlimited-level' | translate }}\" ng-model=query.maxLevel aria-label=\"{{ 'tb.rulenode.max-relation-level' | translate }}\"> </md-input-container> </div> <div class=md-caption style=padding-bottom:10px;color:rgba(0,0,0,.57) translate>relation.relation-filters</div> <tb-relation-filters ng-model=query.filters> </tb-relation-filters> </section> "},function(e,t){e.exports=' <section layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.originator-source</label> <md-select required ng-model=configuration.originatorSource> <md-option ng-repeat="source in ruleNodeTypes.originatorSource" ng-value=source.value> {{ source.name | translate}} </md-option> </md-select> </md-input-container> <section layout=column ng-if="configuration.originatorSource == ruleNodeTypes.originatorSource.RELATED.value"> <label translate class="tb-title tb-required">tb.rulenode.relations-query</label> <tb-relations-query-config style=padding-bottom:15px ng-model=configuration.relationsQuery> </tb-relations-query-config> </section> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.transform</label> <tb-js-func ng-model=configuration.jsScript function-name=Transform function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row style=padding-bottom:15px> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-transformer-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=" <section ng-form name=toEmailConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.from-template</label> <textarea ng-required=true name=fromTemplate ng-model=configuration.fromTemplate rows=2></textarea> <div ng-messages=toEmailConfigForm.fromTemplate.$error> <div ng-message=required translate>tb.rulenode.from-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.from-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.to-template</label> <textarea ng-required=true name=toTemplate ng-model=configuration.toTemplate rows=2></textarea> <div ng-messages=toEmailConfigForm.toTemplate.$error> <div ng-message=required translate>tb.rulenode.to-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.mail-address-list-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.cc-template</label> <textarea name=ccTemplate ng-model=configuration.ccTemplate rows=2></textarea> <div class=tb-hint translate>tb.rulenode.mail-address-list-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.bcc-template</label> <textarea name=ccTemplate ng-model=configuration.bccTemplate rows=2></textarea> <div class=tb-hint translate>tb.rulenode.mail-address-list-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.subject-template</label> <textarea ng-required=true name=subjectTemplate ng-model=configuration.subjectTemplate rows=2></textarea> <div ng-messages=toEmailConfigForm.subjectTemplate.$error> <div ng-message=required translate>tb.rulenode.subject-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.subject-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.body-template</label> <textarea ng-required=true name=bodyTemplate ng-model=configuration.bodyTemplate rows=6></textarea> <div ng-messages=toEmailConfigForm.bodyTemplate.$error> <div ng-message=required translate>tb.rulenode.body-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.body-template-hint</div> </md-input-container> </section> "},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,r,a,i){var l=o.default;r.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(r.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(5),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n,r){var a=function(a,i,l,s){var u=o.default;i.html(u),a.types=n,a.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(a.configuration)}),s.$render=function(){a.configuration=s.$viewValue},a.testDetailsBuildJs=function(e){var n=angular.copy(a.configuration.alarmDetailsBuildJs);r.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}a.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(6),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n,r){var a=function(a,i,l,s){var u=o.default;i.html(u),a.types=n,a.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(a.configuration)}),s.$render=function(){a.configuration=s.$viewValue},a.testDetailsBuildJs=function(e){var n=angular.copy(a.configuration.alarmDetailsBuildJs);r.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}a.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(7),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n,r){var a=function(a,i,l,s){var u=o.default;i.html(u),a.types=n,a.originator=null,a.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(a.configuration)}),s.$render=function(){a.configuration=s.$viewValue,a.configuration.originatorId&&a.configuration.originatorType?a.originator={id:a.configuration.originatorId,entityType:a.configuration.originatorType}:a.originator=null,a.$watch("originator",function(e,t){angular.equals(e,t)||(a.originator?(s.$viewValue.originatorId=a.originator.id,s.$viewValue.originatorType=a.originator.entityType):(s.$viewValue.originatorId=null,s.$viewValue.originatorType=null))},!0)},a.testScript=function(e){var n=angular.copy(a.configuration.jsScript);r.testNodeScript(e,n,"generate",t.instant("tb.rulenode.generator")+"","Generate",["prevMsg","prevMetadata","prevMsgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,s.$setDirty()})},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}a.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a,n(1);var i=n(8),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(54),i=r(a),o=n(38),l=r(o),s=n(41),u=r(s),d=n(40),c=r(d),m=n(39),g=r(m),p=n(44),f=r(p),b=n(49),v=r(b),y=n(50),q=r(y),h=n(48),$=r(h),k=n(43),T=r(k),w=n(52),x=r(w),C=n(53),M=r(C),_=n(47),S=r(_),N=n(45),V=r(N),j=n(51),P=r(j),F=n(46),E=r(F);t.default=angular.module("thingsboard.ruleChain.config.action",[]).directive("tbActionNodeTimeseriesConfig",i.default).directive("tbActionNodeAttributesConfig",l.default).directive("tbActionNodeGeneratorConfig",u.default).directive("tbActionNodeCreateAlarmConfig",c.default).directive("tbActionNodeClearAlarmConfig",g.default).directive("tbActionNodeLogConfig",f.default).directive("tbActionNodeRpcReplyConfig",v.default).directive("tbActionNodeRpcRequestConfig",q.default).directive("tbActionNodeRestApiCallConfig",$.default).directive("tbActionNodeKafkaConfig",T.default).directive("tbActionNodeSnsConfig",x.default).directive("tbActionNodeSqsConfig",M.default).directive("tbActionNodeRabbitMqConfig",S.default).directive("tbActionNodeMqttConfig",V.default).directive("tbActionNodeSendEmailConfig",P.default).directive("tbActionNodeMsgDelayConfig",E.default).name},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.ackValues=["all","-1","0","1"],t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(9),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var r=function(r,a,i,l){var s=o.default;a.html(s),r.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(r.configuration)}),l.$render=function(){r.configuration=l.$viewValue},r.testScript=function(e){var a=angular.copy(r.configuration.jsScript);n.testNodeScript(e,a,"string",t.instant("tb.rulenode.to-string")+"","ToString",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.jsScript=e,l.$setDirty()})},e(a.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}a.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(10),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var r=function(r,a,i,l){var s=o.default;a.html(s),r.$mdExpansionPanel=t,r.ruleNodeTypes=n,r.credentialsTypeChanged=function(){var e=r.configuration.credentials.type;r.configuration.credentials={},r.configuration.credentials.type=e,r.updateValidity()},r.certFileAdded=function(e,t){var n=new FileReader;n.onload=function(n){r.$apply(function(){if(n.target.result){l.$setDirty();var a=n.target.result;a&&a.length>0&&("caCert"==t&&(r.configuration.credentials.caCertFileName=e.name,r.configuration.credentials.caCert=a),"privateKey"==t&&(r.configuration.credentials.privateKeyFileName=e.name,r.configuration.credentials.privateKey=a),"Cert"==t&&(r.configuration.credentials.certFileName=e.name,r.configuration.credentials.cert=a)),r.updateValidity()}})},n.readAsText(e.file)},r.clearCertFile=function(e){l.$setDirty(),"caCert"==e&&(r.configuration.credentials.caCertFileName=null,r.configuration.credentials.caCert=null),"privateKey"==e&&(r.configuration.credentials.privateKeyFileName=null,r.configuration.credentials.privateKey=null),"Cert"==e&&(r.configuration.credentials.certFileName=null,r.configuration.credentials.cert=null),r.updateValidity()},r.updateValidity=function(){var e=!0,t=r.configuration.credentials;t.type==n.mqttCredentialTypes["cert.PEM"].value&&(t.caCert&&t.cert&&t.privateKey||(e=!1)),l.$setValidity("Certs",e)},r.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(r.configuration)}),l.$render=function(){r.configuration=l.$viewValue},e(a.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:r}}a.$inject=["$compile","$mdExpansionPanel","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a,n(2);var i=n(11),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(12),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"],t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(13),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,r,a,i){var l=o.default;r.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(r.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(14),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(15),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(16),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.smtpProtocols=["smtp","smtps"],t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(17),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(18),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,r,a,i){var l=o.default;r.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(r.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(19),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(20),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,r,a,i){var l=o.default;r.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(r.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(21),o=r(i)},function(e,t){"use strict";function n(e){var t=function(t,n,r,a){n.html("<div></div>"),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}n.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(22),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,r,a,i){var l=o.default;r.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(r.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(23),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(60),i=r(a),o=n(61),l=r(o),s=n(58),u=r(s),d=n(62),c=r(d),m=n(57),g=r(m),p=n(63),f=r(p);t.default=angular.module("thingsboard.ruleChain.config.enrichment",[]).directive("tbEnrichmentNodeOriginatorAttributesConfig",i.default).directive("tbEnrichmentNodeOriginatorFieldsConfig",l.default).directive("tbEnrichmentNodeDeviceAttributesConfig",u.default).directive("tbEnrichmentNodeRelatedAttributesConfig",c.default).directive("tbEnrichmentNodeCustomerAttributesConfig",g.default).directive("tbEnrichmentNodeTenantAttributesConfig",f.default).name},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,r,a,i){var l=o.default;r.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(r.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(24),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(25),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(26),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{
3   -value:!0}),t.default=a;var i=n(27),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,r,a,i){var l=o.default;r.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(r.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(28),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(68),i=r(a),o=n(66),l=r(o),s=n(69),u=r(s),d=n(64),c=r(d),m=n(67),g=r(m);t.default=angular.module("thingsboard.ruleChain.config.filter",[]).directive("tbFilterNodeScriptConfig",i.default).directive("tbFilterNodeMessageTypeConfig",l.default).directive("tbFilterNodeSwitchConfig",u.default).directive("tbFilterNodeCheckRelationConfig",c.default).directive("tbFilterNodeOriginatorTypeConfig",g.default).name},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var r=function(r,a,i,l){function s(){if(l.$viewValue){for(var e=[],t=0;t<r.messageTypes.length;t++)e.push(r.messageTypes[t].value);l.$viewValue.messageTypes=e,u()}}function u(){if(r.required){var e=!(!l.$viewValue.messageTypes||!l.$viewValue.messageTypes.length);l.$setValidity("messageTypes",e)}else l.$setValidity("messageTypes",!0)}var d=o.default;a.html(d),r.selectedMessageType=null,r.messageTypeSearchText=null,r.ngModelCtrl=l;var c=[];for(var m in n.messageType){var g={name:n.messageType[m].name,value:n.messageType[m].value};c.push(g)}r.transformMessageTypeChip=function(e){var n,r=t("filter")(c,{name:e},!0);return n=r&&r.length?angular.copy(r[0]):{name:e,value:e}},r.messageTypesSearch=function(e){var n=e?t("filter")(c,{name:e}):c;return n.map(function(e){return e.name})},r.createMessageType=function(e,t){var n=angular.element(t,a)[0].firstElementChild,r=angular.element(n),i=r.scope().$mdChipsCtrl.getChipBuffer();e.preventDefault(),e.stopPropagation(),r.scope().$mdChipsCtrl.appendChip(i.trim()),r.scope().$mdChipsCtrl.resetChipBuffer()},l.$render=function(){r.messageTypesWatch&&(r.messageTypesWatch(),r.messageTypesWatch=null);var e=l.$viewValue,t=[];if(e&&e.messageTypes)for(var a=0;a<e.messageTypes.length;a++){var i=e.messageTypes[a];n.messageType[i]?t.push(angular.copy(n.messageType[i])):t.push({name:i,value:i})}r.messageTypes=t,r.messageTypesWatch=r.$watch("messageTypes",function(e,t){angular.equals(e,t)||s()},!0)},e(a.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",readonly:"=ngReadonly"},link:r}}a.$inject=["$compile","$filter","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a,n(3);var i=n(29),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,r,a,i){var l=o.default;r.html(l),n.allowedEntityTypes=[t.entityType.device,t.entityType.asset,t.entityType.tenant,t.entityType.customer,t.entityType.user,t.entityType.dashboard,t.entityType.rulechain,t.entityType.rulenode],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(r.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(30),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var r=function(r,a,i,l){var s=o.default;a.html(s),r.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(r.configuration)}),l.$render=function(){r.configuration=l.$viewValue},r.testScript=function(e){var a=angular.copy(r.configuration.jsScript);n.testNodeScript(e,a,"filter",t.instant("tb.rulenode.filter")+"","Filter",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.jsScript=e,l.$setDirty()})},e(a.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}a.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(31),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var r=function(r,a,i,l){var s=o.default;a.html(s),r.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(r.configuration)}),l.$render=function(){r.configuration=l.$viewValue},r.testScript=function(e){var a=angular.copy(r.configuration.jsScript);n.testNodeScript(e,a,"switch",t.instant("tb.rulenode.switch")+"","Switch",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.jsScript=e,l.$setDirty()})},e(a.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}a.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(32),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){function i(e){e>-1&&t.kvList.splice(e,1)}function l(){t.kvList||(t.kvList=[]),t.kvList.push({key:"",value:""})}function s(){var e={};t.kvList.forEach(function(t){t.key&&(e[t.key]=t.value)}),a.$setViewValue(e),u()}function u(){var e=!0;t.required&&!t.kvList.length&&(e=!1),a.$setValidity("kvMap",e)}var d=o.default;n.html(d),t.ngModelCtrl=a,t.removeKeyVal=i,t.addKeyVal=l,t.kvList=[],t.$watch("query",function(e,n){angular.equals(e,n)||a.$setViewValue(t.query)}),a.$render=function(){if(a.$viewValue){var e=a.$viewValue;t.kvList.length=0;for(var n in e)t.kvList.push({key:n,value:e[n]})}t.$watch("kvList",function(e,t){angular.equals(e,t)||s()},!0),u()},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",disabled:"=ngDisabled",requiredText:"=",keyText:"=",keyRequiredText:"=",valText:"=",valRequiredText:"="},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(33),o=r(i);n(4)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,r,a,i){var l=o.default;r.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(r.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(34),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,r,a,i){var l=o.default;r.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(r.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(35),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(72),i=r(a),o=n(74),l=r(o),s=n(75),u=r(s);t.default=angular.module("thingsboard.ruleChain.config.transform",[]).directive("tbTransformationNodeChangeOriginatorConfig",i.default).directive("tbTransformationNodeScriptConfig",l.default).directive("tbTransformationNodeToEmailConfig",u.default).name},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var r=function(r,a,i,l){var s=o.default;a.html(s),r.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(r.configuration)}),l.$render=function(){r.configuration=l.$viewValue},r.testScript=function(e){var a=angular.copy(r.configuration.jsScript);n.testNodeScript(e,a,"update",t.instant("tb.rulenode.transformer")+"","Transform",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.jsScript=e,l.$setDirty()})},e(a.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}a.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(36),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(37),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(79),i=r(a),o=n(65),l=r(o),s=n(59),u=r(s),d=n(73),c=r(d),m=n(42),g=r(m),p=n(56),f=r(p),b=n(71),v=r(b),y=n(55),q=r(y),h=n(70),$=r(h),k=n(78),T=r(k);t.default=angular.module("thingsboard.ruleChain.config",[i.default,l.default,u.default,c.default,g.default]).directive("tbNodeEmptyConfig",f.default).directive("tbRelationsQueryConfig",v.default).directive("tbDeviceRelationsQueryConfig",q.default).directive("tbKvMapConfig",$.default).config(T.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one!","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","latest-timeseries":"Latest timeseries","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required",propagate:"Propagate",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From Template","from-template-required":"From Template is required","from-template-hint":"From address template, use <code>${metaKeyName}</code> to substitute variables from metadata","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":"Comma separated address list, use <code>${metaKeyName}</code> to substitute variables from metadata","cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","subject-template-hint":"Mail subject template, use <code>${metaKeyName}</code> to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use <code>${metaKeyName}</code> to substitute variables from metadata","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","endpoint-url-pattern-hint":"HTTP URL address pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","request-method":"Request method",headers:"Headers","headers-hint":"Use <code>${metaKeyName}</code> in header/value fields to substitute variables from metadata",header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required","mqtt-topic-pattern-hint":"MQTT topic pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","topic-arn-pattern-hint":"Topic ARN pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","queue-url-pattern-hint":"Queue URL pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","message-attributes":"Message attributes","message-attributes-hint":"Use <code>${metaKeyName}</code> in name/value fields to substitute variables from metadata","connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"CA certificate file *","private-key":"Private key file *",cert:"Certificate file *","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter"},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}};e.translations("en_US",t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){(0,o.default)(e)}a.$inject=["$translateProvider"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(77),o=r(i)},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=angular.module("thingsboard.ruleChain.config.types",[]).constant("ruleNodeTypes",{originatorSource:{CUSTOMER:{name:"tb.rulenode.originator-customer",value:"CUSTOMER"},TENANT:{name:"tb.rulenode.originator-tenant",value:"TENANT"},RELATED:{name:"tb.rulenode.originator-related",value:"RELATED"}},httpRequestType:["GET","POST","PUT","DELETE"],sqsQueueType:{STANDARD:{name:"tb.rulenode.sqs-queue-standard",value:"STANDARD"},FIFO:{name:"tb.rulenode.sqs-queue-fifo",value:"FIFO"}},mqttCredentialTypes:{anonymous:{value:"anonymous",name:"tb.rulenode.credentials-anonymous"},basic:{value:"basic",name:"tb.rulenode.credentials-basic"},"cert.PEM":{value:"cert.PEM",name:"tb.rulenode.credentials-pem"}}}).name}]));
  1 +!function(e){function t(a){if(n[a])return n[a].exports;var r=n[a]={exports:{},id:a,loaded:!1};return e[a].call(r.exports,r,r.exports,t),r.loaded=!0,r.exports}var n={};return t.m=e,t.c=n,t.p="/static/",t(0)}(function(e){for(var t in e)if(Object.prototype.hasOwnProperty.call(e,t))switch(typeof e[t]){case"function":break;case"object":e[t]=function(t){var n=t.slice(1),a=e[t[0]];return function(e,t,r){a.apply(this,[e,t,r].concat(n))}}(e[t]);break;default:e[t]=e[e[t]]}return e}([function(e,t,n){e.exports=n(79)},function(e,t){},1,1,1,1,function(e,t){e.exports=' <section ng-form name=attributesConfigForm layout=column> <md-input-container class=md-block> <label translate>attribute.attributes-scope</label> <md-select ng-model=configuration.scope ng-disabled=$root.loading> <md-option ng-repeat="scope in types.attributesScope" ng-value=scope.value> {{scope.name | translate}} </md-option> </md-select> </md-input-container> </section> '},function(e,t){e.exports=" <section class=tb-alarm-config ng-form name=alarmConfigForm layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.alarm-details-builder</label> <tb-js-func ng-model=configuration.alarmDetailsBuildJs function-name=Details function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row style=padding-bottom:15px> <md-button ng-click=testDetailsBuildJs($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-details-function' | translate }} </md-button> </div> <md-input-container class=md-block> <label translate>tb.rulenode.alarm-type</label> <input ng-required=true name=alarmType ng-model=configuration.alarmType> <div ng-messages=alarmConfigForm.alarmType.$error> <div ng-message=required translate>tb.rulenode.alarm-type-required</div> </div> </md-input-container> </section> "},function(e,t){e.exports=" <section class=tb-alarm-config ng-form name=alarmConfigForm layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.alarm-details-builder</label> <tb-js-func ng-model=configuration.alarmDetailsBuildJs function-name=Details function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row style=padding-bottom:15px> <md-button ng-click=testDetailsBuildJs($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-details-function' | translate }} </md-button> </div> <section layout=column layout-gt-sm=row> <md-input-container flex class=md-block> <label translate>tb.rulenode.alarm-type</label> <input ng-required=true name=alarmType ng-model=configuration.alarmType> <div ng-messages=alarmConfigForm.alarmType.$error> <div ng-message=required translate>tb.rulenode.alarm-type-required</div> </div> </md-input-container> <md-input-container flex class=md-block> <label translate>tb.rulenode.alarm-severity</label> <md-select required name=severity ng-model=configuration.severity> <md-option ng-repeat=\"(severityKey, severity) in types.alarmSeverity\" ng-value=severityKey> {{ severity.name | translate}} </md-option> </md-select> <div ng-messages=alarmConfigForm.severity.$error> <div ng-message=required translate>tb.rulenode.alarm-severity-required</div> </div> </md-input-container> </section> <md-checkbox aria-label=\"{{ 'tb.rulenode.propagate' | translate }}\" ng-model=configuration.propagate>{{ 'tb.rulenode.propagate' | translate }} </md-checkbox> </section> "},function(e,t){e.exports=" <section class=tb-generator-config ng-form name=generatorConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.message-count</label> <input ng-required=true type=number step=1 name=messageCount ng-model=configuration.msgCount min=0> <div ng-messages=generatorConfigForm.messageCount.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.message-count-required</div> <div ng-message=min translate>tb.rulenode.min-message-count-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.period-seconds</label> <input ng-required=true type=number step=1 name=periodInSeconds ng-model=configuration.periodInSeconds min=1> <div ng-messages=generatorConfigForm.periodInSeconds.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.period-seconds-required</div> <div ng-message=min translate>tb.rulenode.min-period-seconds-message</div> </div> </md-input-container> <div layout=column> <label class=tb-small>{{ 'tb.rulenode.originator' | translate }}</label> <tb-entity-select the-form=generatorConfigForm tb-required=false ng-model=originator> </tb-entity-select> </div> <label translate class=\"tb-title no-padding\">tb.rulenode.generate</label> <tb-js-func ng-model=configuration.jsScript function-name=Generate function-args=\"{{ ['prevMsg', 'prevMetadata', 'prevMsgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-generator-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=' <section ng-form name=kafkaConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.topic-pattern</label> <input ng-required=true name=topicPattern ng-model=configuration.topicPattern> <div ng-messages=kafkaConfigForm.topicPattern.$error> <div ng-message=required translate>tb.rulenode.topic-pattern-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.bootstrap-servers</label> <input ng-required=true name=bootstrapServers ng-model=configuration.bootstrapServers> <div ng-messages=kafkaConfigForm.bootstrapServers.$error> <div ng-message=required translate>tb.rulenode.bootstrap-servers-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.retries</label> <input type=number step=1 name=retries ng-model=configuration.retries min=0> <div ng-messages=kafkaConfigForm.retries.$error> <div ng-message=min translate>tb.rulenode.min-retries-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.batch-size-bytes</label> <input type=number step=1 name=batchSize ng-model=configuration.batchSize min=0> <div ng-messages=kafkaConfigForm.batchSize.$error> <div ng-message=min translate>tb.rulenode.min-batch-size-bytes-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.linger-ms</label> <input type=number step=1 name=linger ng-model=configuration.linger min=0> <div ng-messages=kafkaConfigForm.linger.$error> <div ng-message=min translate>tb.rulenode.min-linger-ms-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.buffer-memory-bytes</label> <input type=number step=1 name=bufferMemory ng-model=configuration.bufferMemory min=0> <div ng-messages=kafkaConfigForm.bufferMemory.$error> <div ng-message=min translate>tb.rulenode.min-buffer-memory-bytes-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.acks</label> <md-select ng-model=configuration.acks ng-disabled=$root.loading> <md-option ng-repeat="ackValue in ackValues" ng-value=ackValue> {{ ackValue }} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.key-serializer</label> <input ng-required=true name=keySerializer ng-model=configuration.keySerializer> <div ng-messages=kafkaConfigForm.keySerializer.$error> <div ng-message=required translate>tb.rulenode.key-serializer-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.value-serializer</label> <input ng-required=true name=valueSerializer ng-model=configuration.valueSerializer> <div ng-messages=kafkaConfigForm.valueSerializer.$error> <div ng-message=required translate>tb.rulenode.value-serializer-required</div> </div> </md-input-container> <label translate class=tb-title>tb.rulenode.other-properties</label> <tb-kv-map-config ng-model=configuration.otherProperties ng-required=false key-text="\'tb.rulenode.key\'" key-required-text="\'tb.rulenode.key-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.to-string</label> <tb-js-func ng-model=configuration.jsScript function-name=ToString function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-to-string-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=' <section class=tb-mqtt-config ng-form name=mqttConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.topic-pattern</label> <input ng-required=true name=topicPattern ng-model=configuration.topicPattern> <div ng-messages=mqttConfigForm.topicPattern.$error> <div translate ng-message=required>tb.rulenode.topic-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.mqtt-topic-pattern-hint</div> </md-input-container> <div flex layout=column layout-gt-sm=row> <md-input-container flex=60 class=md-block> <label translate>tb.rulenode.host</label> <input ng-required=true name=host ng-model=configuration.host> <div ng-messages=mqttConfigForm.host.$error> <div translate ng-message=required>tb.rulenode.host-required</div> </div> </md-input-container> <md-input-container flex=40 class=md-block> <label translate>tb.rulenode.port</label> <input type=number step=1 min=1 max=65535 ng-required=true name=port ng-model=configuration.port> <div ng-messages=mqttConfigForm.port.$error> <div translate ng-message=required>tb.rulenode.port-required</div> <div translate ng-message=min>tb.rulenode.port-range</div> <div translate ng-message=max>tb.rulenode.port-range</div> </div> </md-input-container> <md-input-container flex=40 class=md-block> <label translate>tb.rulenode.connect-timeout</label> <input type=number step=1 min=1 max=200 ng-required=true name=connectTimeoutSec ng-model=configuration.connectTimeoutSec> <div ng-messages=mqttConfigForm.connectTimeoutSec.$error> <div translate ng-message=required>tb.rulenode.connect-timeout-required</div> <div translate ng-message=min>tb.rulenode.connect-timeout-range</div> <div translate ng-message=max>tb.rulenode.connect-timeout-range</div> </div> </md-input-container> </div> <md-input-container class=md-block> <label translate>tb.rulenode.client-id</label> <input name=clientId ng-model=configuration.clientId> </md-input-container> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.clean-session\' | translate }}" ng-model=configuration.cleanSession> {{ \'tb.rulenode.clean-session\' | translate }} </md-checkbox> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.enable-ssl\' | translate }}" ng-model=configuration.ssl> {{ \'tb.rulenode.enable-ssl\' | translate }} </md-checkbox> <md-expansion-panel-group class=tb-credentials-panel-group ng-class="{\'disabled\': $root.loading || readonly}" md-component-id=credentialsPanelGroup> <md-expansion-panel md-component-id=credentialsPanel> <md-expansion-panel-collapsed> <div class=tb-panel-title>{{ \'tb.rulenode.credentials\' | translate }}</div> <div class=tb-panel-prompt>{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}</div> <span flex></span> <md-expansion-panel-icon></md-expansion-panel-icon> </md-expansion-panel-collapsed> <md-expansion-panel-expanded> <md-expansion-panel-header ng-click="$mdExpansionPanel(\'credentialsPanel\').collapse()"> <div class=tb-panel-title>{{ \'tb.rulenode.credentials\' | translate }}</div> <div class=tb-panel-prompt>{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}</div> <span flex></span> <md-expansion-panel-icon></md-expansion-panel-icon> </md-expansion-panel-header> <md-expansion-panel-content> <div layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.credentials-type</label> <md-select ng-required=true name=credentialsType ng-model=configuration.credentials.type ng-disabled="$root.loading || readonly" ng-change=credentialsTypeChanged()> <md-option ng-repeat="(credentialsType, credentialsValue) in ruleNodeTypes.mqttCredentialTypes" ng-value=credentialsValue.value> {{credentialsValue.name | translate}} </md-option> </md-select> <div ng-messages=mqttConfigForm.credentialsType.$error> <div translate ng-message=required>tb.rulenode.credentials-type-required</div> </div> </md-input-container> <section flex layout=column ng-if="configuration.credentials.type == ruleNodeTypes.mqttCredentialTypes.basic.value"> <md-input-container class=md-block> <label translate>tb.rulenode.username</label> <input ng-required=true name=mqttUsername ng-model=configuration.credentials.username> <div ng-messages=mqttConfigForm.mqttUsername.$error> <div translate ng-message=required>tb.rulenode.username-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.password</label> <input type=password ng-required=true name=mqttPassword ng-model=configuration.credentials.password> <div ng-messages=mqttConfigForm.mqttPassword.$error> <div translate ng-message=required>tb.rulenode.password-required</div> </div> </md-input-container> </section> <section flex layout=column ng-if="configuration.credentials.type == ruleNodeTypes.mqttCredentialTypes[\'cert.PEM\'].value" class=dropdown-section> <div class=tb-container ng-class="configuration.credentials.caCertFileName ? \'ng-valid\' : \'ng-invalid\'"> <label class=tb-label translate>tb.rulenode.ca-cert</label> <div flow-init={singleFile:true} flow-file-added="certFileAdded($file, \'caCert\')" class=tb-file-select-container> <div class=tb-file-clear-container> <md-button ng-click="clearCertFile(\'caCert\')" class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'action.remove\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.remove\' | translate }}" class=material-icons>close</md-icon> </md-button> </div> <div class="alert tb-flow-drop" flow-drop> <label for=caCertSelect translate>tb.rulenode.drop-file</label> <input class=file-input flow-btn id=caCertSelect> </div> </div> </div> <div class=dropdown-messages> <div ng-if=!configuration.credentials.caCertFileName class=tb-error-message translate>tb.rulenode.no-file</div> <div ng-if=configuration.credentials.caCertFileName>{{configuration.credentials.caCertFileName}}</div> </div> <div class=tb-container ng-class="configuration.credentials.certFileName ? \'ng-valid\' : \'ng-invalid\'"> <label class=tb-label translate>tb.rulenode.cert</label> <div flow-init={singleFile:true} flow-file-added="certFileAdded($file, \'Cert\')" class=tb-file-select-container> <div class=tb-file-clear-container> <md-button ng-click="clearCertFile(\'Cert\')" class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'action.remove\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.remove\' | translate }}" class=material-icons>close</md-icon> </md-button> </div> <div class="alert tb-flow-drop" flow-drop> <label for=CertSelect translate>tb.rulenode.drop-file</label> <input class=file-input flow-btn id=CertSelect> </div> </div> </div> <div class=dropdown-messages> <div ng-if=!configuration.credentials.certFileName class=tb-error-message translate>tb.rulenode.no-file</div> <div ng-if=configuration.credentials.certFileName>{{configuration.credentials.certFileName}}</div> </div> <div class=tb-container ng-class="configuration.credentials.privateKeyFileName ? \'ng-valid\' : \'ng-invalid\'"> <label class=tb-label translate>tb.rulenode.private-key</label> <div flow-init={singleFile:true} flow-file-added="certFileAdded($file, \'privateKey\')" class=tb-file-select-container> <div class=tb-file-clear-container> <md-button ng-click="clearCertFile(\'privateKey\')" class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'action.remove\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.remove\' | translate }}" class=material-icons>close</md-icon> </md-button> </div> <div class="alert tb-flow-drop" flow-drop> <label for=privateKeySelect translate>tb.rulenode.drop-file</label> <input class=file-input flow-btn id=privateKeySelect> </div> </div> </div> <div class=dropdown-messages> <div ng-if=!configuration.credentials.privateKeyFileName class=tb-error-message translate>tb.rulenode.no-file</div> <div ng-if=configuration.credentials.privateKeyFileName>{{configuration.credentials.privateKeyFileName}}</div> </div> <md-input-container class=md-block> <label translate>tb.rulenode.private-key-password</label> <input type=password name=privateKeyPassword ng-model=configuration.credentials.password> </md-input-container> </section> </div> </md-expansion-panel-content> </md-expansion-panel-expanded> </md-expansion-panel> </md-expansion-panel-group> </section>'},function(e,t){e.exports=" <section ng-form name=msgDelayConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.period-seconds</label> <input ng-required=true type=number step=1 name=periodInSeconds ng-model=configuration.periodInSeconds min=0> <div ng-messages=msgDelayConfigForm.periodInSeconds.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.period-seconds-required</div> <div ng-message=min translate>tb.rulenode.min-period-0-seconds-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.max-pending-messages</label> <input ng-required=true type=number step=1 name=maxPendingMsgs ng-model=configuration.maxPendingMsgs min=1 max=100000> <div ng-messages=msgDelayConfigForm.maxPendingMsgs.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.max-pending-messages-required</div> <div ng-message=min translate>tb.rulenode.max-pending-messages-range</div> <div ng-message=max translate>tb.rulenode.max-pending-messages-range</div> </div> </md-input-container> </section> "},function(e,t){e.exports=' <section ng-form name=rabbitMqConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.exchange-name-pattern</label> <input name=exchangeNamePattern ng-model=configuration.exchangeNamePattern> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.routing-key-pattern</label> <input name=routingKeyPattern ng-model=configuration.routingKeyPattern> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.message-properties</label> <md-select ng-model=configuration.messageProperties ng-disabled="$root.loading || readonly"> <md-option ng-repeat="property in messageProperties" ng-value=property> {{ property }} </md-option> </md-select> </md-input-container> <div layout-gt-sm=row> <md-input-container class=md-block flex=100 flex-gt-sm=60> <label translate>tb.rulenode.host</label> <input ng-required=true name=host ng-model=configuration.host> <div ng-messages=rabbitMqConfigForm.host.$error> <div ng-message=required translate>tb.rulenode.host-required</div> </div> </md-input-container> <md-input-container class=md-block flex=100 flex-gt-sm=40> <label translate>tb.rulenode.port</label> <input ng-required=true type=number step=1 name=port ng-model=configuration.port min=0 max=65535> <div ng-messages=rabbitMqConfigForm.port.$error> <div ng-message=required translate>tb.rulenode.port-required</div> <div ng-message=min translate>tb.rulenode.port-range</div> <div ng-message=max translate>tb.rulenode.port-range</div> </div> </md-input-container> </div> <md-input-container class=md-block> <label translate>tb.rulenode.virtual-host</label> <input name=virtualHost ng-model=configuration.virtualHost> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.username</label> <input name=virtualHost ng-model=configuration.username> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.password</label> <input name=virtualHost type=password ng-model=configuration.password> </md-input-container> <md-input-container class=md-block> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.automatic-recovery\' | translate }}" ng-model=ruleNode.automaticRecoveryEnabled>{{ \'tb.rulenode.automatic-recovery\' | translate }} </md-checkbox> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.connection-timeout-ms</label> <input type=number step=1 name=connectionTimeout ng-model=configuration.connectionTimeout min=0> <div ng-messages=rabbitMqConfigForm.connectionTimeout.$error> <div ng-message=min translate>tb.rulenode.min-connection-timeout-ms-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.handshake-timeout-ms</label> <input type=number step=1 name=handshakeTimeout ng-model=configuration.handshakeTimeout min=0> <div ng-messages=rabbitMqConfigForm.handshakeTimeout.$error> <div ng-message=min translate>tb.rulenode.min-handshake-timeout-ms-message</div> </div> </md-input-container> <label translate class=tb-title>tb.rulenode.client-properties</label> <tb-kv-map-config ng-model=configuration.clientProperties ng-required=false key-text="\'tb.rulenode.key\'" key-required-text="\'tb.rulenode.key-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=' <section ng-form name=restApiCallConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.endpoint-url-pattern</label> <input ng-required=true name=endpointUrlPattern ng-model=configuration.restEndpointUrlPattern> <div ng-messages=restApiCallConfigForm.endpointUrlPattern.$error> <div ng-message=required translate>tb.rulenode.endpoint-url-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.endpoint-url-pattern-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.request-method</label> <md-select ng-model=configuration.requestMethod ng-disabled=$root.loading> <md-option ng-repeat="type in ruleNodeTypes.httpRequestType" ng-value=type> {{ type }} </md-option> </md-select> </md-input-container> <label translate class=tb-title>tb.rulenode.headers</label> <div class=tb-hint translate>tb.rulenode.headers-hint</div> <tb-kv-map-config ng-model=configuration.headers ng-required=false key-text="\'tb.rulenode.header\'" key-required-text="\'tb.rulenode.header-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=" <section ng-form name=rpcReplyConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.request-id-metadata-attribute</label> <input name=requestIdMetaDataAttribute ng-model=configuration.requestIdMetaDataAttribute> </md-input-container> </section> "},function(e,t){e.exports=" <section ng-form name=rpcRequestConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.timeout-sec</label> <input ng-required=true type=number step=1 name=timeoutInSeconds ng-model=configuration.timeoutInSeconds min=0> <div ng-messages=rpcRequestConfigForm.timeoutInSeconds.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.timeout-required</div> <div ng-message=min translate>tb.rulenode.min-timeout-message</div> </div> </md-input-container> </section> "},function(e,t){e.exports=' <section ng-form name=sendEmailConfigForm layout=column> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.use-system-smtp-settings\' | translate }}" ng-model=configuration.useSystemSmtpSettings> {{ \'tb.rulenode.use-system-smtp-settings\' | translate }} </md-checkbox> <section layout=column ng-if=!configuration.useSystemSmtpSettings> <md-input-container class=md-block> <label translate>tb.rulenode.smtp-protocol</label> <md-select ng-disabled="$root.loading || readonly" ng-model=configuration.smtpProtocol> <md-option ng-repeat="smtpProtocol in smtpProtocols" value={{smtpProtocol}}> {{smtpProtocol.toUpperCase()}} </md-option> </md-select> </md-input-container> <div layout-gt-sm=row> <md-input-container class=md-block flex=100 flex-gt-sm=60> <label translate>tb.rulenode.smtp-host</label> <input ng-required=true name=smtpHost ng-model=configuration.smtpHost> <div ng-messages=sendEmailConfigForm.smtpHost.$error> <div translate ng-message=required>tb.rulenode.smtp-host-required</div> </div> </md-input-container> <md-input-container class=md-block flex=100 flex-gt-sm=40> <label translate>tb.rulenode.smtp-port</label> <input type=number step=1 min=1 max=65535 ng-required=true name=port ng-model=configuration.smtpPort> <div ng-messages=sendEmailConfigForm.port.$error> <div translate ng-message=required>tb.rulenode.smtp-port-required</div> <div translate ng-message=min>tb.rulenode.smtp-port-range</div> <div translate ng-message=max>tb.rulenode.smtp-port-range</div> </div> </md-input-container> </div> <md-input-container class=md-block> <label translate>tb.rulenode.timeout-msec</label> <input type=number step=1 min=0 ng-required=true name=timeout ng-model=configuration.timeout> <div ng-messages=sendEmailConfigForm.timeout.$error> <div translate ng-message=required>tb.rulenode.timeout-required</div> <div translate ng-message=min>tb.rulenode.min-timeout-msec-message</div> </div> </md-input-container> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.enable-tls\' | translate }}" ng-model=configuration.enableTls>{{ \'tb.rulenode.enable-tls\' | translate }}</md-checkbox> <md-input-container class=md-block> <label translate>tb.rulenode.username</label> <input name=username placeholder="{{ \'tb.rulenode.enter-username\' | translate }}" ng-model=configuration.username> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.password</label> <input name=password placeholder="{{ \'tb.rulenode.enter-password\' | translate }}" type=password ng-model=configuration.password> </md-input-container> </section> </section> '},function(e,t){e.exports=" <section ng-form name=snsConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.topic-arn-pattern</label> <input ng-required=true name=topicArnPattern ng-model=configuration.topicArnPattern> <div ng-messages=snsConfigForm.topicArnPattern.$error> <div ng-message=required translate>tb.rulenode.topic-arn-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.topic-arn-pattern-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-access-key-id</label> <input ng-required=true name=accessKeyId ng-model=configuration.accessKeyId> <div ng-messages=snsConfigForm.accessKeyId.$error> <div ng-message=required translate>tb.rulenode.aws-access-key-id-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-secret-access-key</label> <input ng-required=true name=secretAccessKey ng-model=configuration.secretAccessKey> <div ng-messages=snsConfigForm.secretAccessKey.$error> <div ng-message=required translate>tb.rulenode.aws-secret-access-key-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-region</label> <input ng-required=true name=region ng-model=configuration.region> <div ng-messages=snsConfigForm.region.$error> <div ng-message=required translate>tb.rulenode.aws-region-required</div> </div> </md-input-container> </section> "},function(e,t){e.exports=' <section ng-form name=sqsConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.queue-type</label> <md-select ng-model=configuration.queueType ng-disabled="$root.loading || readonly"> <md-option ng-repeat="type in ruleNodeTypes.sqsQueueType" ng-value=type.value> {{ type.name | translate }} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.queue-url-pattern</label> <input ng-required=true name=queueUrlPattern ng-model=configuration.queueUrlPattern> <div ng-messages=sqsConfigForm.queueUrlPattern.$error> <div ng-message=required translate>tb.rulenode.queue-url-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.queue-url-pattern-hint</div> </md-input-container> <md-input-container class=md-block ng-if="configuration.queueType == ruleNodeTypes.sqsQueueType.STANDARD.value"> <label translate>tb.rulenode.delay-seconds</label> <input type=number step=1 name=delaySeconds ng-model=configuration.delaySeconds min=0 max=900> <div ng-messages=sqsConfigForm.delaySeconds.$error> <div ng-message=min translate>tb.rulenode.min-delay-seconds-message</div> <div ng-message=max translate>tb.rulenode.max-delay-seconds-message</div> </div> </md-input-container> <label translate class=tb-title>tb.rulenode.message-attributes</label> <div class=tb-hint translate>tb.rulenode.message-attributes-hint</div> <tb-kv-map-config ng-model=configuration.messageAttributes ng-required=false key-text="\'tb.rulenode.name\'" key-required-text="\'tb.rulenode.name-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> <md-input-container class=md-block> <label translate>tb.rulenode.aws-access-key-id</label> <input ng-required=true name=accessKeyId ng-model=configuration.accessKeyId> <div ng-messages=snsConfigForm.accessKeyId.$error> <div ng-message=required translate>tb.rulenode.aws-access-key-id-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-secret-access-key</label> <input ng-required=true name=secretAccessKey ng-model=configuration.secretAccessKey> <div ng-messages=snsConfigForm.secretAccessKey.$error> <div ng-message=required translate>tb.rulenode.aws-secret-access-key-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-region</label> <input ng-required=true name=region ng-model=configuration.region> <div ng-messages=snsConfigForm.region.$error> <div ng-message=required translate>tb.rulenode.aws-region-required</div> </div> </md-input-container> </section> '},function(e,t){e.exports=" <section ng-form name=timeseriesConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.default-ttl</label> <input ng-required=true type=number step=1 name=defaultTTL ng-model=configuration.defaultTTL min=0> <div ng-messages=timeseriesConfigForm.defaultTTL.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.default-ttl-required</div> <div ng-message=min translate>tb.rulenode.min-default-ttl-message</div> </div> </md-input-container> </section> "},function(e,t){e.exports=' <section layout=column> <div layout=row> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=query.direction> <md-option ng-repeat="direction in types.entitySearchDirection" ng-value=direction> {{ (\'relation.search-direction.\' + direction) | translate}} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.max-relation-level</label> <input name=maxRelationLevel type=number min=1 step=1 placeholder="{{ \'tb.rulenode.unlimited-level\' | translate }}" ng-model=query.maxLevel aria-label="{{ \'tb.rulenode.max-relation-level\' | translate }}"> </md-input-container> </div> <div class=md-caption style=color:rgba(0,0,0,.57) translate>relation.relation-type</div> <tb-relation-type-autocomplete flex hide-label ng-model=query.relationType tb-required=false> </tb-relation-type-autocomplete> <div class="md-caption tb-required" style=color:rgba(0,0,0,.57) translate>device.device-types</div> <tb-entity-subtype-list tb-required=true entity-type=types.entityType.device ng-model=query.deviceTypes> </tb-entity-subtype-list> </section> ';
  2 +},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title tb-required\">tb.rulenode.attr-mapping</label> <md-checkbox aria-label=\"{{ 'tb.rulenode.latest-telemetry' | translate }}\" ng-model=configuration.telemetry>{{ 'tb.rulenode.latest-telemetry' | translate }} </md-checkbox> <tb-kv-map-config ng-model=configuration.attrMapping ng-required=true required-text=\"'tb.rulenode.attr-mapping-required'\" key-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry' : 'tb.rulenode.source-attribute'\" key-required-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry-required' : 'tb.rulenode.source-attribute-required'\" val-text=\"'tb.rulenode.target-attribute'\" val-required-text=\"'tb.rulenode.target-attribute-required'\"> </tb-kv-map-config> </section> "},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title tb-required">tb.rulenode.device-relations-query</label> <tb-device-relations-query-config style=padding-bottom:15px ng-model=configuration.deviceRelationsQuery> </tb-device-relations-query-config> <label translate class="tb-title no-padding">tb.rulenode.client-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.clientAttributeNames placeholder="{{\'tb.rulenode.client-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.shared-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.sharedAttributeNames placeholder="{{\'tb.rulenode.shared-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.server-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.serverAttributeNames placeholder="{{\'tb.rulenode.server-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.latest-timeseries</label> <md-chips ng-required=false readonly=readonly ng-model=configuration.latestTsKeyNames placeholder="{{\'tb.rulenode.latest-timeseries\' | translate}}" md-separator-keys=separatorKeys> </md-chips> </section> '},function(e,t){e.exports=' <section class=tb-telemetry-from-database-config ng-form name=getTelemetryConfigForm layout=column> <label translate class="tb-title no-padding">tb.rulenode.latest-timeseries</label> <md-chips ng-required=false readonly=readonly ng-model=configuration.latestTsKeyNames placeholder="{{\'tb.rulenode.latest-timeseries\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <md-input-container style=margin-bottom:38px;margin-top:58px> <label translate class="tb-title no-padding">tb.rulenode.fetch-mode</label> <md-select required ng-model=configuration.fetchMode> <md-option ng-repeat="type in ruleNodeTypes.fetchModeType" ng-value=type> {{ type }} </md-option> </md-select> </md-input-container> <div layout=column layout-gt-sm=row> <md-input-container flex class="md-block tb-time-value"> <label translate class="tb-title no-padding">tb.rulenode.start-interval</label> <input required type=number step=1 min=1 max=2147483647 name=startInterval ng-model=configuration.startInterval> <div ng-messages=getTelemetryConfigForm.startInterval.$error> <div translate ng-message=required>tb.rulenode.start-interval-value-required</div> <div ng-message=min translate>tb.rulenode.time-value-range</div> <div ng-message=max translate>tb.rulenode.time-value-range</div> </div> </md-input-container> <md-input-container flex class="md-block tb-time-unit"> <label translate class="tb-title no-padding">tb.rulenode.start-interval-time-unit</label> <md-select required name=startIntervalTimeUnit aria-label="{{ \'tb.rulenode.start-interval-time-unit\' | translate }}" ng-model=configuration.startIntervalTimeUnit> <md-option ng-repeat="timeUnit in ruleNodeTypes.timeUnit" ng-value=timeUnit.value> {{timeUnit.name | translate}} </md-option> </md-select> </md-input-container> </div> <div layout=column layout-gt-sm=row> <md-input-container flex class="md-block tb-time-value"> <label translate class="tb-title no-padding">tb.rulenode.end-interval</label> <input required type=number step=1 min=1 max=2147483647 name=endInterval ng-model=configuration.endInterval> <div ng-messages=getTelemetryConfigForm.endInterval.$error> <div translate ng-message=required>tb.rulenode.end-interval-value-required</div> <div ng-message=min translate>tb.rulenode.time-value-range</div> <div ng-message=max translate>tb.rulenode.time-value-range</div> </div> </md-input-container> <md-input-container flex class="md-block tb-time-unit"> <label translate class="tb-title no-padding">tb.rulenode.end-interval-time-unit</label> <md-select required name=endIntervalTimeUnit aria-label="{{ \'tb.rulenode.end-interval-time-unit\' | translate }}" ng-model=configuration.endIntervalTimeUnit> <md-option ng-repeat="timeUnit in ruleNodeTypes.timeUnit" ng-value=timeUnit.value> {{timeUnit.name | translate}} </md-option> </md-select> </md-input-container> </div> </section>'},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding">tb.rulenode.client-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.clientAttributeNames placeholder="{{\'tb.rulenode.client-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.shared-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.sharedAttributeNames placeholder="{{\'tb.rulenode.shared-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.server-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.serverAttributeNames placeholder="{{\'tb.rulenode.server-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.latest-timeseries</label> <md-chips ng-required=false readonly=readonly ng-model=configuration.latestTsKeyNames placeholder="{{\'tb.rulenode.latest-timeseries\' | translate}}" md-separator-keys=separatorKeys> </md-chips> </section> '},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title tb-required">tb.rulenode.fields-mapping</label> <tb-kv-map-config ng-model=configuration.fieldsMapping ng-required=true required-text="\'tb.rulenode.fields-mapping-required\'" key-text="\'tb.rulenode.source-field\'" key-required-text="\'tb.rulenode.source-field-required\'" val-text="\'tb.rulenode.target-attribute\'" val-required-text="\'tb.rulenode.target-attribute-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title tb-required\">tb.rulenode.relations-query</label> <tb-relations-query-config style=padding-bottom:15px ng-model=configuration.relationsQuery> </tb-relations-query-config> <label translate class=\"tb-title tb-required\">tb.rulenode.attr-mapping</label> <md-checkbox aria-label=\"{{ 'tb.rulenode.latest-telemetry' | translate }}\" ng-model=configuration.telemetry>{{ 'tb.rulenode.latest-telemetry' | translate }} </md-checkbox> <tb-kv-map-config ng-model=configuration.attrMapping ng-required=true required-text=\"'tb.rulenode.attr-mapping-required'\" key-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry' : 'tb.rulenode.source-attribute'\" key-required-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry-required' : 'tb.rulenode.source-attribute-required'\" val-text=\"'tb.rulenode.target-attribute'\" val-required-text=\"'tb.rulenode.target-attribute-required'\"> </tb-kv-map-config> </section> "},23,function(e,t){e.exports=" <section ng-form name=checkRelationConfigForm> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=configuration.direction> <md-option ng-repeat=\"direction in types.entitySearchDirection\" ng-value=direction> {{ ('relation.search-direction.' + direction) | translate}} </md-option> </md-select> </md-input-container> <div layout=row class=tb-entity-select> <tb-entity-type-select style=min-width:100px the-form=checkRelationConfigForm tb-required=true ng-model=configuration.entityType> </tb-entity-type-select> <tb-entity-autocomplete flex ng-if=configuration.entityType the-form=checkRelationConfigForm tb-required=true entity-type=configuration.entityType ng-model=configuration.entityId> </tb-entity-autocomplete> </div> <tb-relation-type-autocomplete hide-label ng-model=configuration.relationType tb-required=true> </tb-relation-type-autocomplete> </section> "},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding" ng-class="{\'tb-required\': required}">tb.rulenode.message-types-filter</label> <md-chips id=message_type_chips ng-required=required readonly=readonly ng-model=messageTypes md-autocomplete-snap md-transform-chip=transformMessageTypeChip($chip) md-require-match=false> <md-autocomplete id=message_type md-no-cache=true md-selected-item=selectedMessageType md-search-text=messageTypeSearchText md-items="item in messageTypesSearch(messageTypeSearchText)" md-item-text=item.name md-min-length=0 placeholder="{{\'tb.rulenode.message-type\' | translate }}" md-menu-class=tb-message-type-autocomplete> <span md-highlight-text=messageTypeSearchText md-highlight-flags=^i>{{item}}</span> <md-not-found> <div class=tb-not-found> <div class=tb-no-entries ng-if="!messageTypeSearchText || !messageTypeSearchText.length"> <span translate>tb.rulenode.no-message-types-found</span> </div> <div ng-if="messageTypeSearchText && messageTypeSearchText.length"> <span translate translate-values=\'{ messageType: "{{messageTypeSearchText | truncate:true:6:&apos;...&apos;}}" }\'>tb.rulenode.no-message-type-matching</span> <span> <a translate ng-click="createMessageType($event, \'#message_type_chips\')">tb.rulenode.create-new-message-type</a> </span> </div> </div> </md-not-found> </md-autocomplete> <md-chip-template> <span>{{$chip.name}}</span> </md-chip-template> </md-chips> <div class=tb-error-messages ng-messages=ngModelCtrl.$error role=alert> <div translate ng-message=messageTypes class=tb-error-message>tb.rulenode.message-types-required</div> </div> </section>'},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding" class=required>tb.rulenode.originator-types-filter</label> <tb-entity-type-list flex ng-model=configuration.originatorTypes allowed-entity-types=allowedEntityTypes ignore-authority-filter=true tb-required=true> </tb-entity-type-list> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.filter</label> <tb-js-func ng-model=configuration.jsScript function-name=Filter function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-filter-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.switch</label> <tb-js-func ng-model=configuration.jsScript function-name=Switch function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-switch-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=' <section class=tb-kv-map-config layout=column> <div class=header flex layout=row> <span class=cell flex translate>{{ keyText }}</span> <span class=cell flex translate>{{ valText }}</span> <span ng-show=!disabled style=width:52px>&nbsp</span> </div> <div class=body> <div class=row ng-form name=kvForm flex layout=row layout-align="start center" ng-repeat="keyVal in kvList track by $index"> <md-input-container class="cell md-block" flex md-no-float> <input placeholder="{{ keyText | translate }}" ng-required=true name=key ng-model=keyVal.key> <div ng-messages=kvForm.key.$error> <div translate ng-message=required>{{keyRequiredText}}</div> </div> </md-input-container> <md-input-container class="cell md-block" flex md-no-float> <input placeholder="{{ valText | translate }}" ng-required=true name=value ng-model=keyVal.value> <div ng-messages=kvForm.value.$error> <div translate ng-message=required>{{valRequiredText}}</div> </div> </md-input-container> <md-button ng-show=!disabled ng-disabled=loading class="md-icon-button md-primary" ng-click=removeKeyVal($index) aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'tb.key-val.remove-entry\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.delete\' | translate }}" class=material-icons> close </md-icon> </md-button> </div> </div> <div class=tb-error-messages ng-messages=ngModelCtrl.$error role=alert> <div translate ng-message=kvMap class=tb-error-message>{{requiredText}}</div> </div> <div> <md-button ng-show=!disabled ng-disabled=loading class="md-primary md-raised" ng-click=addKeyVal() aria-label="{{ \'action.add\' | translate }}"> <md-tooltip md-direction=top> {{ \'tb.key-val.add-entry\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.add\' | translate }}" class=material-icons> add </md-icon> {{ \'action.add\' | translate }} </md-button> </div> </section> '},function(e,t){e.exports=" <section layout=column> <div layout=row> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=query.direction> <md-option ng-repeat=\"direction in types.entitySearchDirection\" ng-value=direction> {{ ('relation.search-direction.' + direction) | translate}} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.max-relation-level</label> <input name=maxRelationLevel type=number min=1 step=1 placeholder=\"{{ 'tb.rulenode.unlimited-level' | translate }}\" ng-model=query.maxLevel aria-label=\"{{ 'tb.rulenode.max-relation-level' | translate }}\"> </md-input-container> </div> <div class=md-caption style=padding-bottom:10px;color:rgba(0,0,0,.57) translate>relation.relation-filters</div> <tb-relation-filters ng-model=query.filters> </tb-relation-filters> </section> "},function(e,t){e.exports=' <section layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.originator-source</label> <md-select required ng-model=configuration.originatorSource> <md-option ng-repeat="source in ruleNodeTypes.originatorSource" ng-value=source.value> {{ source.name | translate}} </md-option> </md-select> </md-input-container> <section layout=column ng-if="configuration.originatorSource == ruleNodeTypes.originatorSource.RELATED.value"> <label translate class="tb-title tb-required">tb.rulenode.relations-query</label> <tb-relations-query-config style=padding-bottom:15px ng-model=configuration.relationsQuery> </tb-relations-query-config> </section> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.transform</label> <tb-js-func ng-model=configuration.jsScript function-name=Transform function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row style=padding-bottom:15px> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-transformer-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=" <section ng-form name=toEmailConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.from-template</label> <textarea ng-required=true name=fromTemplate ng-model=configuration.fromTemplate rows=2></textarea> <div ng-messages=toEmailConfigForm.fromTemplate.$error> <div ng-message=required translate>tb.rulenode.from-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.from-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.to-template</label> <textarea ng-required=true name=toTemplate ng-model=configuration.toTemplate rows=2></textarea> <div ng-messages=toEmailConfigForm.toTemplate.$error> <div ng-message=required translate>tb.rulenode.to-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.mail-address-list-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.cc-template</label> <textarea name=ccTemplate ng-model=configuration.ccTemplate rows=2></textarea> <div class=tb-hint translate>tb.rulenode.mail-address-list-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.bcc-template</label> <textarea name=ccTemplate ng-model=configuration.bccTemplate rows=2></textarea> <div class=tb-hint translate>tb.rulenode.mail-address-list-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.subject-template</label> <textarea ng-required=true name=subjectTemplate ng-model=configuration.subjectTemplate rows=2></textarea> <div ng-messages=toEmailConfigForm.subjectTemplate.$error> <div ng-message=required translate>tb.rulenode.subject-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.subject-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.body-template</label> <textarea ng-required=true name=bodyTemplate ng-model=configuration.bodyTemplate rows=6></textarea> <div ng-messages=toEmailConfigForm.bodyTemplate.$error> <div ng-message=required translate>tb.rulenode.body-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.body-template-hint</div> </md-input-container> </section> "},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(6),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var u=o.default;i.html(u),r.types=n,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue},r.testDetailsBuildJs=function(e){var n=angular.copy(r.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(7),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var u=o.default;i.html(u),r.types=n,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue},r.testDetailsBuildJs=function(e){var n=angular.copy(r.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(8),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var u=o.default;i.html(u),r.types=n,r.originator=null,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue,r.configuration.originatorId&&r.configuration.originatorType?r.originator={id:r.configuration.originatorId,entityType:r.configuration.originatorType}:r.originator=null,r.$watch("originator",function(e,t){angular.equals(e,t)||(r.originator?(s.$viewValue.originatorId=r.originator.id,s.$viewValue.originatorType=r.originator.entityType):(s.$viewValue.originatorId=null,s.$viewValue.originatorType=null))},!0)},r.testScript=function(e){var n=angular.copy(r.configuration.jsScript);a.testNodeScript(e,n,"generate",t.instant("tb.rulenode.generator")+"","Generate",["prevMsg","prevMetadata","prevMsgType"],r.ruleNodeId).then(function(e){r.configuration.jsScript=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(1);var i=n(9),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(56),i=a(r),o=n(40),l=a(o),s=n(43),u=a(s),d=n(42),c=a(d),m=n(41),g=a(m),p=n(46),f=a(p),b=n(51),v=a(b),y=n(52),q=a(y),h=n(50),$=a(h),T=n(45),k=a(T),w=n(54),x=a(w),C=n(55),M=a(C),S=n(49),_=a(S),N=n(47),V=a(N),E=n(53),P=a(E),j=n(48),F=a(j);t.default=angular.module("thingsboard.ruleChain.config.action",[]).directive("tbActionNodeTimeseriesConfig",i.default).directive("tbActionNodeAttributesConfig",l.default).directive("tbActionNodeGeneratorConfig",u.default).directive("tbActionNodeCreateAlarmConfig",c.default).directive("tbActionNodeClearAlarmConfig",g.default).directive("tbActionNodeLogConfig",f.default).directive("tbActionNodeRpcReplyConfig",v.default).directive("tbActionNodeRpcRequestConfig",q.default).directive("tbActionNodeRestApiCallConfig",$.default).directive("tbActionNodeKafkaConfig",k.default).directive("tbActionNodeSnsConfig",x.default).directive("tbActionNodeSqsConfig",M.default).directive("tbActionNodeRabbitMqConfig",_.default).directive("tbActionNodeMqttConfig",V.default).directive("tbActionNodeSendEmailConfig",P.default).directive("tbActionNodeMsgDelayConfig",F.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.ackValues=["all","-1","0","1"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(10),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"string",t.instant("tb.rulenode.to-string")+"","ToString",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(11),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$mdExpansionPanel=t,a.ruleNodeTypes=n,a.credentialsTypeChanged=function(){var e=a.configuration.credentials.type;a.configuration.credentials={},a.configuration.credentials.type=e,a.updateValidity()},a.certFileAdded=function(e,t){var n=new FileReader;n.onload=function(n){a.$apply(function(){if(n.target.result){l.$setDirty();var r=n.target.result;r&&r.length>0&&("caCert"==t&&(a.configuration.credentials.caCertFileName=e.name,a.configuration.credentials.caCert=r),"privateKey"==t&&(a.configuration.credentials.privateKeyFileName=e.name,a.configuration.credentials.privateKey=r),"Cert"==t&&(a.configuration.credentials.certFileName=e.name,a.configuration.credentials.cert=r)),a.updateValidity()}})},n.readAsText(e.file)},a.clearCertFile=function(e){l.$setDirty(),"caCert"==e&&(a.configuration.credentials.caCertFileName=null,a.configuration.credentials.caCert=null),"privateKey"==e&&(a.configuration.credentials.privateKeyFileName=null,a.configuration.credentials.privateKey=null),"Cert"==e&&(a.configuration.credentials.certFileName=null,a.configuration.credentials.cert=null),a.updateValidity()},a.updateValidity=function(){var e=!0,t=a.configuration.credentials;t.type==n.mqttCredentialTypes["cert.PEM"].value&&(t.caCert&&t.cert&&t.privateKey||(e=!1)),l.$setValidity("Certs",e)},a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:a}}r.$inject=["$compile","$mdExpansionPanel","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(2);var i=n(12),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(13),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(14),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(15),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(16),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(17),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.smtpProtocols=["smtp","smtps"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(18),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(19),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(20),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(21),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(22),o=a(i)},function(e,t){"use strict";function n(e){var t=function(t,n,a,r){n.html("<div></div>"),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}n.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(23),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration);
  3 +}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(24),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s);var u=186;a.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,u],a.ruleNodeTypes=n,a.aggPeriodTimeUnits={},a.aggPeriodTimeUnits.MINUTES=n.timeUnit.MINUTES,a.aggPeriodTimeUnits.HOURS=n.timeUnit.HOURS,a.aggPeriodTimeUnits.DAYS=n.timeUnit.DAYS,a.aggPeriodTimeUnits.MILLISECONDS=n.timeUnit.MILLISECONDS,a.aggPeriodTimeUnits.SECONDS=n.timeUnit.SECONDS,a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{},link:a}}r.$inject=["$compile","$mdConstant","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(25),o=a(i);n(3)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(63),i=a(r),o=n(64),l=a(o),s=n(60),u=a(s),d=n(65),c=a(d),m=n(59),g=a(m),p=n(66),f=a(p),b=n(61),v=a(b);t.default=angular.module("thingsboard.ruleChain.config.enrichment",[]).directive("tbEnrichmentNodeOriginatorAttributesConfig",i.default).directive("tbEnrichmentNodeOriginatorFieldsConfig",l.default).directive("tbEnrichmentNodeDeviceAttributesConfig",u.default).directive("tbEnrichmentNodeRelatedAttributesConfig",c.default).directive("tbEnrichmentNodeCustomerAttributesConfig",g.default).directive("tbEnrichmentNodeTenantAttributesConfig",f.default).directive("tbEnrichmentNodeGetTelemetryFromDatabase",v.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(26),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(27),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(28),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(29),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(30),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(71),i=a(r),o=n(69),l=a(o),s=n(72),u=a(s),d=n(67),c=a(d),m=n(70),g=a(m);t.default=angular.module("thingsboard.ruleChain.config.filter",[]).directive("tbFilterNodeScriptConfig",i.default).directive("tbFilterNodeMessageTypeConfig",l.default).directive("tbFilterNodeSwitchConfig",u.default).directive("tbFilterNodeCheckRelationConfig",c.default).directive("tbFilterNodeOriginatorTypeConfig",g.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){function s(){if(l.$viewValue){for(var e=[],t=0;t<a.messageTypes.length;t++)e.push(a.messageTypes[t].value);l.$viewValue.messageTypes=e,u()}}function u(){if(a.required){var e=!(!l.$viewValue.messageTypes||!l.$viewValue.messageTypes.length);l.$setValidity("messageTypes",e)}else l.$setValidity("messageTypes",!0)}var d=o.default;r.html(d),a.selectedMessageType=null,a.messageTypeSearchText=null,a.ngModelCtrl=l;var c=[];for(var m in n.messageType){var g={name:n.messageType[m].name,value:n.messageType[m].value};c.push(g)}a.transformMessageTypeChip=function(e){var n,a=t("filter")(c,{name:e},!0);return n=a&&a.length?angular.copy(a[0]):{name:e,value:e}},a.messageTypesSearch=function(e){var n=e?t("filter")(c,{name:e}):c;return n.map(function(e){return e.name})},a.createMessageType=function(e,t){var n=angular.element(t,r)[0].firstElementChild,a=angular.element(n),i=a.scope().$mdChipsCtrl.getChipBuffer();e.preventDefault(),e.stopPropagation(),a.scope().$mdChipsCtrl.appendChip(i.trim()),a.scope().$mdChipsCtrl.resetChipBuffer()},l.$render=function(){a.messageTypesWatch&&(a.messageTypesWatch(),a.messageTypesWatch=null);var e=l.$viewValue,t=[];if(e&&e.messageTypes)for(var r=0;r<e.messageTypes.length;r++){var i=e.messageTypes[r];n.messageType[i]?t.push(angular.copy(n.messageType[i])):t.push({name:i,value:i})}a.messageTypes=t,a.messageTypesWatch=a.$watch("messageTypes",function(e,t){angular.equals(e,t)||s()},!0)},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",readonly:"=ngReadonly"},link:a}}r.$inject=["$compile","$filter","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(4);var i=n(31),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.allowedEntityTypes=[t.entityType.device,t.entityType.asset,t.entityType.tenant,t.entityType.customer,t.entityType.user,t.entityType.dashboard,t.entityType.rulechain,t.entityType.rulenode],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(32),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"filter",t.instant("tb.rulenode.filter")+"","Filter",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(33),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"switch",t.instant("tb.rulenode.switch")+"","Switch",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(34),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){function i(e){e>-1&&t.kvList.splice(e,1)}function l(){t.kvList||(t.kvList=[]),t.kvList.push({key:"",value:""})}function s(){var e={};t.kvList.forEach(function(t){t.key&&(e[t.key]=t.value)}),r.$setViewValue(e),u()}function u(){var e=!0;t.required&&!t.kvList.length&&(e=!1),r.$setValidity("kvMap",e)}var d=o.default;n.html(d),t.ngModelCtrl=r,t.removeKeyVal=i,t.addKeyVal=l,t.kvList=[],t.$watch("query",function(e,n){angular.equals(e,n)||r.$setViewValue(t.query)}),r.$render=function(){if(r.$viewValue){var e=r.$viewValue;t.kvList.length=0;for(var n in e)t.kvList.push({key:n,value:e[n]})}t.$watch("kvList",function(e,t){angular.equals(e,t)||s()},!0),u()},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",disabled:"=ngDisabled",requiredText:"=",keyText:"=",keyRequiredText:"=",valText:"=",valRequiredText:"="},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(35),o=a(i);n(5)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(36),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(37),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(75),i=a(r),o=n(77),l=a(o),s=n(78),u=a(s);t.default=angular.module("thingsboard.ruleChain.config.transform",[]).directive("tbTransformationNodeChangeOriginatorConfig",i.default).directive("tbTransformationNodeScriptConfig",l.default).directive("tbTransformationNodeToEmailConfig",u.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"update",t.instant("tb.rulenode.transformer")+"","Transform",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(38),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(39),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(82),i=a(r),o=n(68),l=a(o),s=n(62),u=a(s),d=n(76),c=a(d),m=n(44),g=a(m),p=n(58),f=a(p),b=n(74),v=a(b),y=n(57),q=a(y),h=n(73),$=a(h),T=n(81),k=a(T);t.default=angular.module("thingsboard.ruleChain.config",[i.default,l.default,u.default,c.default,g.default]).directive("tbNodeEmptyConfig",f.default).directive("tbRelationsQueryConfig",v.default).directive("tbDeviceRelationsQueryConfig",q.default).directive("tbKvMapConfig",$.default).config(k.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{"start-interval":"Start Interval","end-interval":"End Interval","start-interval-time-unit":"Start Interval Time Unit","end-interval-time-unit":"End Interval Time Unit","fetch-mode":"Fetch mode","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Time value should be in a range from 1 to 2147483647'.","start-interval-value-required":"Start interval value is required.","end-interval-value-required":"End interval value is required.",filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one!","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","latest-timeseries":"Latest timeseries","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required",propagate:"Propagate",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From Template","from-template-required":"From Template is required","from-template-hint":"From address template, use <code>${metaKeyName}</code> to substitute variables from metadata","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":"Comma separated address list, use <code>${metaKeyName}</code> to substitute variables from metadata","cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","subject-template-hint":"Mail subject template, use <code>${metaKeyName}</code> to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use <code>${metaKeyName}</code> to substitute variables from metadata","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","endpoint-url-pattern-hint":"HTTP URL address pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","request-method":"Request method",headers:"Headers","headers-hint":"Use <code>${metaKeyName}</code> in header/value fields to substitute variables from metadata",header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required","mqtt-topic-pattern-hint":"MQTT topic pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","topic-arn-pattern-hint":"Topic ARN pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","queue-url-pattern-hint":"Queue URL pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","message-attributes":"Message attributes","message-attributes-hint":"Use <code>${metaKeyName}</code> in name/value fields to substitute variables from metadata","connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"CA certificate file *","private-key":"Private key file *",cert:"Certificate file *","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter"},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}};e.translations("en_US",t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){(0,o.default)(e)}r.$inject=["$translateProvider"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(80),o=a(i)},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=angular.module("thingsboard.ruleChain.config.types",[]).constant("ruleNodeTypes",{originatorSource:{CUSTOMER:{name:"tb.rulenode.originator-customer",value:"CUSTOMER"},TENANT:{name:"tb.rulenode.originator-tenant",value:"TENANT"},RELATED:{name:"tb.rulenode.originator-related",value:"RELATED"}},fetchModeType:["FIRST","LAST","ALL"],httpRequestType:["GET","POST","PUT","DELETE"],sqsQueueType:{STANDARD:{name:"tb.rulenode.sqs-queue-standard",value:"STANDARD"},FIFO:{name:"tb.rulenode.sqs-queue-fifo",value:"FIFO"}},timeUnit:{MILLISECONDS:{value:"MILLISECONDS",name:"tb.rulenode.time-unit-milliseconds"},SECONDS:{value:"SECONDS",name:"tb.rulenode.time-unit-seconds"},MINUTES:{value:"MINUTES",name:"tb.rulenode.time-unit-minutes"},HOURS:{value:"HOURS",name:"tb.rulenode.time-unit-hours"},DAYS:{value:"DAYS",name:"tb.rulenode.time-unit-days"}},mqttCredentialTypes:{anonymous:{value:"anonymous",name:"tb.rulenode.credentials-anonymous"},basic:{value:"basic",name:"tb.rulenode.credentials-basic"},"cert.PEM":{value:"cert.PEM",name:"tb.rulenode.credentials-pem"}}}).name}]));
4 4 //# sourceMappingURL=rulenode-core-config.js.map
\ No newline at end of file
... ...
  1 +{
  2 + "extends": ["stylelint-config-standard", "stylelint-config-recommended-scss"],
  3 + "plugins": [
  4 + "stylelint-order"
  5 + ],
  6 + "rules": {
  7 + "at-rule-empty-line-before": ["always", {
  8 + "except": ["first-nested"],
  9 + "ignore": ["after-comment"]
  10 + }],
  11 + "at-rule-name-space-after": "always",
  12 + "at-rule-no-vendor-prefix": true,
  13 + "at-rule-semicolon-space-before": "never",
  14 + "block-closing-brace-empty-line-before": "never",
  15 + "block-closing-brace-newline-after": null,
  16 + "block-opening-brace-space-before": null,
  17 + "color-named": "never",
  18 + "declaration-block-semicolon-newline-after": "always-multi-line",
  19 + "declaration-block-semicolon-newline-before": "never-multi-line",
  20 + "declaration-block-semicolon-space-after": "always-single-line",
  21 + "declaration-empty-line-before": null,
  22 + "declaration-no-important": null,
  23 + "font-family-name-quotes": "always-where-recommended",
  24 + "font-weight-notation": [
  25 + "numeric", {
  26 + "ignore": ["relative"]
  27 + }],
  28 + "function-url-no-scheme-relative": true,
  29 + "function-url-quotes": "always",
  30 + "length-zero-no-unit": true,
  31 + "max-empty-lines": 2,
  32 + "max-line-length": null,
  33 + "media-feature-name-no-vendor-prefix": true,
  34 + "media-feature-parentheses-space-inside": "never",
  35 + "media-feature-range-operator-space-after": "always",
  36 + "media-feature-range-operator-space-before": "never",
  37 + "no-descending-specificity": null,
  38 + "no-duplicate-selectors": true,
  39 + "number-leading-zero": "never",
  40 + "media-feature-name-no-unknown": [true, {
  41 + "ignoreMediaFeatureNames": ["prefers-reduced-motion"]
  42 + }],
  43 + "order/properties-order": [
  44 + "position",
  45 + "top",
  46 + "right",
  47 + "bottom",
  48 + "left",
  49 + "z-index",
  50 + "box-sizing",
  51 + "display",
  52 + "flex",
  53 + "flex-align",
  54 + "flex-basis",
  55 + "flex-direction",
  56 + "flex-wrap",
  57 + "flex-flow",
  58 + "flex-shrink",
  59 + "flex-grow",
  60 + "flex-order",
  61 + "flex-pack",
  62 + "align-content",
  63 + "align-items",
  64 + "align-self",
  65 + "justify-content",
  66 + "order",
  67 + "float",
  68 + "width",
  69 + "min-width",
  70 + "max-width",
  71 + "height",
  72 + "min-height",
  73 + "max-height",
  74 + "padding",
  75 + "padding-top",
  76 + "padding-right",
  77 + "padding-bottom",
  78 + "padding-left",
  79 + "margin",
  80 + "margin-top",
  81 + "margin-right",
  82 + "margin-bottom",
  83 + "margin-left",
  84 + "overflow",
  85 + "overflow-x",
  86 + "overflow-y",
  87 + "-webkit-overflow-scrolling",
  88 + "-ms-overflow-x",
  89 + "-ms-overflow-y",
  90 + "-ms-overflow-style",
  91 + "columns",
  92 + "column-count",
  93 + "column-fill",
  94 + "column-gap",
  95 + "column-rule",
  96 + "column-rule-width",
  97 + "column-rule-style",
  98 + "column-rule-color",
  99 + "column-span",
  100 + "column-width",
  101 + "orphans",
  102 + "widows",
  103 + "clip",
  104 + "clear",
  105 + "font",
  106 + "font-family",
  107 + "font-size",
  108 + "font-style",
  109 + "font-weight",
  110 + "font-variant",
  111 + "font-size-adjust",
  112 + "font-stretch",
  113 + "font-effect",
  114 + "font-emphasize",
  115 + "font-emphasize-position",
  116 + "font-emphasize-style",
  117 + "font-smooth",
  118 + "src",
  119 + "hyphens",
  120 + "line-height",
  121 + "color",
  122 + "text-align",
  123 + "text-align-last",
  124 + "text-emphasis",
  125 + "text-emphasis-color",
  126 + "text-emphasis-style",
  127 + "text-emphasis-position",
  128 + "text-decoration",
  129 + "text-indent",
  130 + "text-justify",
  131 + "text-outline",
  132 + "-ms-text-overflow",
  133 + "text-overflow",
  134 + "text-overflow-ellipsis",
  135 + "text-overflow-mode",
  136 + "text-shadow",
  137 + "text-transform",
  138 + "text-wrap",
  139 + "-webkit-text-size-adjust",
  140 + "-ms-text-size-adjust",
  141 + "letter-spacing",
  142 + "-ms-word-break",
  143 + "word-break",
  144 + "word-spacing",
  145 + "-ms-word-wrap",
  146 + "word-wrap",
  147 + "overflow-wrap",
  148 + "tab-size",
  149 + "white-space",
  150 + "vertical-align",
  151 + "direction",
  152 + "unicode-bidi",
  153 + "list-style",
  154 + "list-style-position",
  155 + "list-style-type",
  156 + "list-style-image",
  157 + "pointer-events",
  158 + "-ms-touch-action",
  159 + "touch-action",
  160 + "cursor",
  161 + "visibility",
  162 + "zoom",
  163 + "table-layout",
  164 + "empty-cells",
  165 + "caption-side",
  166 + "border-spacing",
  167 + "border-collapse",
  168 + "content",
  169 + "quotes",
  170 + "counter-reset",
  171 + "counter-increment",
  172 + "resize",
  173 + "user-select",
  174 + "nav-index",
  175 + "nav-up",
  176 + "nav-right",
  177 + "nav-down",
  178 + "nav-left",
  179 + "background",
  180 + "background-color",
  181 + "background-image",
  182 + "filter",
  183 + "background-repeat",
  184 + "background-attachment",
  185 + "background-position",
  186 + "background-position-x",
  187 + "background-position-y",
  188 + "background-clip",
  189 + "background-origin",
  190 + "background-size",
  191 + "border",
  192 + "border-color",
  193 + "border-style",
  194 + "border-width",
  195 + "border-top",
  196 + "border-top-color",
  197 + "border-top-style",
  198 + "border-top-width",
  199 + "border-right",
  200 + "border-right-color",
  201 + "border-right-style",
  202 + "border-right-width",
  203 + "border-bottom",
  204 + "border-bottom-color",
  205 + "border-bottom-style",
  206 + "border-bottom-width",
  207 + "border-left",
  208 + "border-left-color",
  209 + "border-left-style",
  210 + "border-left-width",
  211 + "border-radius",
  212 + "border-top-left-radius",
  213 + "border-top-right-radius",
  214 + "border-bottom-right-radius",
  215 + "border-bottom-left-radius",
  216 + "border-image",
  217 + "border-image-source",
  218 + "border-image-slice",
  219 + "border-image-width",
  220 + "border-image-outset",
  221 + "border-image-repeat",
  222 + "outline",
  223 + "outline-width",
  224 + "outline-style",
  225 + "outline-color",
  226 + "outline-offset",
  227 + "box-shadow",
  228 + "opacity",
  229 + "-ms-interpolation-mode",
  230 + "page-break-after",
  231 + "page-break-before",
  232 + "page-break-inside",
  233 + "transition",
  234 + "transition-delay",
  235 + "transition-timing-function",
  236 + "transition-duration",
  237 + "transition-property",
  238 + "transform",
  239 + "transform-origin",
  240 + "perspective",
  241 + "appearance",
  242 + "animation",
  243 + "animation-name",
  244 + "animation-duration",
  245 + "animation-play-state",
  246 + "animation-timing-function",
  247 + "animation-delay",
  248 + "animation-iteration-count",
  249 + "animation-direction",
  250 + "animation-fill-mode",
  251 + "fill",
  252 + "stroke"
  253 + ],
  254 + "property-no-vendor-prefix": null,
  255 + "rule-empty-line-before": ["always", {
  256 + "except": ["first-nested"],
  257 + "ignore": ["after-comment"]
  258 + }],
  259 + "scss/dollar-variable-default": [true, { "ignore": "local" }],
  260 + "selector-attribute-quotes": "always",
  261 + "selector-list-comma-newline-after": "always",
  262 + "selector-list-comma-newline-before": "never-multi-line",
  263 + "selector-list-comma-space-after": "always-single-line",
  264 + "selector-list-comma-space-before": "never-single-line",
  265 + "selector-max-attribute": 2,
  266 + "selector-max-class": 6,
  267 + "selector-max-combinators": 8,
  268 + "selector-max-compound-selectors": 9,
  269 + "selector-max-empty-lines": 1,
  270 + "selector-max-id": 1,
  271 + "selector-max-specificity": null,
  272 + "selector-max-type": 5,
  273 + "selector-max-universal": 1,
  274 + "selector-no-qualifying-type": null,
  275 + "selector-no-vendor-prefix": null,
  276 + "selector-type-no-unknown": [true, {
  277 + "ignoreTypes": [
  278 + "/^md-/",
  279 + "/^mdp-/",
  280 + "/^ng-/",
  281 + "/^tb-/",
  282 + "/^v-pane/"
  283 + ]
  284 + }],
  285 + "string-quotes": "double",
  286 + "value-keyword-case": "lower",
  287 + "value-list-comma-newline-after": "always-multi-line",
  288 + "value-list-comma-newline-before": "never-multi-line",
  289 + "value-list-comma-space-after": "always-single-line",
  290 + "value-no-vendor-prefix": null
  291 + }
  292 +}
... ...
... ... @@ -100,6 +100,7 @@
100 100 "copy-webpack-plugin": "^3.0.1",
101 101 "cross-env": "^3.2.4",
102 102 "css-loader": "^0.25.0",
  103 + "directory-tree": "^2.1.0",
103 104 "eslint": "^3.4.0",
104 105 "eslint-config-angular": "^0.5.0",
105 106 "eslint-loader": "^1.5.0",
... ... @@ -112,6 +113,7 @@
112 113 "html-minifier-loader": "^1.3.4",
113 114 "html-webpack-plugin": "^2.30.1",
114 115 "img-loader": "^1.3.1",
  116 + "jsonminify": "^0.4.1",
115 117 "less": "^2.7.1",
116 118 "less-loader": "^2.2.3",
117 119 "ng-annotate-loader": "^0.1.1",
... ... @@ -122,14 +124,18 @@
122 124 "react-hot-loader": "^3.0.0-beta.6",
123 125 "sass-loader": "^4.0.2",
124 126 "style-loader": "^0.13.1",
  127 + "stylelint": "^9.5.0",
  128 + "stylelint-config-recommended-scss": "^3.2.0",
  129 + "stylelint-config-standard": "^18.2.0",
  130 + "stylelint-order": "^1.0.0",
  131 + "stylelint-scss": "^3.3.0",
  132 + "stylelint-webpack-plugin": "^0.10.5",
125 133 "url-loader": "^0.5.7",
126 134 "webpack": "^1.13.2",
127 135 "webpack-dev-middleware": "^1.6.1",
128 136 "webpack-dev-server": "^1.15.1",
129 137 "webpack-hot-middleware": "^2.12.2",
130   - "webpack-material-design-icons": "^0.1.0",
131   - "directory-tree": "^2.1.0",
132   - "jsonminify": "^0.4.1"
  138 + "webpack-material-design-icons": "^0.1.0"
133 139 },
134 140 "engine": "node >= 5.9.0",
135 141 "nyc": {
... ...
... ... @@ -13,14 +13,16 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-alarm-details-panel {
17   - margin-left: 15px;
18   - border: 1px solid #C0C0C0;
19 18 height: 100%;
  19 + margin-left: 15px;
  20 + border: 1px solid #c0c0c0;
  21 +
20 22 #tb-alarm-details {
21   - min-width: 600px;
22   - min-height: 200px;
23 23 width: 100%;
  24 + min-width: 600px;
24 25 height: 100%;
  26 + min-height: 200px;
25 27 }
26 28 }
... ...
... ... @@ -13,26 +13,27 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-alarm-container {
17 18 overflow-x: auto;
18 19 }
19 20
20 21 md-list.tb-alarm-table {
21   - padding: 0px;
22 22 min-width: 700px;
  23 + padding: 0;
23 24
24 25 md-list-item {
25   - padding: 0px;
  26 + padding: 0;
26 27 }
27 28
28 29 .tb-row {
29 30 height: 48px;
30   - padding: 0px;
  31 + padding: 0;
31 32 overflow: hidden;
32 33 }
33 34
34 35 .tb-row:hover {
35   - background-color: #EEEEEE;
  36 + background-color: #eee;
36 37 }
37 38
38 39 .tb-header:hover {
... ... @@ -41,9 +42,9 @@ md-list.tb-alarm-table {
41 42
42 43 .tb-header {
43 44 .tb-cell {
44   - color: rgba(0,0,0,.54);
45 45 font-size: 12px;
46 46 font-weight: 700;
  47 + color: rgba(0, 0, 0, .54);
47 48 white-space: nowrap;
48 49 background: none;
49 50 }
... ... @@ -52,11 +53,12 @@ md-list.tb-alarm-table {
52 53 .tb-cell {
53 54 padding: 0 24px;
54 55 margin: auto 0;
55   - color: rgba(0,0,0,.87);
  56 + overflow: hidden;
56 57 font-size: 13px;
57   - vertical-align: middle;
  58 + color: rgba(0, 0, 0, .87);
58 59 text-align: left;
59   - overflow: hidden;
  60 + vertical-align: middle;
  61 +
60 62 .md-button {
61 63 padding: 0;
62 64 margin: 0;
... ... @@ -66,12 +68,11 @@ md-list.tb-alarm-table {
66 68 .tb-cell.tb-number {
67 69 text-align: right;
68 70 }
69   -
70 71 }
71 72
72 73 #tb-alarm-content {
73   - min-width: 400px;
74   - min-height: 50px;
75 74 width: 100%;
  75 + min-width: 400px;
76 76 height: 100%;
  77 + min-height: 50px;
77 78 }
... ...
... ... @@ -13,10 +13,12 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -#tb-audit-log-action-data, #tb-audit-log-failure-details {
17   - min-width: 400px;
18   - min-height: 50px;
  16 +
  17 +#tb-audit-log-action-data,
  18 +#tb-audit-log-failure-details {
19 19 width: 100%;
  20 + min-width: 400px;
20 21 height: 100%;
21   - border: 1px solid #C0C0C0;
22   -}
\ No newline at end of file
  22 + min-height: 50px;
  23 + border: 1px solid #c0c0c0;
  24 +}
... ...
... ... @@ -13,17 +13,21 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-audit-logs {
17 18 background-color: #fff;
  19 +
18 20 .tb-audit-log-margin-18px {
19 21 margin-bottom: 18px;
20 22 }
  23 +
21 24 .tb-audit-log-toolbar {
22 25 font-size: 20px;
23 26 }
  27 +
24 28 md-input-container.tb-audit-log-search-input {
25 29 .md-errors-spacer {
26   - min-height: 0px;
  30 + min-height: 0;
27 31 }
28 32 }
29 33 }
... ... @@ -32,27 +36,26 @@
32 36 overflow-x: auto;
33 37 }
34 38
35   -
36   -
37 39 md-list.tb-audit-log-table {
38   - padding: 0px;
39 40 min-width: 700px;
  41 + padding: 0;
  42 +
40 43 &.tb-audit-log-table-full {
41 44 min-width: 900px;
42 45 }
43 46
44 47 md-list-item {
45   - padding: 0px;
  48 + padding: 0;
46 49 }
47 50
48 51 .tb-row {
49 52 height: 48px;
50   - padding: 0px;
  53 + padding: 0;
51 54 overflow: hidden;
52 55 }
53 56
54 57 .tb-row:hover {
55   - background-color: #EEEEEE;
  58 + background-color: #eee;
56 59 }
57 60
58 61 .tb-header:hover {
... ... @@ -61,9 +64,9 @@ md-list.tb-audit-log-table {
61 64
62 65 .tb-header {
63 66 .tb-cell {
64   - color: rgba(0,0,0,.54);
65 67 font-size: 12px;
66 68 font-weight: 700;
  69 + color: rgba(0, 0, 0, .54);
67 70 white-space: nowrap;
68 71 background: none;
69 72 }
... ... @@ -72,11 +75,12 @@ md-list.tb-audit-log-table {
72 75 .tb-cell {
73 76 padding: 0 24px;
74 77 margin: auto 0;
75   - color: rgba(0,0,0,.87);
  78 + overflow: hidden;
76 79 font-size: 13px;
77   - vertical-align: middle;
  80 + color: rgba(0, 0, 0, .87);
78 81 text-align: left;
79   - overflow: hidden;
  82 + vertical-align: middle;
  83 +
80 84 .md-button {
81 85 padding: 0;
82 86 margin: 0;
... ... @@ -86,5 +90,4 @@ md-list.tb-audit-log-table {
86 90 .tb-cell.tb-number {
87 91 text-align: right;
88 92 }
89   -
90 93 }
... ...
... ... @@ -13,16 +13,19 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-dashboard-autocomplete {
17 18 .tb-not-found {
18 19 display: block;
19   - line-height: 1.5;
20 20 height: 48px;
  21 + line-height: 1.5;
21 22 }
  23 +
22 24 .tb-dashboard-item {
23 25 display: block;
24 26 height: 48px;
25 27 }
  28 +
26 29 li {
27 30 height: auto !important;
28 31 white-space: normal !important;
... ...
... ... @@ -13,18 +13,20 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -@import '../../scss/constants';
  16 +@import "../../scss/constants";
17 17
18 18 tb-dashboard-select {
19 19 min-width: 52px;
  20 +
20 21 md-select {
21   - pointer-events: all;
22 22 max-width: 300px;
  23 + pointer-events: all;
23 24 }
24 25 }
25 26
26 27 .tb-dashboard-select {
27 28 min-height: 32px;
  29 +
28 30 span {
29 31 pointer-events: all;
30 32 cursor: pointer;
... ... @@ -38,23 +40,27 @@ tb-dashboard-select {
38 40 }
39 41
40 42 .tb-dashboard-select-panel {
  43 + min-width: 300px;
  44 + max-width: 320px;
41 45 max-height: 150px;
  46 + overflow-x: hidden;
  47 + overflow-y: auto;
  48 + background: #fff;
  49 + border-radius: 4px;
  50 + box-shadow:
  51 + 0 7px 8px -4px rgba(0, 0, 0, .2),
  52 + 0 13px 19px 2px rgba(0, 0, 0, .14),
  53 + 0 5px 24px 4px rgba(0, 0, 0, .12);
  54 +
42 55 @media (min-height: 350px) {
43 56 max-height: 250px;
44 57 }
45   - max-width: 320px;
  58 +
46 59 @media (min-width: $layout-breakpoint-xs) {
47 60 max-width: 100%;
48 61 }
49   - min-width: 300px;
50   - background: white;
51   - border-radius: 4px;
52   - box-shadow: 0 7px 8px -4px rgba(0, 0, 0, 0.2),
53   - 0 13px 19px 2px rgba(0, 0, 0, 0.14),
54   - 0 5px 24px 4px rgba(0, 0, 0, 0.12);
55   - overflow-x: hidden;
56   - overflow-y: auto;
  62 +
57 63 md-content {
58 64 background-color: #fff;
59 65 }
60   -}
\ No newline at end of file
  66 +}
... ...
... ... @@ -21,6 +21,7 @@ div.tb-widget {
21 21 margin: 0;
22 22 overflow: hidden;
23 23 outline: none;
  24 +
24 25 @include transition(all .2s ease-in-out);
25 26
26 27 .tb-widget-title {
... ... @@ -32,7 +33,7 @@ div.tb-widget {
32 33
33 34 tb-timewindow {
34 35 font-size: 14px;
35   - opacity: 0.85;
  36 + opacity: .85;
36 37 }
37 38 }
38 39
... ... @@ -44,17 +45,19 @@ div.tb-widget {
44 45 margin: 0;
45 46
46 47 .md-button.md-icon-button {
47   - margin: 0 !important;
48   - padding: 0 !important;
49   - line-height: 20px;
50 48 width: 32px;
51   - height: 32px;
52 49 min-width: 32px;
  50 + height: 32px;
53 51 min-height: 32px;
54   - md-icon, ng-md-icon {
  52 + padding: 0 !important;
  53 + margin: 0 !important;
  54 + line-height: 20px;
  55 +
  56 + md-icon,
  57 + ng-md-icon {
55 58 width: 20px;
56   - height: 20px;
57 59 min-width: 20px;
  60 + height: 20px;
58 61 min-height: 20px;
59 62 font-size: 20px;
60 63 }
... ... @@ -63,8 +66,8 @@ div.tb-widget {
63 66
64 67 .tb-widget-content {
65 68 tb-widget {
66   - width: 100%;
67 69 position: relative;
  70 + width: 100%;
68 71 }
69 72 }
70 73 }
... ... @@ -75,40 +78,41 @@ div.tb-widget.tb-highlighted {
75 78 }
76 79
77 80 div.tb-widget.tb-not-highlighted {
78   - opacity: 0.5;
  81 + opacity: .5;
79 82 }
80 83
81 84 tb-dashboard {
82 85 position: absolute;
83 86 top: 0;
84   - left: 0;
85 87 right: 0;
86 88 bottom: 0;
  89 + left: 0;
87 90 }
88 91
89 92 md-content.tb-dashboard-content {
90 93 position: absolute;
91 94 top: 0;
92   - left: 0;
93 95 right: 0;
94 96 bottom: 0;
95   - outline: none;
  97 + left: 0;
96 98 background: none;
  99 + outline: none;
  100 +
97 101 .gridster-item {
98   - @include transition(none);
  102 + @include transition(none);
99 103 }
100 104 }
101 105
102 106 .tb-widget-error-container {
103 107 position: absolute;
104   - background-color: #fff;
105 108 width: 100%;
106 109 height: 100%;
  110 + background-color: #fff;
107 111 }
108 112
109 113 .tb-widget-error-msg {
110   - color: red;
  114 + padding: 5px;
111 115 font-size: 16px;
  116 + color: #f00;
112 117 word-wrap: break-word;
113   - padding: 5px;
114 118 }
... ...
... ... @@ -13,9 +13,11 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-datakey-config {
17 18 min-width: 500px !important;
18 19 min-height: 500px !important;
  20 +
19 21 md-content {
20 22 background-color: #fff;
21 23 }
... ...
... ... @@ -13,17 +13,22 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -@import '../../scss/constants';
  16 +@import "../../scss/constants";
17 17
18   -.tb-entity-alias-autocomplete, .tb-timeseries-datakey-autocomplete, .tb-attribute-datakey-autocomplete, .tb-alarm-datakey-autocomplete {
  18 +.tb-entity-alias-autocomplete,
  19 +.tb-timeseries-datakey-autocomplete,
  20 +.tb-attribute-datakey-autocomplete,
  21 +.tb-alarm-datakey-autocomplete {
19 22 .tb-not-found {
20 23 display: block;
21   - line-height: 1.5;
22 24 height: 48px;
  25 + line-height: 1.5;
  26 +
23 27 .tb-no-entries {
24 28 line-height: 48px;
25 29 }
26 30 }
  31 +
27 32 li {
28 33 height: auto !important;
29 34 white-space: normal !important;
... ... @@ -32,13 +37,14 @@
32 37
33 38 tb-datasource-entity {
34 39 @media (min-width: $layout-breakpoint-sm) {
35   - padding-left: 4px;
36 40 padding-right: 4px;
  41 + padding-left: 4px;
37 42 }
  43 +
38 44 tb-entity-alias-select {
39 45 @media (min-width: $layout-breakpoint-sm) {
40 46 width: 200px;
41 47 max-width: 200px;
42 48 }
43 49 }
44   -}
\ No newline at end of file
  50 +}
... ...
... ... @@ -13,7 +13,7 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -@import '../../scss/constants';
  16 +@import "../../scss/constants";
17 17
18 18 .tb-datasource-func {
19 19 @media (min-width: $layout-breakpoint-sm) {
... ... @@ -22,19 +22,22 @@
22 22
23 23 md-input-container.tb-datasource-name {
24 24 .md-errors-spacer {
25   - display: none;
  25 + display: none;
26 26 }
27 27 }
28 28
29   - .tb-func-datakey-autocomplete, .tb-alarm-datakey-autocomplete {
  29 + .tb-func-datakey-autocomplete,
  30 + .tb-alarm-datakey-autocomplete {
30 31 .tb-not-found {
31 32 display: block;
32   - line-height: 1.5;
33 33 height: 48px;
  34 + line-height: 1.5;
  35 +
34 36 .tb-no-entries {
35 37 line-height: 48px;
36 38 }
37 39 }
  40 +
38 41 li {
39 42 height: auto !important;
40 43 white-space: normal !important;
... ...
... ... @@ -13,39 +13,43 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-datasource {
17   - #entity-autocomplete {
18   - height: 30px;
19   - margin-top: 18px;
20   - md-autocomplete-wrap {
21   - height: 30px;
22   - }
23   - input, input:not(.md-input) {
24   - height: 30px;
25   - }
26   - }
27   - #datasourceType {
  18 + #entity-autocomplete {
  19 + height: 30px;
  20 + margin-top: 18px;
  21 +
  22 + md-autocomplete-wrap {
  23 + height: 30px;
  24 + }
  25 +
  26 + input,
  27 + input:not(.md-input) {
  28 + height: 30px;
  29 + }
28 30 }
29 31 }
30 32
31 33 @mixin tb-checkered-bg() {
32 34 background-color: #fff;
33   - background-image: linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%, #ddd),
34   - linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%, #ddd);
35   - background-size: 8px 8px;
  35 + background-image:
  36 + linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%, #ddd),
  37 + linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%, #ddd);
36 38 background-position: 0 0, 4px 4px;
  39 + background-size: 8px 8px;
37 40 }
38 41
39 42 .tb-color-preview {
40   - content: '';
41   - min-width: 24px;
  43 + position: relative;
42 44 width: 24px;
  45 + min-width: 24px;
43 46 height: 24px;
  47 + overflow: hidden;
  48 + content: "";
44 49 border: 2px solid #fff;
45 50 border-radius: 50%;
46 51 box-shadow: 0 3px 1px -2px rgba(0, 0, 0, .14), 0 2px 2px 0 rgba(0, 0, 0, .098), 0 1px 5px 0 rgba(0, 0, 0, .084);
47   - position: relative;
48   - overflow: hidden;
  52 +
49 53 @include tb-checkered-bg();
50 54
51 55 .tb-color-result {
... ... @@ -60,6 +64,7 @@
60 64 text-overflow: ellipsis;
61 65 white-space: nowrap;
62 66 }
  67 +
63 68 .tb-chip-separator {
64 69 white-space: pre;
65 70 }
... ...
... ... @@ -13,13 +13,16 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 tb-datetime-period {
17 18 md-input-container {
18   - margin-bottom: 0px;
  19 + margin-bottom: 0;
  20 +
19 21 .md-errors-spacer {
20   - min-height: 0px;
  22 + min-height: 0;
21 23 }
22 24 }
  25 +
23 26 mdp-date-picker {
24 27 .md-input {
25 28 width: 150px !important;
... ...
... ... @@ -13,47 +13,51 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -@import '../../scss/constants';
  16 +@import "../../scss/constants";
17 17
18 18 .tb-details-title {
19   - font-size: 1.000rem;
20   - @media (min-width: $layout-breakpoint-gt-sm) {
21   - font-size: 1.600rem;
22   - }
23   - font-weight: 400;
24   - text-transform: uppercase;
  19 + width: inherit;
25 20 margin: 20px 8px 0 0;
26 21 overflow: hidden;
  22 + font-size: 1rem;
  23 + font-weight: 400;
27 24 text-overflow: ellipsis;
  25 + text-transform: uppercase;
28 26 white-space: nowrap;
29   - width: inherit;
  27 +
  28 + @media (min-width: $layout-breakpoint-gt-sm) {
  29 + font-size: 1.6rem;
  30 + }
30 31 }
31 32
32 33 .tb-details-subtitle {
33   - font-size: 1.000rem;
  34 + width: inherit;
34 35 margin: 10px 0;
35   - opacity: 0.8;
36 36 overflow: hidden;
  37 + font-size: 1rem;
37 38 text-overflow: ellipsis;
38 39 white-space: nowrap;
39   - width: inherit;
  40 + opacity: .8;
40 41 }
41 42
42 43 md-sidenav.tb-sidenav-details {
43 44 .md-toolbar-tools {
44   - min-height: 100px;
45   - max-height: 120px;
46   - height: 100%;
  45 + height: 100%;
  46 + min-height: 100px;
  47 + max-height: 120px;
47 48 }
  49 + z-index: 59 !important;
48 50 width: 100% !important;
49 51 max-width: 100% !important;
50   - z-index: 59 !important;
  52 +
51 53 @media (min-width: $layout-breakpoint-gt-sm) {
52 54 width: 80% !important;
53 55 }
  56 +
54 57 @media (min-width: $layout-breakpoint-gt-md) {
55 58 width: 65% !important;
56 59 }
  60 +
57 61 tb-dashboard {
58 62 md-content {
59 63 background-color: $primary-hue-3;
... ...
... ... @@ -13,15 +13,18 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-entity-alias-autocomplete {
17 18 .tb-not-found {
18 19 display: block;
19   - line-height: 1.5;
20 20 height: 48px;
  21 + line-height: 1.5;
  22 +
21 23 .tb-no-entries {
22 24 line-height: 48px;
23 25 }
24 26 }
  27 +
25 28 li {
26 29 height: auto !important;
27 30 white-space: normal !important;
... ...
... ... @@ -16,37 +16,38 @@
16 16 @import "../../scss/constants";
17 17
18 18 .tb-fullscreen {
19   - section.header-buttons {
20   - top: 25px;
21   - }
22   -}
23   -
24   -.tb-fullscreen {
25   - width: 100% !important;
26   - height: 100% !important;
27 19 position: fixed !important;
28 20 top: 0;
29 21 left: 0;
  22 + width: 100% !important;
  23 + height: 100% !important;
  24 +
  25 + section.header-buttons {
  26 + top: 25px;
  27 + }
30 28 }
31 29
32 30 .tb-fullscreen-parent {
33   - background-color: $gray;
34   - width: 100%;
35   - height: 100%;
36 31 position: fixed;
37 32 top: 0;
38 33 left: 0;
  34 + width: 100%;
  35 + height: 100%;
  36 + background-color: $gray;
39 37 }
40 38
41   -.md-button.tb-fullscreen-button-style, .tb-fullscreen-button-style {
  39 +.md-button.tb-fullscreen-button-style,
  40 +.tb-fullscreen-button-style {
42 41 background: #ccc;
43   - opacity: 0.85;
  42 + opacity: .85;
  43 +
44 44 ng-md-icon {
45 45 color: #666;
46 46 }
47 47 }
48 48
49   -.md-button.tb-fullscreen-button-pos, .tb-fullscreen-button-pos {
  49 +.md-button.tb-fullscreen-button-pos,
  50 +.tb-fullscreen-button-pos {
50 51 position: absolute;
51 52 top: 10px;
52 53 right: 10px;
... ...
... ... @@ -21,15 +21,19 @@
21 21
22 22 .tb-card-item {
23 23 @include transition(all .2s ease-in-out);
  24 +
24 25 md-card-content {
25   - padding-top: 0px;
26 26 max-height: 53px;
  27 + padding-top: 0;
27 28 }
  29 +
28 30 md-card-title {
29 31 width: 100%;
  32 +
30 33 md-card-title-text {
31   - max-height: 32px;
32 34 min-width: 50%;
  35 + max-height: 32px;
  36 +
33 37 .md-headline {
34 38 overflow: hidden;
35 39 text-overflow: ellipsis;
... ... @@ -40,14 +44,15 @@
40 44 }
41 45
42 46 .tb-current-item {
43   - opacity: 0.5;
  47 + opacity: .5;
  48 +
44 49 @include transform(scale(1.05));
45 50 }
46 51
47 52 #tb-vertical-container {
48 53 position: absolute;
49 54 top: 0;
50   - left: 0;
51 55 right: 0;
52 56 bottom: 0;
  57 + left: 0;
53 58 }
... ...
... ... @@ -13,11 +13,14 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 tb-js-func {
17 18 position: relative;
  19 +
18 20 .tb-disabled {
19   - color: rgba(0,0,0,0.38);
  21 + color: rgba(0, 0, 0, .38);
20 22 }
  23 +
21 24 .fill-height {
22 25 height: 100%;
23 26 }
... ... @@ -25,25 +28,27 @@ tb-js-func {
25 28
26 29 .tb-js-func-toolbar {
27 30 .md-button.tidy {
28   - color: #7B7B7B;
29 31 min-width: 32px;
30 32 min-height: 15px;
31   - line-height: 15px;
32   - font-size: 0.800rem;
33   - margin: 0 5px 0 0;
34 33 padding: 4px;
35   - background: rgba(220, 220, 220, 0.35);
  34 + margin: 0 5px 0 0;
  35 + font-size: .8rem;
  36 + line-height: 15px;
  37 + color: #7b7b7b;
  38 + background: rgba(220, 220, 220, .35);
36 39 }
37 40 }
38 41
39 42 .tb-js-func-panel {
40   - margin-left: 15px;
41   - border: 1px solid #C0C0C0;
42 43 height: calc(100% - 80px);
  44 + margin-left: 15px;
  45 + border: 1px solid #c0c0c0;
  46 +
43 47 #tb-javascript-input {
44   - min-width: 200px;
45 48 width: 100%;
  49 + min-width: 200px;
46 50 height: 100%;
  51 +
47 52 &:not(.fill-height) {
48 53 min-height: 200px;
49 54 }
... ...
... ... @@ -13,8 +13,10 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 tb-json-content {
17 18 position: relative;
  19 +
18 20 .fill-height {
19 21 height: 100%;
20 22 }
... ... @@ -22,25 +24,27 @@ tb-json-content {
22 24
23 25 .tb-json-content-toolbar {
24 26 .md-button.tidy {
25   - color: #7B7B7B;
26 27 min-width: 32px;
27 28 min-height: 15px;
28   - line-height: 15px;
29   - font-size: 0.800rem;
30   - margin: 0 5px 0 0;
31 29 padding: 4px;
32   - background: rgba(220, 220, 220, 0.35);
  30 + margin: 0 5px 0 0;
  31 + font-size: .8rem;
  32 + line-height: 15px;
  33 + color: #7b7b7b;
  34 + background: rgba(220, 220, 220, .35);
33 35 }
34 36 }
35 37
36 38 .tb-json-content-panel {
37   - margin-left: 15px;
38   - border: 1px solid #C0C0C0;
39 39 height: 100%;
  40 + margin-left: 15px;
  41 + border: 1px solid #c0c0c0;
  42 +
40 43 #tb-json-input {
41   - min-width: 200px;
42 44 width: 100%;
  45 + min-width: 200px;
43 46 height: 100%;
  47 +
44 48 &:not(.fill-height) {
45 49 min-height: 200px;
46 50 }
... ...
... ... @@ -13,7 +13,8 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 tb-json-form {
17   - overflow: auto;
18 18 padding-bottom: 14px !important;
19   -}
\ No newline at end of file
  19 + overflow: auto;
  20 +}
... ...
... ... @@ -13,21 +13,25 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 tb-json-object-edit {
17 18 position: relative;
  19 +
18 20 .fill-height {
19 21 height: 100%;
20 22 }
21 23 }
22 24
23 25 .tb-json-object-panel {
24   - margin-left: 15px;
25   - border: 1px solid #C0C0C0;
26 26 height: 100%;
  27 + margin-left: 15px;
  28 + border: 1px solid #c0c0c0;
  29 +
27 30 #tb-json-input {
28   - min-width: 200px;
29 31 width: 100%;
  32 + min-width: 200px;
30 33 height: 100%;
  34 +
31 35 &:not(.fill-height) {
32 36 min-height: 200px;
33 37 }
... ...
... ... @@ -13,14 +13,16 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-kv-map {
17 18 span.no-data-found {
18 19 position: relative;
  20 + display: flex;
19 21 height: 40px;
20 22 text-transform: uppercase;
21   - display: flex;
  23 +
22 24 &.disabled {
23   - color: rgba(0,0,0,0.38);
  25 + color: rgba(0, 0, 0, .38);
24 26 }
25 27 }
26   -}
\ No newline at end of file
  28 +}
... ...
... ... @@ -13,6 +13,7 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .md-panel {
17 18 &.tb-legend-config-panel {
18 19 position: absolute;
... ... @@ -20,21 +21,26 @@
20 21 }
21 22
22 23 .tb-legend-config-panel {
23   - max-height: 220px;
24 24 min-width: 220px;
25   - background: white;
26   - border-radius: 4px;
27   - box-shadow: 0 7px 8px -4px rgba(0, 0, 0, 0.2),
28   - 0 13px 19px 2px rgba(0, 0, 0, 0.14),
29   - 0 5px 24px 4px rgba(0, 0, 0, 0.12);
  25 + max-height: 220px;
30 26 overflow: hidden;
31   - form, fieldset {
  27 + background: #fff;
  28 + border-radius: 4px;
  29 + box-shadow:
  30 + 0 7px 8px -4px rgba(0, 0, 0, .2),
  31 + 0 13px 19px 2px rgba(0, 0, 0, .14),
  32 + 0 5px 24px 4px rgba(0, 0, 0, .12);
  33 +
  34 + form,
  35 + fieldset {
32 36 height: 100%;
33 37 }
  38 +
34 39 md-content {
35   - background-color: #fff;
36 40 overflow: hidden;
  41 + background-color: #fff;
37 42 }
  43 +
38 44 .md-padding {
39 45 padding: 0 16px;
40 46 }
... ...
... ... @@ -13,39 +13,49 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 table.tb-legend {
17 18 width: 100%;
18 19 font-size: 12px;
19   - .tb-legend-header, .tb-legend-value {
20   - text-align: right;
  20 +
  21 + .tb-legend-header,
  22 + .tb-legend-value {
  23 + text-align: right;
21 24 }
  25 +
22 26 .tb-legend-header {
23 27 th {
24   - color: rgb(255,110,64);
25   - white-space: nowrap;
26 28 padding: 0 10px 1px 0;
  29 + color: rgb(255, 110, 64);
  30 + white-space: nowrap;
27 31 }
28 32 }
  33 +
29 34 .tb-legend-keys {
30   - td.tb-legend-label, td.tb-legend-value {
31   - white-space: nowrap;
  35 + td.tb-legend-label,
  36 + td.tb-legend-value {
32 37 padding: 2px 10px;
  38 + white-space: nowrap;
33 39 }
  40 +
34 41 .tb-legend-line {
  42 + display: inline-block;
35 43 width: 15px;
36 44 height: 3px;
37   - display: inline-block;
38 45 vertical-align: middle;
39 46 }
  47 +
40 48 .tb-legend-label {
41 49 text-align: left;
42 50 outline: none;
  51 +
43 52 &.tb-horizontal {
44 53 width: 95%;
45 54 }
  55 +
46 56 &.tb-hidden-label {
47 57 text-decoration: line-through;
48   - opacity: 0.6;
  58 + opacity: .6;
49 59 }
50 60 }
51 61 }
... ...
... ... @@ -13,14 +13,16 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-material-icon-select {
17   - md-icon {
18   - padding: 4px;
19   - margin: 8px 4px 4px;
20   - cursor: pointer;
21   - border: solid 1px rgba(0,0,0,0.27);
22   - }
23   - md-input-container {
24   - margin-bottom: 0px;
25   - }
26   -}
\ No newline at end of file
  18 + md-icon {
  19 + padding: 4px;
  20 + margin: 8px 4px 4px;
  21 + cursor: pointer;
  22 + border: solid 1px rgba(0, 0, 0, .27);
  23 + }
  24 +
  25 + md-input-container {
  26 + margin-bottom: 0;
  27 + }
  28 +}
... ...
... ... @@ -13,18 +13,20 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-material-icons-dialog {
17 18 button.md-icon-button.tb-select-icon-button {
18   - border: solid 1px orange;
19   - border-radius: 0%;
20   - padding: 16px;
21   - height: 56px;
22 19 width: 56px;
  20 + height: 56px;
  21 + padding: 16px;
23 22 margin: 10px;
  23 + border: solid 1px #ffa500;
  24 + border-radius: 0%;
24 25 }
  26 +
25 27 .tb-icons-load {
26 28 top: 64px;
27   - background: rgba(255,255,255,0.75);
28 29 z-index: 3;
  30 + background: rgba(255, 255, 255, .75);
29 31 }
30   -}
\ No newline at end of file
  32 +}
... ...
... ... @@ -24,9 +24,11 @@
24 24 }
25 25
26 26 .tb-menu-toggle-list {
27   - overflow: hidden;
28 27 position: relative;
29 28 z-index: 1;
30   - @include transition(0.75s cubic-bezier(0.35, 0, 0.25, 1));
  29 + overflow: hidden;
  30 +
  31 + @include transition(.75s cubic-bezier(.35, 0, .25, 1));
  32 +
31 33 @include transition-property(height);
32 34 }
... ...
... ... @@ -13,30 +13,35 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .json-form-ace-editor {
17   - position: relative;
18   - border: 1px solid #C0C0C0;
19   - height: 100%;
20   - .title-panel {
21   - position: absolute;
22   - font-size: 0.800rem;
23   - font-weight: 500;
24   - top: 10px;
25   - right: 20px;
26   - z-index: 5;
27   - label {
28   - color: #00acc1;
29   - background: rgba(220, 220, 220, 0.35);
30   - border-radius: 5px;
31   - padding: 4px;
32   - text-transform: uppercase;
33   - }
34   - button.tidy-button {
35   - background: rgba(220, 220, 220, 0.35) !important;
36   - span {
37   - padding: 0px !important;
38   - font-size: 12px !important;
39   - }
40   - }
  18 + position: relative;
  19 + height: 100%;
  20 + border: 1px solid #c0c0c0;
  21 +
  22 + .title-panel {
  23 + position: absolute;
  24 + top: 10px;
  25 + right: 20px;
  26 + z-index: 5;
  27 + font-size: .8rem;
  28 + font-weight: 500;
  29 +
  30 + label {
  31 + padding: 4px;
  32 + color: #00acc1;
  33 + text-transform: uppercase;
  34 + background: rgba(220, 220, 220, .35);
  35 + border-radius: 5px;
41 36 }
42   -}
\ No newline at end of file
  37 +
  38 + button.tidy-button {
  39 + background: rgba(220, 220, 220, .35) !important;
  40 +
  41 + span {
  42 + padding: 0 !important;
  43 + font-size: 12px !important;
  44 + }
  45 + }
  46 + }
  47 +}
... ...
... ... @@ -15,21 +15,23 @@
15 15 */
16 16 @mixin tb-checkered-bg() {
17 17 background-color: #fff;
18   - background-image: linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%, #ddd),
19   - linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%, #ddd);
20   - background-size: 8px 8px;
  18 + background-image:
  19 + linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%, #ddd),
  20 + linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%, #ddd);
21 21 background-position: 0 0, 4px 4px;
  22 + background-size: 8px 8px;
22 23 }
23 24
24 25 .tb-color-preview {
25   - content: '';
  26 + position: relative;
26 27 width: 24px;
27 28 height: 24px;
  29 + overflow: hidden;
  30 + content: "";
28 31 border: 2px solid #fff;
29 32 border-radius: 50%;
30 33 box-shadow: 0 3px 1px -2px rgba(0, 0, 0, .14), 0 2px 2px 0 rgba(0, 0, 0, .098), 0 1px 5px 0 rgba(0, 0, 0, .084);
31   - position: relative;
32   - overflow: hidden;
  34 +
33 35 @include tb-checkered-bg();
34 36
35 37 .tb-color-result {
... ...
... ... @@ -13,66 +13,69 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -$previewSize: 100px;
  16 +$previewSize: 100px !default;
17 17
18 18 .tb-image-select-container {
19 19 position: relative;
20   - height: $previewSize;
21 20 width: 100%;
  21 + height: $previewSize;
22 22 }
23 23
24 24 .tb-image-preview {
25   - max-width: $previewSize;
26   - max-height: $previewSize;
27   - width: 100%;
28   - height: 100%;
  25 + width: 100%;
  26 + max-width: $previewSize;
  27 + height: 100%;
  28 + max-height: $previewSize;
29 29 }
30 30
31 31 .tb-image-preview-container {
32   - position: relative;
33   - width: $previewSize;
34   - height: $previewSize;
35   - margin-right: 12px;
36   - border: solid 1px;
37   - vertical-align: top;
38   - float: left;
39   - div {
40   - width: 100%;
41   - font-size: 18px;
42   - text-align: center;
43   - position: absolute;
44   - top: 50%;
45   - left: 50%;
46   - transform: translate(-50%,-50%);
47   - }
  32 + position: relative;
  33 + float: left;
  34 + width: $previewSize;
  35 + height: $previewSize;
  36 + margin-right: 12px;
  37 + vertical-align: top;
  38 + border: solid 1px;
  39 +
  40 + div {
  41 + position: absolute;
  42 + top: 50%;
  43 + left: 50%;
  44 + width: 100%;
  45 + font-size: 18px;
  46 + text-align: center;
  47 + transform: translate(-50%, -50%);
  48 + }
48 49 }
49 50
50 51 .tb-dropzone {
51   - position: relative;
52   - border: dashed 2px;
53   - height: $previewSize;
54   - vertical-align: top;
55   - padding: 0 8px;
56   - overflow: hidden;
57   - div {
58   - width: 100%;
59   - font-size: 24px;
60   - text-align: center;
61   - position: absolute;
62   - top: 50%;
63   - left: 50%;
64   - transform: translate(-50%,-50%);
65   - }
  52 + position: relative;
  53 + height: $previewSize;
  54 + padding: 0 8px;
  55 + overflow: hidden;
  56 + vertical-align: top;
  57 + border: dashed 2px;
  58 +
  59 + div {
  60 + position: absolute;
  61 + top: 50%;
  62 + left: 50%;
  63 + width: 100%;
  64 + font-size: 24px;
  65 + text-align: center;
  66 + transform: translate(-50%, -50%);
  67 + }
66 68 }
67 69
68 70 .tb-image-clear-container {
69   - width: 48px;
70   - height: $previewSize;
71   - position: relative;
72   - float: right;
  71 + position: relative;
  72 + float: right;
  73 + width: 48px;
  74 + height: $previewSize;
73 75 }
  76 +
74 77 .tb-image-clear-btn {
75   - position: absolute !important;
76   - top: 50%;
77   - transform: translate(0%,-50%) !important;
  78 + position: absolute !important;
  79 + top: 50%;
  80 + transform: translate(0%, -50%) !important;
78 81 }
... ...
... ... @@ -15,87 +15,92 @@
15 15 */
16 16 @import "~compass-sass-mixins/lib/compass";
17 17
18   -$swift-ease-out-duration: 0.4s !default;
19   -$swift-ease-out-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1) !default;
  18 +$swift-ease-out-duration: .4s !default;
  19 +$swift-ease-out-timing-function: cubic-bezier(.25, .8, .25, 1) !default;
20 20
21 21 $input-label-float-offset: 6px !default;
22   -$input-label-float-scale: 0.75 !default;
  22 +$input-label-float-scale: .75 !default;
23 23
24 24 .json-form-error {
25   - position: relative;
26   - bottom: -5px;
27   - font-size: 12px;
28   - line-height: 12px;
29   - color: rgb(244, 67, 54);
30   - @include transition(all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms);
  25 + position: relative;
  26 + bottom: -5px;
  27 + font-size: 12px;
  28 + line-height: 12px;
  29 + color: rgb(244, 67, 54);
  30 +
  31 + @include transition(all 450ms cubic-bezier(.23, 1, .32, 1) 0ms);
31 32 }
32 33
33 34 .tb-container {
34   - position: relative;
35   - margin-top: 32px;
36   - padding: 10px 0;
  35 + position: relative;
  36 + padding: 10px 0;
  37 + margin-top: 32px;
37 38 }
38 39
39 40 .tb-field {
40   - &.tb-required {
41   - label:after {
42   - content: ' *';
43   - font-size: 13px;
44   - vertical-align: top;
45   - color: rgba(0,0,0,0.54);
46   - }
  41 + &.tb-required {
  42 + label::after {
  43 + font-size: 13px;
  44 + color: rgba(0, 0, 0, .54);
  45 + vertical-align: top;
  46 + content: " *";
47 47 }
48   - &.tb-focused:not(.tb-readonly) {
49   - label:after {
50   - color: rgb(221,44,0);
51   - }
  48 + }
  49 +
  50 + &.tb-focused:not(.tb-readonly) {
  51 + label::after {
  52 + color: rgb(221, 44, 0);
52 53 }
  54 + }
53 55 }
54 56
55 57 .tb-date-field {
56   - &.tb-required {
57   - div>div:first-child:after {
58   - content: ' *';
59   - font-size: 13px;
60   - vertical-align: top;
61   - color: rgba(0,0,0,0.54);
62   - }
  58 + &.tb-required {
  59 + div > div:first-child::after {
  60 + font-size: 13px;
  61 + color: rgba(0, 0, 0, .54);
  62 + vertical-align: top;
  63 + content: " *";
63 64 }
64   - &.tb-focused:not(.tb-readonly) {
65   - div>div:first-child:after {
66   - color: rgb(221,44,0);
67   - }
  65 + }
  66 +
  67 + &.tb-focused:not(.tb-readonly) {
  68 + div > div:first-child::after {
  69 + color: rgb(221, 44, 0);
68 70 }
  71 + }
69 72 }
70 73
71 74 label.tb-label {
72   - color: rgba(0,0,0,0.54);
73   - -webkit-font-smoothing: antialiased;
74   - position: absolute;
75   - bottom: 100%;
76   - left: 0;
77   - right: auto;
78   - @include transform(translate3d(0, $input-label-float-offset, 0) scale($input-label-float-scale));
79   - @include transition(transform $swift-ease-out-timing-function $swift-ease-out-duration,
80   - width $swift-ease-out-timing-function $swift-ease-out-duration);
81   - transform-origin: left top;
82   -
83   - &.tb-focused {
84   - color: rgb(96,125,139);
85   - }
  75 + position: absolute;
  76 + right: auto;
  77 + bottom: 100%;
  78 + left: 0;
  79 + color: rgba(0, 0, 0, .54);
  80 + transform-origin: left top;
  81 + -webkit-font-smoothing: antialiased;
86 82
87   - &.tb-required:after {
88   - content: ' *';
89   - font-size: 13px;
90   - vertical-align: top;
91   - color: rgba(0,0,0,0.54);
92   - }
  83 + @include transform(translate3d(0, $input-label-float-offset, 0) scale($input-label-float-scale));
93 84
94   - &.tb-focused:not(.tb-readonly):after {
95   - color: rgb(221,44,0);
96   - }
  85 + @include transition(transform $swift-ease-out-timing-function $swift-ease-out-duration,
  86 + width $swift-ease-out-timing-function $swift-ease-out-duration);
  87 +
  88 + &.tb-focused {
  89 + color: rgb(96, 125, 139);
  90 + }
  91 +
  92 + &.tb-required::after {
  93 + font-size: 13px;
  94 + color: rgba(0, 0, 0, .54);
  95 + vertical-align: top;
  96 + content: " *";
  97 + }
  98 +
  99 + &.tb-focused:not(.tb-readonly)::after {
  100 + color: rgb(221, 44, 0);
  101 + }
97 102 }
98 103
99 104 .tb-head-label {
100   - color: rgba(0,0,0,0.54);
  105 + color: rgba(0, 0, 0, .54);
101 106 }
... ...
... ... @@ -13,16 +13,19 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-related-entity-autocomplete {
17 18 .tb-not-found {
18 19 display: block;
19   - line-height: 1.5;
20 20 height: 48px;
  21 + line-height: 1.5;
21 22 }
  23 +
22 24 .tb-entity-item {
23 25 display: block;
24 26 height: 48px;
25 27 }
  28 +
26 29 li {
27 30 height: auto !important;
28 31 white-space: normal !important;
... ...
... ... @@ -16,21 +16,22 @@
16 16 @import "~compass-sass-mixins/lib/compass";
17 17
18 18 .tb-side-menu .md-button.tb-active {
19   - background-color: rgba(255, 255, 255, 0.15);
20 19 font-weight: 500;
  20 + background-color: rgba(255, 255, 255, .15);
21 21 }
22 22
23   -.tb-side-menu, .tb-side-menu ul {
24   - list-style: none;
  23 +.tb-side-menu,
  24 +.tb-side-menu ul {
25 25 padding: 0;
26 26 margin-top: 0;
  27 + list-style: none;
27 28 }
28 29
29 30 .tb-side-menu .tb-menu-toggle-list a.md-button {
30 31 padding: 0 16px 0 32px;
  32 + font-weight: 500;
31 33 text-transform: none;
32 34 text-rendering: optimizeLegibility;
33   - font-weight: 500;
34 35 }
35 36
36 37 .tb-side-menu .tb-menu-toggle-list .md-button {
... ... @@ -43,36 +44,38 @@
43 44 }
44 45
45 46 .tb-side-menu > li {
46   - border-bottom: 1px solid rgba(0, 0, 0, 0.12);
  47 + border-bottom: 1px solid rgba(0, 0, 0, .12);
47 48 }
48 49
49 50 .tb-side-menu .md-button-toggle .md-toggle-icon {
50   - background-size: 100% auto;
51 51 display: inline-block;
52   - margin: auto 0 auto auto;
53 52 width: 15px;
  53 + margin: auto 0 auto auto;
  54 + background-size: 100% auto;
  55 +
54 56 @include transition(transform .3s, ease-in-out);
55 57 }
56 58
57 59 .tb-side-menu .md-button {
58 60 display: flex;
59   - border-radius: 0;
60   - color: inherit;
61   - cursor: pointer;
62   - line-height: 40px;
63   - margin: 0;
  61 + width: 100%;
64 62 max-height: 40px;
  63 + padding: 0 16px;
  64 + margin: 0;
65 65 overflow: hidden;
66   - padding: 0px 16px;
  66 + line-height: 40px;
  67 + color: inherit;
67 68 text-align: left;
68 69 text-decoration: none;
69   - white-space: nowrap;
70 70 text-overflow: ellipsis;
71   - width: 100%;
  71 + white-space: nowrap;
  72 + cursor: pointer;
  73 + border-radius: 0;
  74 +
72 75 span {
73 76 overflow: hidden;
74   - white-space: nowrap;
75 77 text-overflow: ellipsis;
  78 + white-space: nowrap;
76 79 }
77 80 }
78 81
... ... @@ -83,10 +86,10 @@
83 86
84 87 .tb-side-menu ng-md-icon {
85 88 margin-right: 8px;
86   - margin-left: 0px;
  89 + margin-left: 0;
87 90 }
88 91
89 92 .tb-side-menu md-icon {
90 93 margin-right: 8px;
91   - margin-left: 0px;
  94 + margin-left: 0;
92 95 }
... ...
... ... @@ -13,25 +13,32 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 tb-timeinterval {
17 18 min-width: 355px;
  19 +
18 20 md-input-container {
19   - margin-bottom: 0px;
  21 + margin-bottom: 0;
  22 +
20 23 .md-errors-spacer {
21   - min-height: 0px;
  24 + min-height: 0;
22 25 }
23 26 }
  27 +
24 28 mdp-date-picker {
25 29 .md-input {
26 30 width: 150px;
27 31 }
28 32 }
  33 +
29 34 .md-input {
30 35 width: 70px !important;
31 36 }
  37 +
32 38 .advanced-switch {
33 39 margin-top: 0;
34 40 }
  41 +
35 42 .advanced-label {
36 43 margin: 5px 0;
37 44 }
... ...
... ... @@ -13,6 +13,7 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .md-panel {
17 18 &.tb-timewindow-panel {
18 19 position: absolute;
... ... @@ -20,38 +21,48 @@
20 21 }
21 22
22 23 .tb-timewindow-panel {
23   - max-height: 440px;
24 24 min-width: 417px;
25   - background: white;
26   - border-radius: 4px;
27   - box-shadow: 0 7px 8px -4px rgba(0, 0, 0, 0.2),
28   - 0 13px 19px 2px rgba(0, 0, 0, 0.14),
29   - 0 5px 24px 4px rgba(0, 0, 0, 0.12);
  25 + max-height: 440px;
30 26 overflow: hidden;
31   - form, fieldset {
  27 + background: #fff;
  28 + border-radius: 4px;
  29 + box-shadow:
  30 + 0 7px 8px -4px rgba(0, 0, 0, .2),
  31 + 0 13px 19px 2px rgba(0, 0, 0, .14),
  32 + 0 5px 24px 4px rgba(0, 0, 0, .12);
  33 +
  34 + form,
  35 + fieldset {
32 36 height: 100%;
33 37 }
  38 +
34 39 md-content {
35   - background-color: #fff;
36 40 overflow: hidden;
  41 + background-color: #fff;
37 42 }
  43 +
38 44 .md-padding {
39 45 padding: 0 16px;
40 46 }
  47 +
41 48 .md-radio-interactive {
42   - md-select, md-switch {
  49 + md-select,
  50 + md-switch {
43 51 pointer-events: all;
44 52 }
45 53 }
  54 +
46 55 md-radio-button {
47 56 .md-label {
48 57 width: 100%;
49 58 }
  59 +
50 60 tb-timeinterval {
51 61 width: 355px;
  62 +
52 63 .advanced-switch {
53   - min-height: 30px;
54 64 max-width: 44px;
  65 + min-height: 30px;
55 66 }
56 67 }
57 68 }
... ... @@ -59,15 +70,17 @@
59 70
60 71 tb-timewindow {
61 72 min-width: 52px;
  73 +
62 74 section.tb-timewindow {
63 75 min-height: 32px;
64 76 padding: 0 6px;
  77 +
65 78 span {
66   - pointer-events: all;
67   - cursor: pointer;
68 79 overflow: hidden;
69 80 text-overflow: ellipsis;
70 81 white-space: nowrap;
  82 + pointer-events: all;
  83 + cursor: pointer;
71 84 }
72 85 }
73 86 }
... ...
... ... @@ -13,18 +13,19 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-manage-widget-actions {
17 18 table.md-table {
18 19 tbody {
19 20 tr {
20 21 td {
21 22 &.tb-action-cell {
  23 + width: 100px;
  24 + min-width: 100px;
  25 + max-width: 100px;
22 26 overflow: hidden;
23 27 text-overflow: ellipsis;
24 28 white-space: nowrap;
25   - min-width: 100px;
26   - max-width: 100px;
27   - width: 100px;
28 29 }
29 30 }
30 31 }
... ...
... ... @@ -13,11 +13,13 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-widget-config {
17 18 md-tab-content.md-active > div {
18 19 height: 100%;
19 20 }
  21 +
20 22 .tb-advanced-widget-config {
21 23 height: 100%;
22 24 }
23   -}
\ No newline at end of file
  25 +}
... ...
... ... @@ -13,18 +13,21 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-widget {
17 18 .tb-widget-error {
18 19 display: flex;
19   - justify-content: center;
20 20 align-items: center;
21   - background: rgba(255,255,255,0.5);
  21 + justify-content: center;
  22 + background: rgba(255, 255, 255, .5);
  23 +
22 24 span {
23   - color: red;
  25 + color: #f00;
24 26 }
25 27 }
  28 +
26 29 .tb-widget-loading {
27   - background: rgba(255,255,255,0.15);
28 30 z-index: 3;
  31 + background: rgba(255, 255, 255, .15);
29 32 }
30 33 }
... ...
... ... @@ -17,9 +17,10 @@
17 17
18 18 tb-widgets-bundle-select {
19 19 md-select {
20   - margin: 0;
21 20 padding: 5px 20px;
  21 + margin: 0;
22 22 }
  23 +
23 24 .tb-bundle-item {
24 25 height: 24px;
25 26 line-height: 24px;
... ... @@ -33,24 +34,29 @@ tb-widgets-bundle-select {
33 34 }
34 35 }
35 36
36   -tb-widgets-bundle-select, .tb-widgets-bundle-select {
  37 +tb-widgets-bundle-select,
  38 +.tb-widgets-bundle-select {
37 39 .md-text {
38 40 display: block;
39 41 width: 100%;
40 42 }
  43 +
41 44 .tb-bundle-item {
42 45 display: inline-block;
43 46 width: 100%;
  47 +
44 48 span {
45 49 display: inline-block;
46 50 vertical-align: middle;
47 51 }
  52 +
48 53 .tb-bundle-system {
49   - font-size: 0.8rem;
50   - opacity: 0.8;
51 54 float: right;
  55 + font-size: .8rem;
  56 + opacity: .8;
52 57 }
53 58 }
  59 +
54 60 md-option {
55 61 height: auto !important;
56 62 white-space: normal !important;
... ... @@ -60,19 +66,23 @@ tb-widgets-bundle-select, .tb-widgets-bundle-select {
60 66 md-toolbar {
61 67 tb-widgets-bundle-select {
62 68 md-select {
63   - background: rgba(255,255,255,0.2);
  69 + background: rgba(255, 255, 255, .2);
  70 +
64 71 .md-select-value {
65   - color: #fff;
66 72 font-size: 1.2rem;
67   - span:first-child:after {
68   - color: #fff;
  73 + color: #fff;
  74 +
  75 + span:first-child::after {
  76 + color: #fff;
69 77 }
70 78 }
  79 +
71 80 .md-select-value.md-select-placeholder {
72 81 color: #fff;
73   - opacity: 0.8;
  82 + opacity: .8;
74 83 }
75 84 }
  85 +
76 86 md-select.ng-invalid.ng-touched {
77 87 .md-select-value {
78 88 color: #fff !important;
... ...
... ... @@ -13,14 +13,15 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-dashboard-assigned-customers {
17 18 display: block;
18 19 display: -webkit-box;
19 20 height: 34px;
  21 + margin-bottom: 4px;
20 22 overflow: hidden;
21 23 text-overflow: ellipsis;
22 24 -webkit-line-clamp: 2;
23 25 -webkit-box-orient: vertical;
24   - margin-bottom: 4px;
25 26 }
26 27
... ...
... ... @@ -13,50 +13,54 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -$previewSize: 100px;
  16 +$previewSize: 100px !default;
17 17
18 18 .tb-image-select-container {
19 19 position: relative;
20   - height: $previewSize;
21 20 width: 100%;
  21 + height: $previewSize;
22 22 }
23 23
24 24 .tb-image-preview {
25   - max-width: $previewSize;
26   - max-height: $previewSize;
27 25 width: auto;
  26 + max-width: $previewSize;
28 27 height: auto;
  28 + max-height: $previewSize;
29 29 }
30 30
31 31 .tb-image-preview-container {
32 32 position: relative;
  33 + float: left;
33 34 width: $previewSize;
34 35 height: $previewSize;
35 36 margin-right: 12px;
36   - border: solid 1px;
37 37 vertical-align: top;
38   - float: left;
  38 + border: solid 1px;
  39 +
39 40 div {
40 41 width: 100%;
41 42 font-size: 18px;
42 43 text-align: center;
43 44 }
44   - div, .tb-image-preview {
  45 +
  46 + div,
  47 + .tb-image-preview {
45 48 position: absolute;
46 49 top: 50%;
47 50 left: 50%;
48   - transform: translate(-50%,-50%);
  51 + transform: translate(-50%, -50%);
49 52 }
50 53 }
51 54
52 55 .tb-image-clear-container {
53   - width: 48px;
54   - height: $previewSize;
55 56 position: relative;
56 57 float: right;
  58 + width: 48px;
  59 + height: $previewSize;
57 60 }
  61 +
58 62 .tb-image-clear-btn {
59 63 position: absolute !important;
60 64 top: 50%;
61   - transform: translate(0%,-50%) !important;
  65 + transform: translate(0%, -50%) !important;
62 66 }
... ...
... ... @@ -14,13 +14,14 @@
14 14 * limitations under the License.
15 15 */
16 16 @import "~compass-sass-mixins/lib/compass";
17   -@import '../../scss/constants';
18 17
19   -$toolbar-height: 50px;
20   -$fullscreen-toolbar-height: 64px;
21   -$mobile-toolbar-height: 80px;
22   -$half-mobile-toolbar-height: 40px;
23   -$mobile-toolbar-height-total: 84px;
  18 +@import "../../scss/constants";
  19 +
  20 +$toolbar-height: 50px !default;
  21 +$fullscreen-toolbar-height: 64px !default;
  22 +$mobile-toolbar-height: 80px !default;
  23 +$half-mobile-toolbar-height: 40px !default;
  24 +$mobile-toolbar-height-total: 84px !default;
24 25
25 26 tb-dashboard-toolbar {
26 27 md-fab-toolbar {
... ... @@ -29,126 +30,156 @@ tb-dashboard-toolbar {
29 30 .md-button {
30 31 &.md-fab {
31 32 opacity: 1;
  33 +
32 34 @include transition(opacity .3s cubic-bezier(.55,0,.55,.2));
  35 +
33 36 .md-fab-toolbar-background {
34   - background-color: $primary-default !important;
  37 + background-color: $primary-default !important;
35 38 }
36 39 }
37 40 }
38 41 }
39 42 }
  43 +
40 44 md-fab-trigger {
41 45 .md-button {
42 46 &.md-fab {
43   - line-height: 36px;
44 47 width: 36px;
45 48 height: 36px;
46 49 margin: 4px 0 0 4px;
47   - opacity: 0.5;
  50 + line-height: 36px;
  51 + opacity: .5;
  52 +
48 53 @include transition(opacity .3s cubic-bezier(.55,0,.55,.2) .2s);
  54 +
49 55 md-icon {
50 56 position: absolute;
51 57 top: 25%;
52   - margin: 0;
53   - line-height: 18px;
54   - height: 18px;
55 58 width: 18px;
56   - min-height: 18px;
57 59 min-width: 18px;
  60 + height: 18px;
  61 + min-height: 18px;
  62 + margin: 0;
  63 + line-height: 18px;
58 64 }
59 65 }
60 66 }
61 67 }
  68 +
62 69 &.is-fullscreen {
63 70 &.md-is-open {
64 71 md-fab-trigger {
65 72 .md-button {
66 73 &.md-fab {
67 74 .md-fab-toolbar-background {
68   - transition-delay: 0ms !important;
69   - transition-duration: 0ms !important;
  75 + transition-delay: 0ms !important;
  76 + transition-duration: 0ms !important;
70 77 }
71 78 }
72 79 }
73 80 }
74 81 }
  82 +
75 83 .md-fab-toolbar-wrapper {
76 84 height: $mobile-toolbar-height-total;
  85 +
77 86 @media (min-width: $layout-breakpoint-sm) {
78 87 height: $fullscreen-toolbar-height;
79 88 }
  89 +
80 90 md-toolbar {
81   - min-height: $mobile-toolbar-height;
82 91 height: $mobile-toolbar-height;
  92 + min-height: $mobile-toolbar-height;
  93 +
83 94 @media (min-width: $layout-breakpoint-sm) {
84   - min-height: $fullscreen-toolbar-height;
85 95 height: $fullscreen-toolbar-height;
  96 + min-height: $fullscreen-toolbar-height;
86 97 }
87 98 }
88 99 }
89 100 }
  101 +
90 102 .md-fab-toolbar-wrapper {
91 103 height: $mobile-toolbar-height-total;
  104 +
92 105 @media (min-width: $layout-breakpoint-sm) {
93 106 height: $toolbar-height;
94 107 }
  108 +
95 109 md-toolbar {
96   - min-height: $mobile-toolbar-height;
97 110 height: $mobile-toolbar-height;
  111 + min-height: $mobile-toolbar-height;
  112 +
98 113 @media (min-width: $layout-breakpoint-sm) {
99   - min-height: $toolbar-height;
100 114 height: $toolbar-height;
  115 + min-height: $toolbar-height;
101 116 }
  117 +
102 118 md-fab-actions {
  119 + margin-top: 0;
103 120 font-size: 16px;
104   - margin-top: 0px;
  121 +
105 122 @media (max-width: $layout-breakpoint-sm) {
106 123 height: $mobile-toolbar-height;
107 124 max-height: $mobile-toolbar-height;
108 125 }
  126 +
109 127 .close-action {
110 128 margin-right: -18px;
111 129 }
  130 +
112 131 .md-fab-action-item {
113 132 width: 100%;
114 133 height: $mobile-toolbar-height;
  134 +
115 135 @media (min-width: $layout-breakpoint-sm) {
116 136 height: 46px;
117 137 }
  138 +
118 139 .tb-dashboard-action-panels {
  140 + flex-direction: column-reverse;
119 141 height: $mobile-toolbar-height;
  142 +
120 143 @media (min-width: $layout-breakpoint-sm) {
121 144 height: 46px;
122 145 }
123   - flex-direction: column-reverse;
  146 +
124 147 @media (min-width: $layout-breakpoint-sm) {
125 148 flex-direction: row-reverse;
126 149 }
  150 +
127 151 .tb-dashboard-action-panel {
128   - min-width: 0px;
  152 + flex-direction: row-reverse;
  153 + min-width: 0;
129 154 height: $half-mobile-toolbar-height;
  155 +
130 156 @media (min-width: $layout-breakpoint-sm) {
131 157 height: 46px;
132 158 }
133   - flex-direction: row-reverse;
  159 +
134 160 div {
135 161 height: $half-mobile-toolbar-height;
  162 +
136 163 @media (min-width: $layout-breakpoint-sm) {
137 164 height: 46px;
138 165 }
139 166 }
  167 +
140 168 md-select {
141 169 pointer-events: all;
142 170 }
  171 +
143 172 tb-states-component {
144 173 pointer-events: all;
145 174 }
  175 +
146 176 button.md-icon-button {
147 177 min-width: 40px;
  178 +
148 179 @media (max-width: $layout-breakpoint-sm) {
149 180 min-width: 28px;
150   - margin: 0px;
151 181 padding: 2px;
  182 + margin: 0;
152 183 }
153 184 }
154 185 }
... ... @@ -158,4 +189,4 @@ tb-dashboard-toolbar {
158 189 }
159 190 }
160 191 }
161   -}
\ No newline at end of file
  192 +}
... ...
... ... @@ -14,11 +14,12 @@
14 14 * limitations under the License.
15 15 */
16 16 @import "~compass-sass-mixins/lib/compass";
17   -@import '../../scss/constants';
18 17
19   -$toolbar-height: 50px;
20   -$fullscreen-toolbar-height: 64px;
21   -$mobile-toolbar-height: 84px;
  18 +@import "../../scss/constants";
  19 +
  20 +$toolbar-height: 50px !default;
  21 +$fullscreen-toolbar-height: 64px !default;
  22 +$mobile-toolbar-height: 84px !default;
22 23
23 24 section.tb-dashboard-title {
24 25 position: absolute;
... ... @@ -27,10 +28,10 @@ section.tb-dashboard-title {
27 28 }
28 29
29 30 input.tb-dashboard-title {
30   - font-size: 2.000rem;
31   - font-weight: 500;
32   - letter-spacing: 0.005em;
33 31 height: 38px;
  32 + font-size: 2rem;
  33 + font-weight: 500;
  34 + letter-spacing: .005em;
34 35 }
35 36
36 37 div.tb-padded {
... ... @@ -50,9 +51,11 @@ tb-details-sidenav.tb-widget-details-sidenav {
50 51 @media (min-width: $layout-breakpoint-sm) {
51 52 width: 85% !important;
52 53 }
  54 +
53 55 @media (min-width: $layout-breakpoint-md) {
54 56 width: 75% !important;
55 57 }
  58 +
56 59 @media (min-width: $layout-breakpoint-lg) {
57 60 width: 60% !important;
58 61 }
... ... @@ -65,47 +68,55 @@ tb-details-sidenav.tb-widget-details-sidenav {
65 68
66 69 section.tb-dashboard-toolbar {
67 70 position: absolute;
68   - top: 0px;
69   - left: 0px;
  71 + top: 0;
  72 + left: 0;
70 73 z-index: 13;
71 74 pointer-events: none;
  75 +
72 76 &.tb-dashboard-toolbar-opened {
73   - right: 0px;
74   - // @include transition(right .3s cubic-bezier(.55,0,.55,.2));
  77 + right: 0;
  78 + // @include transition(right .3s cubic-bezier(.55,0,.55,.2));
75 79 }
  80 +
76 81 &.tb-dashboard-toolbar-closed {
77 82 right: 18px;
  83 +
78 84 @include transition(right .3s cubic-bezier(.55,0,.55,.2) .2s);
79 85 }
80 86 }
81 87
82 88 .tb-dashboard-container {
83   - &.tb-dashboard-toolbar-opened {
84   - &.is-fullscreen {
85   - margin-top: $mobile-toolbar-height;
86   - @media (min-width: $layout-breakpoint-sm) {
87   - margin-top: $fullscreen-toolbar-height;
88   - }
89   - }
90   - &:not(.is-fullscreen) {
91   - margin-top: $mobile-toolbar-height;
92   - @media (min-width: $layout-breakpoint-sm) {
93   - margin-top: $toolbar-height;
94   - }
95   - @include transition(margin-top .3s cubic-bezier(.55,0,.55,.2));
96   - }
97   - }
98   - &.tb-dashboard-toolbar-closed {
99   - margin-top: 0px;
100   - @include transition(margin-top .3s cubic-bezier(.55,0,.55,.2) .2s);
  89 + &.tb-dashboard-toolbar-opened {
  90 + &.is-fullscreen {
  91 + margin-top: $mobile-toolbar-height;
  92 +
  93 + @media (min-width: $layout-breakpoint-sm) {
  94 + margin-top: $fullscreen-toolbar-height;
  95 + }
  96 + }
  97 +
  98 + &:not(.is-fullscreen) {
  99 + margin-top: $mobile-toolbar-height;
  100 +
  101 + @media (min-width: $layout-breakpoint-sm) {
  102 + margin-top: $toolbar-height;
  103 + }
  104 +
  105 + @include transition(margin-top .3s cubic-bezier(.55,0,.55,.2));
  106 + }
  107 + }
  108 +
  109 + &.tb-dashboard-toolbar-closed {
  110 + margin-top: 0;
  111 +
  112 + @include transition(margin-top .3s cubic-bezier(.55,0,.55,.2) .2s);
101 113 }
  114 +
102 115 .tb-dashboard-layouts {
103 116 md-backdrop {
104 117 z-index: 1;
105 118 }
106   - #tb-main-layout {
107 119
108   - }
109 120 #tb-right-layout {
110 121 md-sidenav {
111 122 z-index: 1;
... ... @@ -124,13 +135,15 @@ section.tb-powered-by-footer {
124 135 bottom: 5px;
125 136 z-index: 3;
126 137 pointer-events: none;
  138 +
127 139 span {
128 140 font-size: 12px;
  141 +
129 142 a {
130   - font-weight: bold;
  143 + font-weight: 700;
131 144 text-decoration: none;
132   - border: none;
133 145 pointer-events: all;
  146 + border: none;
134 147 }
135 148 }
136 149 }
... ...
... ... @@ -13,6 +13,7 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 md-select.default-state-controller {
17   - margin: 0px;
18   -}
\ No newline at end of file
  18 + margin: 0;
  19 +}
... ...
... ... @@ -13,34 +13,37 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -@import '../../../scss/constants';
  16 +@import "../../../scss/constants";
17 17
18 18 tb-states-component {
19   - min-width: 0px;
  19 + min-width: 0;
20 20 }
21 21
22 22 .entity-state-controller {
23   - .state-divider {
24   - font-size: 18px;
25   - padding-left: 15px;
26   - padding-right: 15px;
27   - overflow: hidden;
28   - text-overflow: ellipsis;
29   - white-space: nowrap;
30   - pointer-events: none;
31   - }
32   - .state-entry {
33   - font-size: 18px;
34   - overflow: hidden;
35   - text-overflow: ellipsis;
36   - white-space: nowrap;
37   - outline: none;
38   - }
39   - md-select {
40   - margin: 0px;
41   - .md-text {
42   - font-size: 18px;
43   - font-weight: bold;
44   - }
  23 + .state-divider {
  24 + padding-right: 15px;
  25 + padding-left: 15px;
  26 + overflow: hidden;
  27 + font-size: 18px;
  28 + text-overflow: ellipsis;
  29 + white-space: nowrap;
  30 + pointer-events: none;
  31 + }
  32 +
  33 + .state-entry {
  34 + overflow: hidden;
  35 + font-size: 18px;
  36 + text-overflow: ellipsis;
  37 + white-space: nowrap;
  38 + outline: none;
  39 + }
  40 +
  41 + md-select {
  42 + margin: 0;
  43 +
  44 + .md-text {
  45 + font-size: 18px;
  46 + font-weight: 700;
45 47 }
46   -}
\ No newline at end of file
  48 + }
  49 +}
... ...
... ... @@ -13,21 +13,22 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .manage-dashboard-states {
17 18 table.md-table {
18 19 tbody {
19 20 tr {
20 21 td {
21 22 &.tb-action-cell {
  23 + width: 100px;
  24 + min-width: 100px;
  25 + max-width: 100px;
22 26 overflow: hidden;
23 27 text-overflow: ellipsis;
24 28 white-space: nowrap;
25   - min-width: 100px;
26   - max-width: 100px;
27   - width: 100px;
28 29 }
29 30 }
30 31 }
31 32 }
32 33 }
33   -}
\ No newline at end of file
  34 +}
... ...
... ... @@ -13,7 +13,7 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -@import '../../../scss/constants';
  16 +@import "../../../scss/constants";
17 17
18 18 tb-aliases-entity-select {
19 19 min-width: 52px;
... ... @@ -26,18 +26,21 @@ tb-aliases-entity-select {
26 26 }
27 27
28 28 .tb-aliases-entity-select-panel {
  29 + min-width: 300px;
29 30 max-height: 150px;
  31 + overflow-x: hidden;
  32 + overflow-y: auto;
  33 + background: #fff;
  34 + border-radius: 4px;
  35 + box-shadow:
  36 + 0 7px 8px -4px rgba(0, 0, 0, .2),
  37 + 0 13px 19px 2px rgba(0, 0, 0, .14),
  38 + 0 5px 24px 4px rgba(0, 0, 0, .12);
  39 +
30 40 @media (min-height: 350px) {
31 41 max-height: 250px;
32 42 }
33   - min-width: 300px;
34   - background: white;
35   - border-radius: 4px;
36   - box-shadow: 0 7px 8px -4px rgba(0, 0, 0, 0.2),
37   - 0 13px 19px 2px rgba(0, 0, 0, 0.14),
38   - 0 5px 24px 4px rgba(0, 0, 0, 0.12);
39   - overflow-x: hidden;
40   - overflow-y: auto;
  43 +
41 44 md-content {
42 45 background-color: #fff;
43 46 }
... ... @@ -46,15 +49,17 @@ tb-aliases-entity-select {
46 49 section.tb-aliases-entity-select {
47 50 min-height: 32px;
48 51 padding: 0 6px;
  52 +
49 53 @media (max-width: $layout-breakpoint-sm) {
50   - padding: 0px;
  54 + padding: 0;
51 55 }
  56 +
52 57 span {
53 58 max-width: 200px;
54   - pointer-events: all;
55   - cursor: pointer;
56 59 overflow: hidden;
57 60 text-overflow: ellipsis;
58 61 white-space: nowrap;
  62 + pointer-events: all;
  63 + cursor: pointer;
59 64 }
60 65 }
... ...
... ... @@ -13,14 +13,17 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-entity-alias-dialog {
17 18 .tb-resolve-multiple-switch {
18 19 padding-left: 10px;
  20 +
19 21 .resolve-multiple-switch {
20 22 margin: 0;
21 23 }
  24 +
22 25 .resolve-multiple-label {
23 26 margin: 5px 0;
24 27 }
25 28 }
26   -}
\ No newline at end of file
  29 +}
... ...
... ... @@ -13,35 +13,43 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-aliases-dialog {
17 18 .md-dialog-content {
18   - padding-bottom: 0px;
19   - padding-top: 0px;
  19 + padding-top: 0;
  20 + padding-bottom: 0;
20 21 }
  22 +
21 23 .tb-aliases-header {
22 24 min-height: 40px;
23 25 padding: 0 34px 0 34px;
24 26 margin: 5px;
  27 +
25 28 .tb-header-label {
26 29 font-size: 14px;
27   - color: rgba(0, 0, 0, 0.570588);
  30 + color: rgba(0, 0, 0, .570588);
28 31 }
29 32 }
  33 +
30 34 .tb-alias {
31 35 padding: 0 0 0 10px;
32 36 margin: 5px;
  37 +
33 38 md-input-container {
34   - margin: 0px;
  39 + margin: 0;
35 40 }
  41 +
36 42 .tb-resolve-multiple-switch {
37 43 padding-left: 10px;
  44 +
38 45 .resolve-multiple-switch {
39 46 margin: 0;
40 47 }
41 48 }
  49 +
42 50 .md-button {
43 51 &.md-icon-button {
44   - margin: 0px;
  52 + margin: 0;
45 53 }
46 54 }
47 55 }
... ...
... ... @@ -13,46 +13,48 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -@import '../../../scss/constants';
  16 +@import "../../../scss/constants";
17 17
18   -$md-light: rgba(255, 255, 255, 100%);
19   -$md-edit-icon-fill: #757575;
  18 +$md-light: rgba(255, 255, 255, 100%) !default;
  19 +$md-edit-icon-fill: #757575 !default;
20 20
21 21 md-toolbar.md-table-toolbar.alternate {
22 22 .md-toolbar-tools {
23   - md-icon {
24   - color: $md-light;
25   - }
  23 + md-icon {
  24 + color: $md-light;
  25 + }
26 26 }
27 27 }
28 28
29 29 .md-table {
30 30 &.tb-attribute-table {
31   - table-layout: fixed;
32   - td.md-cell {
33   - &.tb-value-cell {
34   - overflow: auto;
35   - }
  31 + table-layout: fixed;
  32 +
  33 + td.md-cell {
  34 + &.tb-value-cell {
  35 + overflow: auto;
36 36 }
  37 + }
37 38 }
  39 +
38 40 .md-cell {
39 41 ng-md-icon {
40   - fill: $md-edit-icon-fill;
41 42 float: right;
42 43 height: 16px;
  44 + fill: $md-edit-icon-fill;
43 45 }
44 46 }
45 47 }
46 48
47 49 .widgets-carousel {
48 50 position: relative;
49   - margin: 0px;
50 51 height: calc(100% - 100px);
51 52 min-height: 150px !important;
  53 + margin: 0;
52 54
53 55 tb-dashboard {
54 56 #gridster-parent {
55 57 padding: 0 7px;
56 58 }
57 59 }
58   -}
\ No newline at end of file
  60 +}
... ...
... ... @@ -13,11 +13,13 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-entity-autocomplete {
17 18 .tb-entity-item {
18 19 display: block;
19 20 height: 48px;
20 21 }
  22 +
21 23 li {
22 24 height: auto !important;
23 25 white-space: normal !important;
... ...
... ... @@ -13,20 +13,23 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-entity-filter-view {
17 18 .entity-filter-empty {
18   - color: rgba(221, 44, 0, 0.87);
19 19 font-size: 14px;
20 20 line-height: 16px;
  21 + color: rgba(221, 44, 0, .87);
21 22 }
  23 +
22 24 .entity-filter-type {
23 25 font-size: 14px;
24 26 line-height: 16px;
25   - color: rgba(0, 0, 0, 0.570588);
  27 + color: rgba(0, 0, 0, .570588);
26 28 }
  29 +
27 30 .entity-filter-value {
28 31 font-size: 14px;
29 32 line-height: 16px;
30   - color: rgba(0, 0, 0, 0.570588);
  33 + color: rgba(0, 0, 0, .570588);
31 34 }
32   -}
\ No newline at end of file
  35 +}
... ...
... ... @@ -13,10 +13,11 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -.tb-entity-filter {
17 16
  17 +.tb-entity-filter {
18 18 #relationsQueryFilter {
19 19 padding-top: 20px;
  20 +
20 21 tb-entity-select {
21 22 min-height: 92px;
22 23 }
... ... @@ -24,12 +25,13 @@
24 25
25 26 .tb-root-state-entity-switch {
26 27 padding-left: 10px;
  28 +
27 29 .root-state-entity-switch {
28 30 margin: 0;
29 31 }
  32 +
30 33 .root-state-entity-label {
31 34 margin: 5px 0;
32 35 }
33 36 }
34   -
35   -}
\ No newline at end of file
  37 +}
... ...
... ... @@ -13,7 +13,9 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -/*.tb-entity-list {
  16 +
  17 +/*
  18 +.tb-entity-list {
17 19 #entity_list_chips {
18 20 .md-chips {
19 21 padding-bottom: 1px;
... ... @@ -26,4 +28,5 @@
26 28 padding-left: 1px;
27 29 }
28 30 }
29   -}*/
\ No newline at end of file
  31 +}
  32 +*/
... ...
... ... @@ -13,6 +13,8 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -.tb-entity-select {
17 16
18   -}
\ No newline at end of file
  17 +/*
  18 +.tb-entity-select {
  19 +}
  20 +*/
... ...
... ... @@ -13,11 +13,13 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-entity-subtype-autocomplete {
17 18 .tb-entity-subtype-item {
18 19 display: block;
19 20 height: 48px;
20 21 }
  22 +
21 23 li {
22 24 height: auto !important;
23 25 white-space: normal !important;
... ...
... ... @@ -13,7 +13,9 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -/*.tb-entity-subtype-list {
  16 +
  17 +/*
  18 +.tb-entity-subtype-list {
17 19 #entity_subtype_list_chips {
18 20 .md-chips {
19 21 padding-bottom: 1px;
... ... @@ -26,4 +28,5 @@
26 28 padding-left: 1px;
27 29 }
28 30 }
29   -}*/
  31 +}
  32 +*/
... ...
... ... @@ -13,6 +13,7 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 md-select.tb-entity-subtype-select {
17 18 min-width: 200px;
18 19 }
... ...
... ... @@ -13,7 +13,9 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -/*.tb-entity-type-list {
  16 +
  17 +/*
  18 +.tb-entity-type-list {
17 19 #entity_type_list_chips {
18 20 .md-chips {
19 21 padding-bottom: 1px;
... ... @@ -26,4 +28,5 @@
26 28 padding-left: 1px;
27 29 }
28 30 }
29   -}*/
\ No newline at end of file
  31 +}
  32 +*/
... ...
... ... @@ -13,5 +13,8 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
  17 +/*
16 18 md-select.tb-entity-type-select {
17 19 }
  20 +*/
... ...
... ... @@ -13,14 +13,16 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-relation-additional-info-panel {
17   - margin-left: 15px;
18   - border: 1px solid #C0C0C0;
19 18 height: 100%;
  19 + margin-left: 15px;
  20 + border: 1px solid #c0c0c0;
  21 +
20 22 #tb-relation-additional-info {
21   - min-width: 200px;
22   - min-height: 200px;
23 23 width: 100%;
  24 + min-width: 200px;
24 25 height: 100%;
  26 + min-height: 200px;
25 27 }
26 28 }
... ...
... ... @@ -13,32 +13,37 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-relation-filters {
17 18 .header {
18   - padding-left: 5px;
19 19 padding-right: 5px;
20 20 padding-bottom: 5px;
  21 + padding-left: 5px;
  22 +
21 23 .cell {
22   - padding-left: 5px;
23 24 padding-right: 5px;
24   - color: rgba(0,0,0,.54);
  25 + padding-left: 5px;
25 26 font-size: 12px;
26 27 font-weight: 700;
  28 + color: rgba(0, 0, 0, .54);
27 29 white-space: nowrap;
28 30 }
29 31 }
  32 +
30 33 .body {
31   - padding-left: 5px;
32   - padding-right: 5px;
33 34 max-height: 300px;
34   - overflow: auto;
  35 + padding-right: 5px;
35 36 padding-bottom: 20px;
  37 + padding-left: 5px;
  38 + overflow: auto;
  39 +
36 40 .row {
37 41 padding-top: 5px;
38 42 }
  43 +
39 44 .cell {
40   - padding-left: 5px;
41 45 padding-right: 5px;
  46 + padding-left: 5px;
42 47
43 48 md-select {
44 49 margin: 0 0 24px;
... ... @@ -49,25 +54,27 @@
49 54 }
50 55
51 56 md-chips-wrap {
52   - padding: 0px;
  57 + padding: 0;
53 58 margin: 0 0 24px;
  59 +
54 60 md-autocomplete {
55 61 height: 30px;
  62 +
56 63 md-autocomplete-wrap {
57 64 height: 30px;
58 65 }
59 66 }
60 67 }
  68 +
61 69 .md-chips .md-chip-input-container input {
62   - padding: 2px 2px 2px;
63 70 height: 26px;
  71 + padding: 2px 2px 2px;
64 72 line-height: 26px;
65 73 }
66   -
67 74 }
68 75
69 76 .md-button {
70 77 margin: 0;
71 78 }
72 79 }
73   -}
\ No newline at end of file
  80 +}
... ...
... ... @@ -13,9 +13,9 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -@import '../../../scss/constants';
  16 +@import "../../../scss/constants";
17 17
18   -$md-light: rgba(255, 255, 255, 100%);
  18 +$md-light: rgba(255, 255, 255, 100%) !default;
19 19
20 20 .tb-relation-table {
21 21 md-toolbar.md-table-toolbar.alternate {
... ...
... ... @@ -13,11 +13,13 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-relation-type-autocomplete {
17 18 .tb-relation-type-item {
18 19 display: block;
19 20 height: 48px;
20 21 }
  22 +
21 23 li {
22 24 height: auto !important;
23 25 white-space: normal !important;
... ...
... ... @@ -13,24 +13,28 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 md-list.tb-event-table {
17   - padding: 0px;
  18 + padding: 0;
18 19
19   - md-list-item {
20   - padding: 0px;
21   - }
  20 + md-list-item {
  21 + padding: 0;
  22 + }
22 23
23 24 .tb-row {
24 25 height: 48px;
25   - padding: 0px;
  26 + padding: 0;
26 27 overflow: hidden;
  28 +
27 29 .tb-cell {
28 30 text-overflow: ellipsis;
  31 +
29 32 &.tb-scroll {
30   - white-space: nowrap;
31   - overflow-y: hidden;
32 33 overflow-x: auto;
  34 + overflow-y: hidden;
  35 + white-space: nowrap;
33 36 }
  37 +
34 38 &.tb-nowrap {
35 39 white-space: nowrap;
36 40 }
... ... @@ -38,7 +42,7 @@ md-list.tb-event-table {
38 42 }
39 43
40 44 .tb-row:hover {
41   - background-color: #EEEEEE;
  45 + background-color: #eee;
42 46 }
43 47
44 48 .tb-header:hover {
... ... @@ -46,44 +50,45 @@ md-list.tb-event-table {
46 50 }
47 51
48 52 .tb-header {
49   - .tb-cell {
50   - color: rgba(0,0,0,.54);
51   - font-size: 12px;
52   - font-weight: 700;
53   - background: none;
54   - white-space: nowrap;
55   - }
  53 + .tb-cell {
  54 + font-size: 12px;
  55 + font-weight: 700;
  56 + color: rgba(0, 0, 0, .54);
  57 + white-space: nowrap;
  58 + background: none;
  59 + }
56 60 }
57 61
58 62 .tb-cell {
59   - &:first-child {
60   - padding-left: 14px;
61   - }
62   - &:last-child {
63   - padding-right: 14px;
64   - }
65   - padding: 0 6px;
66   - margin: auto 0;
67   - color: rgba(0,0,0,.87);
68   - font-size: 13px;
69   - vertical-align: middle;
70   - text-align: left;
71   - overflow: hidden;
72   - .md-button {
73   - padding: 0;
74   - margin: 0;
75   - }
  63 + &:first-child {
  64 + padding-left: 14px;
  65 + }
  66 +
  67 + &:last-child {
  68 + padding-right: 14px;
  69 + }
  70 + padding: 0 6px;
  71 + margin: auto 0;
  72 + overflow: hidden;
  73 + font-size: 13px;
  74 + color: rgba(0, 0, 0, .87);
  75 + text-align: left;
  76 + vertical-align: middle;
  77 +
  78 + .md-button {
  79 + padding: 0;
  80 + margin: 0;
  81 + }
76 82 }
77 83
78 84 .tb-cell.tb-number {
79 85 text-align: right;
80 86 }
81   -
82 87 }
83 88
84 89 #tb-event-content {
85   - min-width: 400px;
86   - min-height: 50px;
87 90 width: 100%;
  91 + min-width: 400px;
88 92 height: 100%;
  93 + min-height: 50px;
89 94 }
... ...
... ... @@ -13,23 +13,26 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -@import '../../scss/constants';
  16 +@import "../../scss/constants";
17 17
18 18
19 19 .extension-table {
20   -
21 20 md-input-container .md-errors-spacer {
22 21 min-height: 0;
23 22 }
24 23
25   - /*&.tb-data-table table.md-table tbody tr td.tb-action-cell,
  24 + /*
  25 + &.tb-data-table table.md-table tbody tr td.tb-action-cell,
26 26 &.tb-data-table table.md-table.md-row-select tbody tr td.tb-action-cell {
27 27 width: 114px;
28   - }*/
  28 + }
  29 + */
  30 +
29 31 .sync-widget {
30 32 max-height: 90px;
31 33 overflow: hidden;
32 34 }
  35 +
33 36 .toolbar-widget {
34 37 min-height: 39px;
35 38 max-height: 39px;
... ... @@ -37,11 +40,13 @@
37 40 }
38 41
39 42 .extension__syncStatus--black {
40   - color: #000000!important;
  43 + color: #000 !important;
41 44 }
  45 +
42 46 .extension__syncStatus--green {
43   - color: #228634!important;
  47 + color: #228634 !important;
44 48 }
  49 +
45 50 .extension__syncStatus--red {
46   - color: #862222!important;
47   -}
\ No newline at end of file
  51 + color: #862222 !important;
  52 +}
... ...
... ... @@ -13,28 +13,34 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .extension-form {
17 18 li > .md-button {
18   - color: rgba(0, 0, 0, 0.7);
19 19 margin: 0;
  20 + color: rgba(0, 0, 0, .7);
20 21 }
  22 +
21 23 .vAccordion--default {
22   - margin-top: 0;
23 24 padding-left: 3px;
  25 + margin-top: 0;
24 26 }
  27 +
25 28 .tb-container {
26   - width:100%;
  29 + width: 100%;
27 30 }
  31 +
28 32 .dropdown-messages {
29 33 .tb-error-message {
30 34 padding: 5px 0 0 0;
31 35 }
32 36 }
  37 +
33 38 .dropdown-section {
34 39 margin-bottom: 30px;
35 40 }
  41 +
36 42 v-pane.inner-invalid > v-pane-header {
37   - border-bottom: 2px solid rgb(221,44,0);
  43 + border-bottom: 2px solid rgb(221, 44, 0);
38 44 }
39 45 }
40 46
... ... @@ -45,17 +51,19 @@
45 51 }
46 52
47 53 .tb-extension-custom-transformer-panel {
48   - margin-left: 15px;
49   - border: 1px solid #C0C0C0;
50 54 height: 100%;
  55 + margin-left: 15px;
  56 + border: 1px solid #c0c0c0;
  57 +
51 58 .tb-extension-custom-transformer {
52   - min-width: 600px;
53   - min-height: 200px;
54 59 width: 100%;
  60 + min-width: 600px;
55 61 height: 100%;
  62 + min-height: 200px;
56 63 }
  64 +
57 65 .ace_text-input {
58   - position:absolute!important
  66 + position: absolute !important;
59 67 }
60 68 }
61 69
... ... @@ -68,9 +76,9 @@
68 76 }
69 77
70 78 .tb-drop-file-input-hide {
71   - height: 200%;
72   - display: block;
73 79 position: absolute;
74 80 bottom: 0;
  81 + display: block;
75 82 width: 100%;
76   -}
\ No newline at end of file
  83 + height: 200%;
  84 +}
... ...
... ... @@ -15,8 +15,12 @@
15 15 */
16 16 @import "../../scss/constants";
17 17
18   -.md-button.tb-help-button-style, .tb-help-button-style {
  18 +/*
  19 +.md-button.tb-help-button-style,
  20 +.tb-help-button-style {
19 21 }
20 22
21   -.md-button.tb-help-button-pos, .tb-help-button-pos {
  23 +.md-button.tb-help-button-pos,
  24 +.tb-help-button-pos {
22 25 }
  26 +*/
... ...
... ... @@ -16,10 +16,11 @@
16 16 @import "../../scss/constants";
17 17
18 18 .tb-home-links {
19   - .md-headline {
20   - font-size: 20px;
21   - @media (min-width: $layout-breakpoint-xmd) {
22   - font-size: 24px;
23   - }
  19 + .md-headline {
  20 + font-size: 20px;
  21 +
  22 + @media (min-width: $layout-breakpoint-xmd) {
  23 + font-size: 24px;
24 24 }
25   -}
\ No newline at end of file
  25 + }
  26 +}
... ...
... ... @@ -13,29 +13,30 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -$previewSize: 100px;
  16 +$previewSize: 100px !default;
17 17
18 18 .tb-file-select-container {
19 19 position: relative;
20   - height: $previewSize;
21 20 width: 100%;
  21 + height: $previewSize;
22 22 }
23 23
24 24 .tb-file-preview {
25   - max-width: $previewSize;
26   - max-height: $previewSize;
27 25 width: auto;
  26 + max-width: $previewSize;
28 27 height: auto;
  28 + max-height: $previewSize;
29 29 }
30 30
31 31 .tb-file-clear-container {
32   - width: 48px;
33   - height: $previewSize;
34 32 position: relative;
35 33 float: right;
  34 + width: 48px;
  35 + height: $previewSize;
36 36 }
  37 +
37 38 .tb-file-clear-btn {
38 39 position: absolute !important;
39 40 top: 50%;
40   - transform: translate(0%,-50%) !important;
  41 + transform: translate(0%, -50%) !important;
41 42 }
... ...
... ... @@ -13,5 +13,7 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -.form { height: 400px; }
17   -.schema { height: 800px; }
\ No newline at end of file
  16 +
  17 +.form { height: 400px; }
  18 +
  19 +.schema { height: 800px; }
... ...
... ... @@ -29,23 +29,31 @@
29 29 .tb-breadcrumb {
30 30 font-size: 18px !important;
31 31 font-weight: 400 !important;
32   - h1, a, span {
  32 +
  33 + h1,
  34 + a,
  35 + span {
33 36 overflow: hidden;
34 37 text-overflow: ellipsis;
35 38 white-space: nowrap;
36 39 }
  40 +
37 41 a {
38 42 border: none;
39   - opacity: 0.75;
40   - @include transition(opacity 0.35s);
  43 + opacity: .75;
  44 +
  45 + @include transition(opacity .35s);
41 46 }
42   - a:hover, a:focus {
43   - opacity: 1;
  47 +
  48 + a:hover,
  49 + a:focus {
44 50 text-decoration: none !important;
45 51 border: none;
  52 + opacity: 1;
46 53 }
  54 +
47 55 .divider {
48   - padding: 0px 30px;
  56 + padding: 0 30px;
49 57 }
50 58 }
51 59
... ... @@ -54,21 +62,22 @@ md-sidenav.tb-site-sidenav {
54 62 }
55 63
56 64 md-icon.tb-logo-title {
57   - height: 36px;
58 65 width: 200px;
  66 + height: 36px;
59 67 }
60 68
61 69 .tb-nav-header {
62   - flex-shrink: 0;
63 70 z-index: 2;
  71 + flex-shrink: 0;
64 72 white-space: nowrap;
65 73 }
66 74
67 75 .tb-nav-header-toolbar {
68   - border-bottom: 1px solid rgba(0, 0, 0, 0.12);
69   - flex-shrink: 0;
70 76 z-index: 2;
  77 + flex-shrink: 0;
71 78 white-space: nowrap;
  79 + border-bottom: 1px solid rgba(0, 0, 0, .12);
  80 +
72 81 .md-toolbar-tools {
73 82 flex-basis: auto;
74 83 }
... ...
... ... @@ -13,28 +13,32 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 div.tb-user-info {
17 18 line-height: 1.5;
  19 +
18 20 span {
19   - text-transform: none;
20 21 text-align: left;
  22 + text-transform: none;
21 23 }
  24 +
22 25 span.tb-user-display-name {
23   - font-size: 0.800rem;
  26 + font-size: .8rem;
24 27 font-weight: 300;
25   - letter-spacing: 0.008em;
  28 + letter-spacing: .008em;
26 29 }
  30 +
27 31 span.tb-user-authority {
28   - font-size: 0.800rem;
  32 + font-size: .8rem;
29 33 font-weight: 300;
30   - letter-spacing: 0.005em;
31   - opacity: 0.8;
  34 + letter-spacing: .005em;
  35 + opacity: .8;
32 36 }
33 37 }
34 38
35 39 md-icon.tb-mini-avatar {
  40 + width: 36px;
  41 + height: 36px;
36 42 margin: auto 8px;
37 43 font-size: 36px;
38   - height: 36px;
39   - width: 36px;
40 44 }
... ...
... ... @@ -13,18 +13,21 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -@import '../../scss/constants';
  16 +@import "../../scss/constants";
17 17
18 18 md-card.tb-login-card {
19 19 width: 330px !important;
  20 +
20 21 @media (min-width: $layout-breakpoint-sm) {
21 22 width: 450px !important;
22 23 }
  24 +
23 25 md-card-title {
24 26 img.tb-login-logo {
25 27 height: 50px;
26 28 }
27 29 }
  30 +
28 31 md-card-content {
29 32 margin-top: -50px;
30 33 }
... ...
... ... @@ -17,12 +17,14 @@
17 17 .tb-link-label-autocomplete {
18 18 .tb-not-found {
19 19 display: block;
20   - line-height: 1.5;
21 20 height: 48px;
  21 + line-height: 1.5;
  22 +
22 23 .tb-no-entries {
23 24 line-height: 48px;
24 25 }
25 26 }
  27 +
26 28 li {
27 29 height: auto !important;
28 30 white-space: normal !important;
... ...
... ... @@ -13,11 +13,13 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-message-type-autocomplete {
17 18 .tb-message-type-item {
18 19 display: block;
19 20 height: 48px;
20 21 }
  22 +
21 23 li {
22 24 height: auto !important;
23 25 white-space: normal !important;
... ...
... ... @@ -18,88 +18,106 @@
18 18 .tb-fullscreen-button-style {
19 19 z-index: 1;
20 20 }
  21 +
21 22 section.tb-header-buttons.tb-library-open {
22   - pointer-events: none;
23 23 position: absolute;
24   - left: 0px;
25   - top: 0px;
  24 + top: 0;
  25 + left: 0;
26 26 z-index: 1;
  27 + pointer-events: none;
  28 +
27 29 .md-button.tb-btn-open-library {
28   - left: 0px;
29   - top: 0px;
30   - line-height: 36px;
  30 + top: 0;
  31 + left: 0;
31 32 width: 36px;
32 33 height: 36px;
33 34 margin: 4px 0 0 4px;
34   - opacity: 0.5;
  35 + line-height: 36px;
  36 + opacity: .5;
35 37 }
36 38 }
  39 +
37 40 .tb-rulechain-library {
  41 + z-index: 1;
38 42 width: 250px;
39 43 min-width: 250px;
40   - z-index: 1;
  44 +
41 45 md-toolbar {
42   - min-height: 48px;
43 46 height: 48px;
44   - .md-toolbar-tools>.md-button:last-child {
45   - margin-right: 0px;
  47 + min-height: 48px;
  48 +
  49 + .md-toolbar-tools > .md-button:last-child {
  50 + margin-right: 0;
46 51 }
  52 +
47 53 .md-toolbar-tools {
48   - font-size: 14px;
49   - padding: 0px 6px;
50 54 height: 48px;
  55 + padding: 0 6px;
  56 + font-size: 14px;
  57 +
51 58 .md-button.md-icon-button {
52   - margin: 0px;
  59 + margin: 0;
  60 +
53 61 &.tb-small {
  62 + width: 32px;
54 63 height: 32px;
55 64 min-height: 32px;
56   - line-height: 20px;
57 65 padding: 6px;
58   - width: 32px;
  66 + line-height: 20px;
  67 +
59 68 md-icon {
60   - line-height: 20px;
61   - font-size: 20px;
62   - height: 20px;
63 69 width: 20px;
64   - min-height: 20px;
65 70 min-width: 20px;
  71 + height: 20px;
  72 + min-height: 20px;
  73 + font-size: 20px;
  74 + line-height: 20px;
66 75 }
67 76 }
68 77 }
69 78 }
70 79 }
  80 +
71 81 .tb-rulechain-library-panel-group {
72   - overflow-y: auto;
73 82 overflow-x: hidden;
  83 + overflow-y: auto;
  84 +
74 85 .tb-panel-title {
  86 + min-width: 150px;
75 87 -webkit-user-select: none;
76 88 -moz-user-select: none;
77 89 -ms-user-select: none;
78 90 user-select: none;
79   - min-width: 150px;
80 91 }
  92 +
81 93 .fc-canvas {
82 94 background: #f9f9f9;
83 95 }
  96 +
84 97 md-icon.md-expansion-panel-icon {
85   - margin-right: 0px;
  98 + margin-right: 0;
86 99 }
87   - md-expansion-panel-collapsed, .md-expansion-panel-header-container {
  100 +
  101 + md-expansion-panel-collapsed,
  102 + .md-expansion-panel-header-container {
  103 + position: static;
88 104 background: #e6e6e6;
89 105 border-color: #909090;
90   - position: static;
91 106 }
  107 +
92 108 md-expansion-panel {
93 109 &.md-open {
94 110 margin-top: 0;
95 111 margin-bottom: 0;
96 112 }
97 113 }
  114 +
98 115 md-expansion-panel-content {
99   - padding: 0px;
  116 + padding: 0;
100 117 }
101 118 }
102 119 }
  120 +
103 121 .tb-rulechain-graph {
104 122 z-index: 0;
105 123 overflow: auto;
... ... @@ -107,29 +125,35 @@
107 125 }
108 126
109 127 #tb-rule-chain-context-menu {
110   - padding-top: 0px;
111   - border-radius: 8px;
112 128 max-height: 404px;
  129 + padding-top: 0;
  130 + border-radius: 8px;
  131 +
113 132 .tb-context-menu-header {
114   - padding: 8px 5px 5px;
115   - font-size: 14px;
116 133 display: flex;
117 134 flex-direction: row;
118 135 height: 36px;
119 136 min-height: 36px;
  137 + padding: 8px 5px 5px;
  138 + font-size: 14px;
  139 +
120 140 &.tb-rulechain {
121 141 background-color: #aac7e4;
122 142 }
  143 +
123 144 &.tb-link {
124 145 background-color: #aac7e4;
125 146 }
  147 +
126 148 md-icon {
127   - padding-left: 2px;
128 149 padding-right: 10px;
  150 + padding-left: 2px;
129 151 }
  152 +
130 153 .tb-context-menu-title {
131 154 font-weight: 500;
132 155 }
  156 +
133 157 .tb-context-menu-subtitle {
134 158 font-size: 12px;
135 159 }
... ... @@ -139,37 +163,45 @@
139 163 .fc-canvas {
140 164 min-width: 100%;
141 165 min-height: 100%;
142   - outline: none;
143   - -webkit-touch-callout: none;
144 166 -webkit-user-select: none;
145 167 -khtml-user-select: none;
146 168 -moz-user-select: none;
147 169 -ms-user-select: none;
148 170 user-select: none;
  171 + outline: none;
  172 + -webkit-touch-callout: none;
  173 +
149 174 svg {
150 175 display: block;
151 176 }
152 177 }
153 178
154   -.tb-rule-node, #tb-rule-chain-context-menu .tb-context-menu-header {
  179 +.tb-rule-node,
  180 +#tb-rule-chain-context-menu .tb-context-menu-header {
155 181 &.tb-filter-type {
156 182 background-color: #f1e861;
157 183 }
  184 +
158 185 &.tb-enrichment-type {
159 186 background-color: #cdf14e;
160 187 }
  188 +
161 189 &.tb-transformation-type {
162 190 background-color: #79cef1;
163 191 }
  192 +
164 193 &.tb-action-type {
165 194 background-color: #f1928f;
166 195 }
  196 +
167 197 &.tb-external-type {
168 198 background-color: #fbc766;
169 199 }
  200 +
170 201 &.tb-rule-chain-type {
171 202 background-color: #d6c4f1;
172 203 }
  204 +
173 205 &.tb-unknown-type {
174 206 background-color: #f16c29;
175 207 }
... ... @@ -180,102 +212,112 @@
180 212 flex-direction: row;
181 213 min-width: 150px;
182 214 max-width: 150px;
  215 + height: 32px;
183 216 min-height: 32px;
184 217 max-height: 32px;
185   - height: 32px;
186 218 padding: 5px 10px;
187   - border-radius: 5px;
188   - background-color: #F15B26;
189   - pointer-events: none;
190   - color: #333;
191   - border: solid 1px #777;
192 219 font-size: 12px;
193 220 line-height: 16px;
  221 + color: #333;
  222 + pointer-events: none;
  223 + background-color: #f15b26;
  224 + border: solid 1px #777;
  225 + border-radius: 5px;
  226 +
194 227 &.tb-rule-node-highlighted:not(.tb-rule-node-invalid) {
195 228 box-shadow: 0 0 10px 6px #51cbee;
  229 +
196 230 .tb-node-title {
  231 + font-weight: 700;
197 232 text-decoration: underline;
198   - font-weight: bold;
199 233 }
200 234 }
  235 +
201 236 &.tb-rule-node-invalid {
202 237 box-shadow: 0 0 10px 6px #ff5c50;
203 238 }
  239 +
204 240 &.tb-input-type {
205   - background-color: #a3eaa9;
206 241 user-select: none;
  242 + background-color: #a3eaa9;
207 243 }
208 244
209 245 md-icon {
210   - font-size: 20px;
211 246 width: 20px;
  247 + min-width: 20px;
212 248 height: 20px;
213 249 min-height: 20px;
214   - min-width: 20px;
215 250 padding-right: 4px;
  251 + font-size: 20px;
216 252 }
217   - .tb-node-type {
218 253
219   - }
220 254 .tb-node-title {
221 255 font-weight: 500;
222 256 }
223   - .tb-node-type, .tb-node-title {
  257 +
  258 + .tb-node-type,
  259 + .tb-node-title {
224 260 overflow: hidden;
225   - white-space: nowrap;
226 261 text-overflow: ellipsis;
  262 + white-space: nowrap;
227 263 }
228 264 }
229 265
230 266 .fc-node {
231 267 z-index: 1;
232   - outline: none;
233 268 border-radius: 8px;
  269 + outline: none;
  270 +
234 271 &.fc-dragging {
235 272 z-index: 10;
236 273 }
  274 +
237 275 p {
238 276 padding: 0 15px;
239 277 text-align: center;
240 278 }
  279 +
241 280 .fc-node-overlay {
242 281 position: absolute;
243   - pointer-events: none;
244   - left: 0;
245 282 top: 0;
246 283 right: 0;
247 284 bottom: 0;
  285 + left: 0;
  286 + pointer-events: none;
248 287 background-color: #000;
249   - opacity: 0;
250 288 border-radius: 5px;
  289 + opacity: 0;
251 290 }
  291 +
252 292 &.fc-hover {
253 293 .fc-node-overlay {
254   - opacity: 0.25;
  294 + opacity: .25;
255 295 }
256 296 }
  297 +
257 298 &.fc-selected {
258 299 .fc-node-overlay {
259   - opacity: 0.25;
  300 + opacity: .25;
260 301 }
261   - }
262   - &.fc-selected {
  302 +
263 303 &:not(.fc-edit) {
264   - border: solid 3px red;
265 304 margin: -3px;
  305 + border: solid 3px #f00;
266 306 }
267 307 }
268 308 }
269 309
270   -.fc-leftConnectors, .fc-rightConnectors {
  310 +.fc-leftConnectors,
  311 +.fc-rightConnectors {
271 312 position: absolute;
272 313 top: 0;
273   - height: 100%;
  314 +
  315 + z-index: 0;
274 316
275 317 display: flex;
276 318 flex-direction: column;
  319 + height: 100%;
277 320
278   - z-index: 0;
279 321 .fc-magnet {
280 322 align-items: center;
281 323 }
... ... @@ -292,18 +334,18 @@
292 334 .fc-magnet {
293 335 display: flex;
294 336 flex-grow: 1;
295   - height: 60px;
296 337 justify-content: center;
  338 + height: 60px;
297 339 }
298 340
299 341 .fc-connector {
300 342 width: 14px;
301 343 height: 14px;
302   - border: 1px solid #333;
303 344 margin: 10px;
304   - border-radius: 5px;
305   - background-color: #ccc;
306 345 pointer-events: all;
  346 + background-color: #ccc;
  347 + border: 1px solid #333;
  348 + border-radius: 5px;
307 349 }
308 350
309 351 .fc-connector.fc-hover {
... ... @@ -312,45 +354,49 @@
312 354
313 355 .fc-arrow-marker {
314 356 polygon {
315   - stroke: gray;
316   - fill: gray;
  357 + fill: #808080;
  358 + stroke: #808080;
317 359 }
318 360 }
319 361
320 362 .fc-arrow-marker-selected {
321 363 polygon {
322   - stroke: red;
323   - fill: red;
  364 + fill: #f00;
  365 + stroke: #f00;
324 366 }
325 367 }
326 368
327 369 .fc-edge {
328 370 outline: none;
329   - stroke: gray;
330   - stroke-width: 4;
331   - fill: transparent;
332 371 transition: stroke-width .2s;
  372 + fill: transparent;
  373 + stroke: #808080;
  374 + stroke-width: 4;
  375 +
333 376 &.fc-selected {
334   - stroke: red;
335   - stroke-width: 4;
336 377 fill: transparent;
  378 + stroke: #f00;
  379 + stroke-width: 4;
337 380 }
  381 +
338 382 &.fc-active {
339 383 animation: dash 3s linear infinite;
340 384 stroke-dasharray: 20;
341 385 }
  386 +
342 387 &.fc-hover {
343   - stroke: gray;
344   - stroke-width: 6;
345 388 fill: transparent;
  389 + stroke: #808080;
  390 + stroke-width: 6;
346 391 }
  392 +
347 393 &.fc-dragging {
348 394 pointer-events: none;
349 395 }
350 396 }
351 397
352 398 .edge-endpoint {
353   - fill: gray;
  399 + fill: #808080;
354 400 }
355 401
356 402 .fc-nodedelete {
... ... @@ -364,22 +410,23 @@
364 410 }
365 411
366 412 .fc-edit {
367   - .fc-nodedelete, .fc-nodeedit {
368   - outline: none;
369   - display: block;
  413 + .fc-nodedelete,
  414 + .fc-nodeedit {
370 415 position: absolute;
371   - border: solid 2px white;
372   - border-radius: 50%;
373   - font-weight: 600;
374   - line-height: 20px;
  416 + display: block;
  417 + width: 22px;
375 418 height: 20px;
376 419 padding-top: 2px;
377   - width: 22px;
378   - background: #f83e05;
  420 + font-weight: 600;
  421 + line-height: 20px;
379 422 color: #fff;
380 423 text-align: center;
381 424 vertical-align: bottom;
382 425 cursor: pointer;
  426 + background: #f83e05;
  427 + border: solid 2px #fff;
  428 + border-radius: 50%;
  429 + outline: none;
383 430 }
384 431
385 432 .fc-nodeedit {
... ... @@ -391,7 +438,6 @@
391 438 top: -24px;
392 439 right: -13px;
393 440 }
394   -
395 441 }
396 442
397 443 .fc-noselect {
... ... @@ -407,31 +453,37 @@
407 453 .fc-edge-label {
408 454 position: absolute;
409 455 transition: transform .2s;
410   -// opacity: 0.8;
  456 + // opacity: 0.8;
  457 +
411 458 &.ng-leave {
412 459 transition: 0s none;
413 460 }
  461 +
414 462 &.fc-hover {
415 463 transform: scale(1.25);
416 464 }
  465 +
417 466 &.fc-selected {
418 467 .fc-edge-label-text {
419 468 span {
420   - border: solid red;
421   - color: #fff;
422 469 font-weight: 600;
423   - background-color: red;
  470 + color: #fff;
  471 + background-color: #f00;
  472 + border: solid #f00;
424 473 }
425 474 }
426 475 }
  476 +
427 477 .fc-nodeedit {
428 478 top: -30px;
429 479 right: 14px;
430 480 }
  481 +
431 482 .fc-nodedelete {
432 483 top: -30px;
433 484 right: -13px;
434 485 }
  486 +
435 487 &:focus {
436 488 outline: 0;
437 489 }
... ... @@ -439,27 +491,28 @@
439 491
440 492 .fc-edge-label-text {
441 493 position: absolute;
442   - -webkit-transform: translate(-50%, -50%);
443   - transform: translate(-50%, -50%);
444   - white-space: nowrap;
445   - text-align: center;
446 494 font-size: 14px;
447 495 font-weight: 600;
  496 + text-align: center;
  497 + white-space: nowrap;
  498 + -webkit-transform: translate(-50%, -50%);
  499 + transform: translate(-50%, -50%);
  500 +
448 501 span {
  502 + padding: 3px 5px;
  503 + color: #003a79;
449 504 cursor: default;
  505 + background-color: #fff;
450 506 border: solid 2px #003a79;
451 507 border-radius: 10px;
452   - color: #003a79;
453   - background-color: #fff;
454   - padding: 3px 5px;
455 508 }
456 509 }
457 510
458 511 .fc-select-rectangle {
459   - border: 2px dashed #5262ff;
460 512 position: absolute;
461   - background: rgba(20,125,255,0.1);
462 513 z-index: 2;
  514 + background: rgba(20, 125, 255, .1);
  515 + border: 2px dashed #5262ff;
463 516 }
464 517
465 518 @keyframes dash {
... ... @@ -468,13 +521,15 @@
468 521 }
469 522 }
470 523
471   -.tb-rule-node-tooltip, .tb-rule-node-help {
  524 +.tb-rule-node-tooltip,
  525 +.tb-rule-node-help {
472 526 color: #333;
473 527 }
474 528
475 529 .tb-rule-node-tooltip {
476   - font-size: 14px;
477 530 max-width: 300px;
  531 + font-size: 14px;
  532 +
478 533 &.tb-lib-tooltip {
479 534 width: 300px;
480 535 }
... ... @@ -489,28 +544,33 @@
489 544 color: #ea0d0d;
490 545 }
491 546
492   -.tb-rule-node-tooltip, .tb-rule-node-error-tooltip, .tb-rule-node-help {
  547 +.tb-rule-node-tooltip,
  548 +.tb-rule-node-error-tooltip,
  549 +.tb-rule-node-help {
493 550 #tb-node-content {
494 551 .tb-node-title {
495 552 font-weight: 600;
496 553 }
  554 +
497 555 .tb-node-description {
498 556 font-style: italic;
499 557 color: #555;
500 558 }
  559 +
501 560 .tb-node-details {
502 561 padding-top: 10px;
503 562 padding-bottom: 10px;
504 563 }
  564 +
505 565 code {
506   - padding: 0px 3px 2px 3px;
  566 + padding: 0 3px 2px 3px;
507 567 margin: 1px;
508   - color: #AD1625;
  568 + font-size: 12px;
  569 + color: #ad1625;
509 570 white-space: nowrap;
510 571 background-color: #f7f7f9;
511 572 border: 1px solid #e1e1e8;
512 573 border-radius: 2px;
513   - font-size: 12px;
514 574 }
515 575 }
516   -}
\ No newline at end of file
  576 +}
... ...
... ... @@ -16,13 +16,13 @@
16 16
17 17 .tb-rulenode {
18 18 tb-json-object-edit.tb-rule-node-configuration-json {
19   - height: 300px;
20 19 display: block;
  20 + height: 300px;
21 21 }
22 22 }
23 23
24 24 .tb-rulenode-directive-error {
25   - color: rgb(221,44,0);
26 25 font-size: 13px;
27 26 font-weight: 400;
28   -}
\ No newline at end of file
  27 + color: rgb(221, 44, 0);
  28 +}
... ...
... ... @@ -13,24 +13,25 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -@import '../../../scss/constants';
  16 +@import "../../../scss/constants";
  17 +
17 18 @import "~compass-sass-mixins/lib/compass";
18 19
19 20 md-dialog.tb-node-script-test-dialog {
20 21 &.md-dialog-fullscreen {
21   - min-height: 100%;
  22 + width: 100%;
22 23 min-width: 100%;
23   - max-height: 100%;
24 24 max-width: 100%;
25   - width: 100%;
26 25 height: 100%;
  26 + min-height: 100%;
  27 + max-height: 100%;
27 28 border-radius: 0;
28 29 }
29 30
30 31 .tb-split {
31 32 @include box-sizing(border-box);
32   - overflow-y: auto;
33 33 overflow-x: hidden;
  34 + overflow-y: auto;
34 35 }
35 36
36 37 .ace_editor {
... ... @@ -38,13 +39,13 @@ md-dialog.tb-node-script-test-dialog {
38 39 }
39 40
40 41 .tb-content {
41   - border: 1px solid #C0C0C0;
42 42 padding-top: 5px;
43 43 padding-left: 5px;
  44 + border: 1px solid #c0c0c0;
44 45 }
45 46
46 47 .gutter {
47   - background-color: #eeeeee;
  48 + background-color: #eee;
48 49
49 50 background-repeat: no-repeat;
50 51 background-position: 50%;
... ... @@ -52,21 +53,23 @@ md-dialog.tb-node-script-test-dialog {
52 53
53 54 .gutter.gutter-horizontal {
54 55 cursor: col-resize;
55   - background-image: url('../../../../node_modules/split.js/grips/vertical.png');
  56 + background-image: url("../../../../node_modules/split.js/grips/vertical.png");
56 57 }
57 58
58 59 .gutter.gutter-vertical {
59 60 cursor: row-resize;
60   - background-image: url('../../../../node_modules/split.js/grips/horizontal.png');
  61 + background-image: url("../../../../node_modules/split.js/grips/horizontal.png");
61 62 }
62 63
63   - .tb-split.tb-split-horizontal, .gutter.gutter-horizontal {
64   - height: 100%;
  64 + .tb-split.tb-split-horizontal,
  65 + .gutter.gutter-horizontal {
65 66 float: left;
  67 + height: 100%;
66 68 }
67 69
68 70 .tb-split.tb-split-vertical {
69 71 display: flex;
  72 +
70 73 .tb-split.tb-content {
71 74 height: 100%;
72 75 }
... ... @@ -74,42 +77,44 @@ md-dialog.tb-node-script-test-dialog {
74 77
75 78 div.tb-editor-area-title-panel {
76 79 position: absolute;
77   - font-size: 0.800rem;
78   - font-weight: 500;
79 80 top: 13px;
80 81 right: 40px;
81 82 z-index: 5;
  83 + font-size: .8rem;
  84 + font-weight: 500;
  85 +
82 86 &.tb-js-function {
83 87 right: 80px;
84 88 }
  89 +
85 90 label {
86   - color: #00acc1;
87   - background: rgba(220, 220, 220, 0.35);
88   - border-radius: 5px;
89 91 padding: 4px;
  92 + color: #00acc1;
90 93 text-transform: uppercase;
  94 + background: rgba(220, 220, 220, .35);
  95 + border-radius: 5px;
91 96 }
  97 +
92 98 .md-button {
93   - color: #7B7B7B;
94 99 min-width: 32px;
95 100 min-height: 15px;
96   - line-height: 15px;
97   - font-size: 0.800rem;
98   - margin: 0;
99 101 padding: 4px;
100   - background: rgba(220, 220, 220, 0.35);
  102 + margin: 0;
  103 + font-size: .8rem;
  104 + line-height: 15px;
  105 + color: #7b7b7b;
  106 + background: rgba(220, 220, 220, .35);
101 107 }
102 108 }
103 109
104 110 .tb-resize-container {
105   - overflow-y: auto;
106   - height: 100%;
107   - width: 100%;
108 111 position: relative;
  112 + width: 100%;
  113 + height: 100%;
  114 + overflow-y: auto;
109 115
110 116 .ace_editor {
111 117 height: 100%;
112 118 }
113 119 }
114   -
115 120 }
... ...
... ... @@ -13,20 +13,21 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 md-toast.tb-info-toast .md-toast-content {
17   - font-size: 18px;
18   - padding: 18px;
19 18 height: 100%;
  19 + padding: 18px;
  20 + font-size: 18px;
20 21 }
21 22
22 23 md-toast.tb-success-toast .md-toast-content {
23   - font-size: 18px !important;
24   - background-color: green;
25 24 height: 100%;
  25 + font-size: 18px !important;
  26 + background-color: #008000;
26 27 }
27 28
28 29 md-toast.tb-error-toast .md-toast-content {
29   - font-size: 18px !important;
30   - background-color: maroon;
31 30 height: 100%;
  31 + font-size: 18px !important;
  32 + background-color: #800000;
32 33 }
... ...
... ... @@ -13,18 +13,20 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -@import '../../scss/constants';
  16 +@import "../../scss/constants";
17 17
18 18 .tb-default-dashboard {
19 19 .tb-default-dashboard-label {
20 20 padding-bottom: 8px;
21 21 }
  22 +
22 23 tb-dashboard-autocomplete {
23 24 @media (min-width: $layout-breakpoint-sm) {
24 25 padding-right: 12px;
25 26 }
  27 +
26 28 @media (max-width: $layout-breakpoint-sm) {
27 29 padding-bottom: 12px;
28 30 }
29 31 }
30   -}
\ No newline at end of file
  32 +}
... ...
... ... @@ -13,11 +13,13 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-has-timewindow {
17 18 .tb-alarms-table {
18 19 md-toolbar {
19 20 min-height: 60px;
20 21 max-height: 60px;
  22 +
21 23 &.md-table-toolbar {
22 24 .md-toolbar-tools {
23 25 max-height: 60px;
... ... @@ -28,10 +30,10 @@
28 30 }
29 31
30 32 .tb-alarms-table {
31   -
32 33 md-toolbar {
33 34 min-height: 39px;
34 35 max-height: 39px;
  36 +
35 37 &.md-table-toolbar {
36 38 .md-toolbar-tools {
37 39 max-height: 39px;
... ... @@ -40,14 +42,15 @@
40 42 }
41 43
42 44 &.tb-data-table {
43   - table.md-table, table.md-table.md-row-select {
  45 + table.md-table,
  46 + table.md-table.md-row-select {
44 47 tbody {
45 48 tr {
46 49 td {
47 50 &.tb-action-cell {
  51 + width: 36px;
48 52 min-width: 36px;
49 53 max-width: 36px;
50   - width: 36px;
51 54 }
52 55 }
53 56 }
... ...
... ... @@ -13,11 +13,13 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 .tb-has-timewindow {
17 18 .tb-entities-table {
18 19 md-toolbar {
19 20 min-height: 60px;
20 21 max-height: 60px;
  22 +
21 23 &.md-table-toolbar {
22 24 .md-toolbar-tools {
23 25 max-height: 60px;
... ... @@ -28,10 +30,10 @@
28 30 }
29 31
30 32 .tb-entities-table {
31   -
32 33 md-toolbar {
33 34 min-height: 39px;
34 35 max-height: 39px;
  36 +
35 37 &.md-table-toolbar {
36 38 .md-toolbar-tools {
37 39 max-height: 39px;
... ... @@ -40,14 +42,15 @@
40 42 }
41 43
42 44 &.tb-data-table {
43   - table.md-table, table.md-table.md-row-select {
  45 + table.md-table,
  46 + table.md-table.md-row-select {
44 47 tbody {
45 48 tr {
46 49 td {
47 50 &.tb-action-cell {
  51 + width: 36px;
48 52 min-width: 36px;
49 53 max-width: 36px;
50   - width: 36px;
51 54 }
52 55 }
53 56 }
... ...
... ... @@ -13,16 +13,19 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 tb-extension-table {
17 18 md-content {
18 19 background-color: #fff;
19 20 }
20 21 }
  22 +
21 23 md-tabs.hide-tabs-menu {
22 24 md-tabs-wrapper {
23 25 display: none;
24 26 }
  27 +
25 28 md-tabs-content-wrapper {
26 29 top: 0 !important;
27 30 }
28   -}
\ No newline at end of file
  31 +}
... ...
... ... @@ -13,141 +13,156 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -$knob-img: url('./svg/knob.svg');
  16 +$knob-img: url("./svg/knob.svg") !default;
17 17
18   -$bars-margin-pct: percentage(0.033);
19   -$background-margin-pct: percentage(0.05);
20   -$value-container-margin-pct: percentage(0.35);
21   -$error-height: percentage(0.05);
22   -$title-height: percentage(0.066);
23   -$title-container-margin-pct: percentage(0.2);
24   -$title-container-margin-bottom-pct: percentage(0.05);
25   -$minmax-height: percentage(0.04);
26   -$minmax-container-margin-pct: percentage(0.18);
27   -$minmax-container-margin-bottom-pct: percentage(0.12);
  18 +$bars-margin-pct: percentage(.033) !default;
  19 +$background-margin-pct: percentage(.05) !default;
  20 +$value-container-margin-pct: percentage(.35) !default;
  21 +$error-height: percentage(.05) !default;
  22 +$title-height: percentage(.066) !default;
  23 +$title-container-margin-pct: percentage(.2) !default;
  24 +$title-container-margin-bottom-pct: percentage(.05) !default;
  25 +$minmax-height: percentage(.04) !default;
  26 +$minmax-container-margin-pct: percentage(.18) !default;
  27 +$minmax-container-margin-bottom-pct: percentage(.12) !default;
28 28
29   -$background-color: #e6e7e8;
  29 +$background-color: #e6e7e8 !default;
30 30
31 31 .tb-knob {
32   - width:100%;
33   - height:100%;
  32 + width: 100%;
  33 + height: 100%;
34 34 background: $background-color;
35 35
36 36 .knob {
37 37 position: relative;
  38 +
38 39 &[draggable] {
39 40 -moz-user-select: none;
40 41 -webkit-user-select: none;
41 42 user-select: none;
42 43 }
  44 +
43 45 #canvasBar {
44   - position:absolute;
45   - top:0;
46   - left:0;
47   - bottom: 0;
  46 + position: absolute;
  47 + top: 0;
48 48 right: 0;
  49 + bottom: 0;
  50 + left: 0;
49 51 z-index: 2;
50 52 }
  53 +
51 54 .canvas-background {
52   - position:absolute;
  55 + position: absolute;
53 56 top: $background-margin-pct;
54   - left: $background-margin-pct;
55   - bottom: $background-margin-pct;
56 57 right: $background-margin-pct;
57   - border-radius: 50%;
  58 + bottom: $background-margin-pct;
  59 + left: $background-margin-pct;
  60 + z-index: 2;
58 61 background: #3f4346;
59   - z-index:2;
  62 + border-radius: 50%;
60 63 }
  64 +
61 65 .value-container {
62   - position:absolute;
  66 + position: absolute;
63 67 top: $value-container-margin-pct;
64   - left: $value-container-margin-pct;
65   - bottom: $value-container-margin-pct;
66 68 right: $value-container-margin-pct;
67   - z-index:4;
  69 + bottom: $value-container-margin-pct;
  70 + left: $value-container-margin-pct;
  71 + z-index: 4;
  72 +
68 73 .knob-value {
69   - color: #fff;
70 74 font-weight: 500;
  75 + color: #fff;
71 76 white-space: nowrap;
72 77 }
73 78 }
  79 +
74 80 .error-container {
75   - position:absolute;
  81 + position: absolute;
76 82 top: 1%;
77   - left: 0;
78 83 right: 0;
79   - z-index:4;
  84 + left: 0;
  85 + z-index: 4;
80 86 height: $error-height;
  87 +
81 88 .knob-error {
82 89 color: #ff3315;
83 90 white-space: nowrap;
84 91 }
85 92 }
  93 +
86 94 .title-container {
87   - position:absolute;
88   - left: $title-container-margin-pct;
89   - bottom: $title-container-margin-bottom-pct;
  95 + position: absolute;
90 96 right: $title-container-margin-pct;
91   - z-index:4;
  97 + bottom: $title-container-margin-bottom-pct;
  98 + left: $title-container-margin-pct;
  99 + z-index: 4;
92 100 height: $title-height;
  101 +
93 102 .knob-title {
94   - color: #757575;
95 103 font-weight: 500;
  104 + color: #757575;
96 105 white-space: nowrap;
97 106 }
98 107 }
  108 +
99 109 .minmax-container {
100   - position:absolute;
101   - left: $minmax-container-margin-pct;
102   - bottom: $minmax-container-margin-bottom-pct;
  110 + position: absolute;
103 111 right: $minmax-container-margin-pct;
104   - z-index:4;
  112 + bottom: $minmax-container-margin-bottom-pct;
  113 + left: $minmax-container-margin-pct;
  114 + z-index: 4;
105 115 height: $minmax-height;
  116 +
106 117 .minmax-label {
107   - color: #757575;
108 118 font-weight: 500;
  119 + color: #757575;
109 120 white-space: nowrap;
110 121 }
111 122 }
  123 +
112 124 .top-pointer-container {
113   - position:absolute;
  125 + position: absolute;
114 126 top: $bars-margin-pct;
115   - left: $bars-margin-pct;
116   - bottom: $bars-margin-pct;
117 127 right: $bars-margin-pct;
118   - z-index:3;
119   - cursor:pointer !important;
  128 + bottom: $bars-margin-pct;
  129 + left: $bars-margin-pct;
  130 + z-index: 3;
  131 + cursor: pointer !important;
  132 +
120 133 .top-pointer {
121   - content:'';
122   - width:5%;
123   - height:5%;
124   - background-color:#b5b5b5;
125   - position:absolute;
126   - top:50%;
127   - left:22%;
128   - margin-top:-2.5%;
  134 + position: absolute;
  135 + top: 50%;
  136 + left: 22%;
  137 + width: 5%;
  138 + height: 5%;
  139 + margin-top: -2.5%;
  140 + cursor: pointer !important;
  141 + content: "";
  142 + background-color: #b5b5b5;
129 143 border-radius: 50%;
130   - cursor:pointer !important;
131 144 box-shadow: 1px 0 2px #040404;
132 145 }
133 146 }
  147 +
134 148 .top{
135   - position:absolute;
  149 + position: absolute;
136 150 top: $bars-margin-pct;
137   - left: $bars-margin-pct;
138   - bottom: $bars-margin-pct;
139 151 right: $bars-margin-pct;
140   - background:$knob-img no-repeat;
  152 + bottom: $bars-margin-pct;
  153 + left: $bars-margin-pct;
  154 + z-index: 2;
  155 + cursor: pointer !important;
  156 + background: $knob-img no-repeat;
141 157 background-size: contain;
142   - z-index:2;
143   - cursor:pointer !important;
144 158 }
  159 +
145 160 #text-measure {
146 161 position: absolute;
147   - visibility: hidden;
148   - height: auto;
149 162 width: auto;
  163 + height: auto;
150 164 white-space: nowrap;
  165 + visibility: hidden;
151 166 }
152 167 }
153 168 }
... ...
... ... @@ -15,61 +15,65 @@
15 15 */
16 16 @import "~compass-sass-mixins/lib/compass";
17 17
18   -$error-height: 14px;
  18 +$error-height: 14px !default;
19 19
20   -$background-color: #e6e7e8;
  20 +$background-color: #e6e7e8 !default;
21 21
22 22 .tb-led-indicator {
23   - width:100%;
24   - height:100%;
  23 + width: 100%;
  24 + height: 100%;
25 25 background: $background-color;
26 26
27 27 .title-container {
28 28 .led-title {
29   - color: #757575;
30 29 font-weight: 500;
  30 + color: #757575;
31 31 white-space: nowrap;
32 32 }
33 33 }
34 34
35 35 .error-container {
36   - position:absolute;
  36 + position: absolute;
37 37 top: 1%;
38   - left: 0;
39 38 right: 0;
40   - z-index:4;
  39 + left: 0;
  40 + z-index: 4;
41 41 height: $error-height;
  42 +
42 43 .led-error {
43 44 color: #ff3315;
44 45 white-space: nowrap;
45 46 }
46 47 }
  48 +
47 49 #text-measure {
48 50 position: absolute;
49   - visibility: hidden;
50   - height: auto;
51 51 width: auto;
  52 + height: auto;
52 53 white-space: nowrap;
  54 + visibility: hidden;
53 55 }
54 56
55 57 #led-container {
56 58 padding: 10px;
  59 +
57 60 .led {
58   - cursor: pointer;
59 61 position: relative;
  62 + cursor: pointer;
  63 + background-image: -owg-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, .25));
  64 + background-image: -webkit-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, .25));
  65 + background-image: -moz-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, .25));
  66 + background-image: -o-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, .25));
  67 + background-image: radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, .25));
60 68 border-radius: 50%;
61   - background-image: -owg-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25));
62   - background-image: -webkit-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25));
63   - background-image: -moz-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25));
64   - background-image: -o-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25));
65   - background-image: radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25));
66   - transition: background-color 0.5s, box-shadow 0.5s;
  69 + transition: background-color .5s, box-shadow .5s;
  70 +
67 71 &.disabled {
68   - background-image: -owg-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1));
69   - background-image: -webkit-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1));
70   - background-image: -moz-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1));
71   - background-image: -o-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1));
72   - background-image: radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1));
  72 + background-image: -owg-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, .5), rgba(0, 0, 0, .1));
  73 + background-image: -webkit-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, .5), rgba(0, 0, 0, .1));
  74 + background-image: -moz-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, .5), rgba(0, 0, 0, .1));
  75 + background-image: -o-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, .5), rgba(0, 0, 0, .1));
  76 + background-image: radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, .5), rgba(0, 0, 0, .1));
73 77 }
74 78 }
75 79 }
... ...
... ... @@ -15,179 +15,201 @@
15 15 */
16 16 @import "~compass-sass-mixins/lib/compass";
17 17
18   -$error-height: 14px;
  18 +$error-height: 14px !default;
19 19
20   -$background-color: #e6e7e8;
  20 +$background-color: #e6e7e8 !default;
21 21
22 22 .tb-round-switch {
23   - width:100%;
24   - height:100%;
  23 + width: 100%;
  24 + height: 100%;
25 25 background: $background-color;
26 26
27 27 .title-container {
28 28 .switch-title {
29   - color: #757575;
30 29 font-weight: 500;
  30 + color: #757575;
31 31 white-space: nowrap;
32 32 }
33 33 }
34 34
35 35 .error-container {
36   - position:absolute;
  36 + position: absolute;
37 37 top: 1%;
38   - left: 0;
39 38 right: 0;
40   - z-index:4;
  39 + left: 0;
  40 + z-index: 4;
41 41 height: $error-height;
  42 +
42 43 .switch-error {
43 44 color: #ff3315;
44 45 white-space: nowrap;
45 46 }
46 47 }
  48 +
47 49 #text-measure {
48 50 position: absolute;
49   - visibility: hidden;
50   - height: auto;
51 51 width: auto;
  52 + height: auto;
52 53 white-space: nowrap;
  54 + visibility: hidden;
53 55 }
54 56
55 57 #switch-container {
56 58 padding: 10px;
  59 +
57 60 .switch {
58   - cursor: pointer;
59 61 position: relative;
60   - background:#ddd;
  62 + width: 260px;
  63 + min-width: 260px;
  64 + height: 260px;
  65 + min-height: 260px;
  66 + padding: 25px;
  67 + font-family: sans-serif;
  68 + font-size: 48px;
  69 +
  70 + color: #424242;
  71 + cursor: pointer;
  72 + -pie-background: -pie-linear-gradient(270deg, #bbb, #ddd);
  73 + background: #ddd;
61 74 background: -owg-linear-gradient(270deg, #bbb, #ddd);
62 75 background: -webkit-linear-gradient(270deg, #bbb, #ddd);
63 76 background: -moz-linear-gradient(270deg, #bbb, #ddd);
64 77 background: -o-linear-gradient(270deg, #bbb, #ddd);
65   - -pie-background: -pie-linear-gradient(270deg, #bbb, #ddd);
66 78 background: linear-gradient(180deg, #bbb, #ddd);
67   - border-radius:130px;
  79 + border-radius: 130px;
  80 +
68 81 @include box-sizing(border-box);
69   - @include box-shadow(
70   - 0px 0px 0px 8px rgba(0,0,0,.1)
71   - ,0px 0px 3px 1px rgba(0,0,0,.1)
72   - ,inset 0 8px 3px -8px rgba(255,255,255,.4));
73   - height: 260px;
74   - min-height: 260px;
75   - padding: 25px;
76   - width: 260px;
77   - min-width: 260px;
78 82
79   - color: #424242;
80   - font-family:sans-serif;
81   - font-size:48px;
  83 + @include box-shadow(
  84 + 0 0 0 8px rgba(0,0,0,.1)
  85 + ,0 0 3px 1px rgba(0,0,0,.1)
  86 + ,inset 0 8px 3px -8px rgba(255,255,255,.4));
82 87
83 88 input {
84   - display:none
  89 + display: none;
85 90 }
86 91
87   - .on,.off {
88   - position:absolute;
89   - text-align:center;
  92 + .on,
  93 + .off {
  94 + position: absolute;
  95 + width: 100%;
  96 + text-align: center;
  97 +
90 98 @include text-shadow(1px 1px 4px #4a4a4a);
91   - width:100%;
92 99 }
93 100
94 101 .on {
95   - color:#444;
96   - top:10px;
97   - @include transition(all 0.1s);
98   - font-family:sans-serif
  102 + top: 10px;
  103 + font-family: sans-serif;
  104 + color: #444;
  105 +
  106 + @include transition(all .1s);
99 107 }
100 108
101 109 .off {
102   - bottom:5px;
103   - @include transition(all 0.1s);
104   - @include transform(scaleY(0.85));
  110 + bottom: 5px;
  111 +
  112 + @include transition(all .1s);
  113 +
  114 + @include transform(scaleY(.85));
105 115 }
106 116
107 117 .but {
  118 + position: relative;
  119 + display: block;
  120 + width: 200px;
  121 + height: 178px;
  122 + font-size: 48px;
108 123 cursor: pointer;
109   - background-color:#d8d8d8;
  124 + background-color: #d8d8d8;
  125 + border-bottom-width: 0;
110 126 border-radius: 400px 400px 400px 400px / 400px 400px 300px 300px;
111   - border-bottom-width:0px;
  127 +
112 128 @include box-shadow(inset 8px 6px 5px -7px #a2a2a2,
113   - inset -8px 6px 5px -7px #a2a2a2,
114   - inset 0 -3px 2px -2px rgba(200, 200, 200, 0.5),
115   - 0 3px 3px -2px #ffffff,
116   - inset 0 -230px 60px -200px rgba(255, 255, 255, 0.2),
117   - inset 0 220px 40px -200px rgba(0, 0, 0, 0.3));
118   - display:block;
119   - font-size:48px;
120   - height:178px;
121   - position:relative;
122   - @include transition(all 0.2s);
123   - width:200px;
  129 + inset -8px 6px 5px -7px #a2a2a2,
  130 + inset 0 -3px 2px -2px rgba(200, 200, 200, .5),
  131 + 0 3px 3px -2px #fff,
  132 + inset 0 -230px 60px -200px rgba(255, 255, 255, .2),
  133 + inset 0 220px 40px -200px rgba(0, 0, 0, .3));
  134 +
  135 + @include transition(all .2s);
124 136 }
125 137
126 138 .back {
  139 + width: 210px;
  140 + height: 210px;
  141 + padding: 4px 4px;
127 142 cursor: pointer;
128 143 background-color: #888787;
129   - background-image: -owg-linear-gradient(0deg, transparent 30%, transparent 70%), -owg-linear-gradient(90deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, 0.2) 50%, rgba(150, 150, 150, 0) 70%);
130   - background-image: -webkit-linear-gradient(0deg, transparent 30%, transparent 70%), -webkit-linear-gradient(90deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, 0.2) 50%, rgba(150, 150, 150, 0) 70%);
131   - background-image: -moz-linear-gradient(0deg, transparent 30%, transparent 70%), -moz-linear-gradient(90deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, 0.2) 50%, rgba(150, 150, 150, 0) 70%);
132   - background-image: -o-linear-gradient(0deg, transparent 30%, transparent 70%), -o-linear-gradient(90deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, 0.2) 50%, rgba(150, 150, 150, 0) 70%);
133   - background-image: linear-gradient(-90deg, transparent 30%, transparent 70%), linear-gradient(0deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, 0.2) 50%, rgba(150, 150, 150, 0) 70%);
134   - border-radius:105px;
135   - @include box-shadow(30px 30px 30px -20px rgba(58, 58, 58, 0.3),
136   - -30px 30px 30px -20px rgba(58, 58, 58, 0.3),
137   - 0 30px 30px 0px rgba(16, 16, 16, 0.3),
138   - inset 0 -1px 0 0 #484848);
  144 + background-image: -owg-linear-gradient(0deg, transparent 30%, transparent 70%), -owg-linear-gradient(90deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, .2) 50%, rgba(150, 150, 150, 0) 70%);
  145 + background-image: -webkit-linear-gradient(0deg, transparent 30%, transparent 70%), -webkit-linear-gradient(90deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, .2) 50%, rgba(150, 150, 150, 0) 70%);
  146 + background-image: -moz-linear-gradient(0deg, transparent 30%, transparent 70%), -moz-linear-gradient(90deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, .2) 50%, rgba(150, 150, 150, 0) 70%);
  147 + background-image: -o-linear-gradient(0deg, transparent 30%, transparent 70%), -o-linear-gradient(90deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, .2) 50%, rgba(150, 150, 150, 0) 70%);
  148 + background-image: linear-gradient(-90deg, transparent 30%, transparent 70%), linear-gradient(0deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, .2) 50%, rgba(150, 150, 150, 0) 70%);
  149 + border-radius: 105px;
  150 +
  151 + @include box-shadow(30px 30px 30px -20px rgba(58, 58, 58, .3),
  152 + -30px 30px 30px -20px rgba(58, 58, 58, .3),
  153 + 0 30px 30px 0 rgba(16, 16, 16, .3),
  154 + inset 0 -1px 0 0 #484848);
  155 +
139 156 @include box-sizing(border-box);
140   - height:210px;
141   - padding:4px 4px;
142   - @include transition(all 0.2s);
143   - width:210px;
  157 +
  158 + @include transition(all .2s);
144 159 }
145 160
146 161
147   - input:checked + .back .on,input:checked + .back .off{
  162 + input:checked + .back .on,
  163 + input:checked + .back .off{
148 164 @include text-shadow(1px 1px 4px #4a4a4a);
149 165 }
  166 +
150 167 input:checked + .back .on{
151   - color:#4c4c4c;
152   - top:10px;
153   - @include transform(scaleY(0.85));
  168 + top: 10px;
  169 + color: #4c4c4c;
  170 +
  171 + @include transform(scaleY(.85));
154 172 }
  173 +
155 174 input:checked + .back .off{
156   - color:#444;
157   - bottom:5px;
  175 + bottom: 5px;
  176 + color: #444;
  177 +
158 178 @include transform(scaleY(1));
159 179 }
  180 +
160 181 input:checked + .back .but{
161   - background:#dcdcdc;
162   - background-image: -owg-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, 0.3), transparent);
163   - background-image: -webkit-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, 0.3), transparent);
164   - background-image: -moz-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, 0.3), transparent);
165   - background-image: -o-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, 0.3), transparent);
166   - background-image: radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, 0.3), transparent);
  182 + margin-top: 20px;
  183 + background: #dcdcdc;
  184 + background-image: -owg-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, .3), transparent);
  185 + background-image: -webkit-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, .3), transparent);
  186 + background-image: -moz-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, .3), transparent);
  187 + background-image: -o-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, .3), transparent);
  188 + background-image: radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, .3), transparent);
167 189 border-radius: 400px 400px 400px 400px / 300px 300px 400px 400px;
  190 +
168 191 @include box-shadow(inset 8px -4px 5px -7px #a9a9a9,
169   - inset -8px -4px 5px -7px #808080,
170   - 0 -3px 8px -4px rgba(50, 50, 50, 0.4),
171   - inset 0 3px 4px -2px #9c9c9c,
172   - inset 0 280px 40px -200px rgba(0, 0, 0, 0.2),
173   - inset 0 -200px 40px -200px rgba(180, 180, 180, 0.2));
174   - margin-top:20px;
  192 + inset -8px -4px 5px -7px #808080,
  193 + 0 -3px 8px -4px rgba(50, 50, 50, .4),
  194 + inset 0 3px 4px -2px #9c9c9c,
  195 + inset 0 280px 40px -200px rgba(0, 0, 0, .2),
  196 + inset 0 -200px 40px -200px rgba(180, 180, 180, .2));
175 197 }
176   - input:checked + .back{
177 198
178   - background-image: -owg-linear-gradient(90deg, #868686 30%, transparent 70%), -owg-linear-gradient(180deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, 0.74) 50%, rgba(105, 105, 105, 0) 100%);
179   - background-image: -webkit-linear-gradient(90deg, #868686 30%, transparent 70%), -webkit-linear-gradient(180deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, 0.74) 50%, rgba(105, 105, 105, 0) 100%);
180   - background-image: -moz-linear-gradient(90deg, #868686 30%, transparent 70%), -moz-linear-gradient(180deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, 0.74) 50%, rgba(105, 105, 105, 0) 100%);
181   - background-image: -o-linear-gradient(90deg, #868686 30%, transparent 70%), -o-linear-gradient(180deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, 0.74) 50%, rgba(105, 105, 105, 0) 100%);
182   - background-image: linear-gradient(0deg, #868686 30%, transparent 70%), linear-gradient(90deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, 0.74) 50%, rgba(105, 105, 105, 0) 100%);
183   -
184   - @include box-shadow(30px 30px 30px -20px rgba(49, 49, 49, 0.1),
185   - -30px 30px 30px -20px rgba(111, 111, 111, 0.1),
186   - 0 30px 30px 0px rgba(0, 0, 0, 0.2),
187   - inset 0 1px 2px 0 rgba(167, 167, 167, 0.6));
188   - padding:2px 4px;
  199 + input:checked + .back{
  200 + padding: 2px 4px;
  201 +
  202 + background-image: -owg-linear-gradient(90deg, #868686 30%, transparent 70%), -owg-linear-gradient(180deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, .74) 50%, rgba(105, 105, 105, 0) 100%);
  203 + background-image: -webkit-linear-gradient(90deg, #868686 30%, transparent 70%), -webkit-linear-gradient(180deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, .74) 50%, rgba(105, 105, 105, 0) 100%);
  204 + background-image: -moz-linear-gradient(90deg, #868686 30%, transparent 70%), -moz-linear-gradient(180deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, .74) 50%, rgba(105, 105, 105, 0) 100%);
  205 + background-image: -o-linear-gradient(90deg, #868686 30%, transparent 70%), -o-linear-gradient(180deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, .74) 50%, rgba(105, 105, 105, 0) 100%);
  206 + background-image: linear-gradient(0deg, #868686 30%, transparent 70%), linear-gradient(90deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, .74) 50%, rgba(105, 105, 105, 0) 100%);
  207 +
  208 + @include box-shadow(30px 30px 30px -20px rgba(49, 49, 49, .1),
  209 + -30px 30px 30px -20px rgba(111, 111, 111, .1),
  210 + 0 30px 30px 0 rgba(0, 0, 0, .2),
  211 + inset 0 1px 2px 0 rgba(167, 167, 167, .6));
189 212 }
190   -
191 213 }
192 214 }
193 215 }
... ...
... ... @@ -13,27 +13,28 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -$thumb-img: url('./svg/thumb.svg');
17   -$thumb-checked-img: url('./svg/thumb-checked.svg');
18   -$thumb-bar-img: url('./svg/thumb-bar.svg');
19   -$thumb-bar-checked-img: url('./svg/thumb-bar-checked.svg');
  16 +$thumb-img: url("./svg/thumb.svg") !default;
  17 +$thumb-checked-img: url("./svg/thumb-checked.svg") !default;
  18 +$thumb-bar-img: url("./svg/thumb-bar.svg") !default;
  19 +$thumb-bar-checked-img: url("./svg/thumb-bar-checked.svg") !default;
20 20
21   -$background-color: #e6e7e8;
  21 +$background-color: #e6e7e8 !default;
22 22
23   -$error-height: 14px;
  23 +$error-height: 14px !default;
24 24
25 25 .tb-switch {
26   - width:100%;
27   - height:100%;
  26 + width: 100%;
  27 + height: 100%;
28 28 background: $background-color;
29 29
30 30 .error-container {
31   - position:absolute;
  31 + position: absolute;
32 32 top: 1%;
33   - left: 0;
34 33 right: 0;
35   - z-index:4;
  34 + left: 0;
  35 + z-index: 4;
36 36 height: $error-height;
  37 +
37 38 .switch-error {
38 39 color: #ff3315;
39 40 white-space: nowrap;
... ... @@ -42,62 +43,71 @@ $error-height: 14px;
42 43
43 44 .onoff-container {
44 45 height: 100%;
45   - color: #757575;
46 46 font-weight: 500;
  47 + color: #757575;
47 48 white-space: nowrap;
  49 +
48 50 .off-label {
49 51 color: #b7b5b5;
50 52 }
  53 +
51 54 .on-label {
52 55 color: #ff7e57;
53 56 text-shadow: #ff6e4a 1px 1px 10px, #ffd1c3 1px 1px 10px;
54 57 }
55 58 }
  59 +
56 60 .title-container {
57 61 .switch-title {
58   - color: #757575;
59 62 font-weight: 500;
  63 + color: #757575;
60 64 white-space: nowrap;
61 65 }
62 66 }
63 67
64 68 #switch-container {
65   - padding-left: 10px;
66   - padding-right: 10px;
  69 + padding-right: 10px;
  70 + padding-left: 10px;
67 71 }
  72 +
68 73 .switch {
69 74 position: relative;
  75 +
70 76 md-switch {
71   - margin: 0;
72   - position:absolute;
  77 + position: absolute;
73 78 top: 0;
74   - left: 0;
75   - bottom: 0;
76 79 right: 0;
  80 + bottom: 0;
  81 + left: 0;
  82 + margin: 0;
  83 +
77 84 .md-container {
78 85 margin: 0;
79 86 }
  87 +
80 88 .md-bar {
  89 + top: 0;
81 90 left: 0;
82 91 width: 100%;
83   - top: 0;
84 92 height: 100%;
85   - border-radius: 0;
86   - background:$thumb-bar-img no-repeat;
  93 + background: $thumb-bar-img no-repeat;
87 94 background-size: contain;
  95 + border-radius: 0;
88 96 }
  97 +
89 98 .md-thumb-container {
90   - left: 0.25%;
91   - width: 50%;
92 99 top: 5%;
  100 + left: .25%;
  101 + width: 50%;
93 102 height: 90%;
94 103 }
  104 +
95 105 .md-thumb {
96 106 top: 0;
97 107 left: 0;
98   - height: 100%;
99 108 width: 100%;
100   - background:$thumb-img no-repeat;
  109 + height: 100%;
  110 + background: $thumb-img no-repeat;
101 111 background-size: contain;
102 112 border-radius: 0;
103 113 box-shadow: none;
... ... @@ -105,22 +115,23 @@ $error-height: 14px;
105 115
106 116 &.md-checked {
107 117 .md-bar {
108   - background:$thumb-bar-checked-img no-repeat;
  118 + background: $thumb-bar-checked-img no-repeat;
109 119 background-size: contain;
110 120 }
  121 +
111 122 .md-thumb {
112   - background:$thumb-checked-img no-repeat;
  123 + background: $thumb-checked-img no-repeat;
113 124 background-size: contain;
114 125 }
115 126 }
116   -
117 127 }
118 128 }
  129 +
119 130 #text-measure {
120 131 position: absolute;
121   - visibility: hidden;
122   - height: auto;
123 132 width: auto;
  133 + height: auto;
124 134 white-space: nowrap;
  135 + visibility: hidden;
125 136 }
126 137 }
... ...
... ... @@ -13,20 +13,22 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
16 17 tb-timeseries-table-widget {
17   - table.md-table thead.md-head>tr.md-row {
18   - height: 40px;
19   - }
  18 + table.md-table thead.md-head > tr.md-row {
  19 + height: 40px;
  20 + }
20 21
21   - table.md-table tbody.md-body>tr.md-row, table.md-table tfoot.md-foot>tr.md-row {
22   - height: 38px;
23   - }
  22 + table.md-table tbody.md-body > tr.md-row,
  23 + table.md-table tfoot.md-foot > tr.md-row {
  24 + height: 38px;
  25 + }
24 26
25   - .md-table-pagination>* {
26   - height: 46px;
27   - }
  27 + .md-table-pagination > * {
  28 + height: 46px;
  29 + }
28 30
29   - .tb-data-table md-toolbar {
30   - z-index: 10;
31   - }
  31 + .tb-data-table md-toolbar {
  32 + z-index: 10;
  33 + }
32 34 }
... ...
... ... @@ -15,13 +15,13 @@
15 15 */
16 16 @import "~compass-sass-mixins/lib/compass";
17 17
18   -$edit-toolbar-height: 40px;
  18 +$edit-toolbar-height: 40px !default;
19 19
20 20 .tb-editor {
21 21 .tb-split {
22 22 @include box-sizing(border-box);
23   - overflow-y: auto;
24 23 overflow-x: hidden;
  24 + overflow-y: auto;
25 25 }
26 26
27 27 .ace_editor {
... ... @@ -29,7 +29,7 @@ $edit-toolbar-height: 40px;
29 29 }
30 30
31 31 .tb-content {
32   - border: 1px solid #C0C0C0;
  32 + border: 1px solid #c0c0c0;
33 33 }
34 34
35 35 .gutter {
... ... @@ -41,21 +41,23 @@ $edit-toolbar-height: 40px;
41 41
42 42 .gutter.gutter-horizontal {
43 43 cursor: col-resize;
44   - background-image: url('../../../node_modules/split.js/grips/vertical.png');
  44 + background-image: url("../../../node_modules/split.js/grips/vertical.png");
45 45 }
46 46
47 47 .gutter.gutter-vertical {
48 48 cursor: row-resize;
49   - background-image: url('../../../node_modules/split.js/grips/horizontal.png');
  49 + background-image: url("../../../node_modules/split.js/grips/horizontal.png");
50 50 }
51 51
52   - .tb-split.tb-split-horizontal, .gutter.gutter-horizontal {
53   - height: 100%;
  52 + .tb-split.tb-split-horizontal,
  53 + .gutter.gutter-horizontal {
54 54 float: left;
  55 + height: 100%;
55 56 }
56 57
57 58 .tb-split.tb-split-vertical {
58 59 display: flex;
  60 +
59 61 .tb-split.tb-content {
60 62 height: 100%;
61 63 }
... ... @@ -64,7 +66,6 @@ $edit-toolbar-height: 40px;
64 66
65 67 .tb-split-vertical {
66 68 md-tabs {
67   -
68 69 md-tabs-content-wrapper {
69 70 height: calc(100% - 49px);
70 71
... ... @@ -74,45 +75,44 @@ $edit-toolbar-height: 40px;
74 75 & > div {
75 76 height: 100%;
76 77 }
77   -
78 78 }
79   -
80 79 }
81   -
82 80 }
83 81 }
84 82
85 83 div.tb-editor-area-title-panel {
86 84 position: absolute;
87   - font-size: 0.800rem;
88   - font-weight: 500;
89 85 top: 5px;
90 86 right: 20px;
91 87 z-index: 5;
  88 + font-size: .8rem;
  89 + font-weight: 500;
  90 +
92 91 label {
93   - color: #00acc1;
94   - background: rgba(220, 220, 220, 0.35);
95   - border-radius: 5px;
96 92 padding: 4px;
  93 + color: #00acc1;
97 94 text-transform: uppercase;
  95 + background: rgba(220, 220, 220, .35);
  96 + border-radius: 5px;
98 97 }
  98 +
99 99 .md-button {
100   - color: #7B7B7B;
101 100 min-width: 32px;
102 101 min-height: 15px;
103   - line-height: 15px;
104   - font-size: 0.800rem;
105   - margin: 0;
106 102 padding: 4px;
107   - background: rgba(220, 220, 220, 0.35);
  103 + margin: 0;
  104 + font-size: .8rem;
  105 + line-height: 15px;
  106 + color: #7b7b7b;
  107 + background: rgba(220, 220, 220, .35);
108 108 }
109 109 }
110 110
111 111 .tb-resize-container {
112   - overflow-y: auto;
113   - height: 100%;
114   - width: 100%;
115 112 position: relative;
  113 + width: 100%;
  114 + height: 100%;
  115 + overflow-y: auto;
116 116
117 117 .ace_editor {
118 118 height: 100%;
... ... @@ -127,24 +127,28 @@ md-toolbar.tb-edit-toolbar {
127 127 .md-toolbar-tools {
128 128 min-height: $edit-toolbar-height !important;
129 129 max-height: $edit-toolbar-height !important;
  130 +
130 131 .md-button {
131 132 min-width: 65px;
132 133 min-height: 30px;
133   - line-height: 30px;
134 134 font-size: 12px;
  135 + line-height: 30px;
  136 +
135 137 md-icon {
136 138 font-size: 20px;
137 139 }
  140 +
138 141 span {
139 142 padding-right: 6px;
140 143 }
141 144 }
  145 +
142 146 md-input-container {
143 147 input {
144   - font-size: 1.200rem;
145   - font-weight: 400;
146   - letter-spacing: 0.005em;
147 148 height: 28px;
  149 + font-size: 1.2rem;
  150 + font-weight: 400;
  151 + letter-spacing: .005em;
148 152 }
149 153 }
150 154 }
... ...
... ... @@ -18,6 +18,7 @@
18 18 @include keyframes(tbMoveFromTopFade) {
19 19 from {
20 20 opacity: 0;
  21 +
21 22 @include transform(translate(0, -100%));
22 23 }
23 24 }
... ... @@ -25,6 +26,7 @@
25 26 @include keyframes(tbMoveToTopFade) {
26 27 to {
27 28 opacity: 0;
  29 +
28 30 @include transform(translate(0, -100%));
29 31 }
30 32 }
... ... @@ -32,6 +34,7 @@
32 34 @include keyframes(tbMoveFromBottomFade) {
33 35 from {
34 36 opacity: 0;
  37 +
35 38 @include transform(translate(0, 100%));
36 39 }
37 40 }
... ... @@ -39,6 +42,7 @@
39 42 @include keyframes(tbMoveToBottomFade) {
40 43 to {
41 44 opacity: 0;
  45 +
42 46 @include transform(translate(0, 150%));
43 47 }
44   -}
\ No newline at end of file
  48 +}
... ...
... ... @@ -17,18 +17,18 @@
17 17
18 18 // Colors
19 19
20   -$gray: #eee;
  20 +$gray: #eee !default;
21 21
22   -$primary-palette-color: 'indigo';
23   -$default: '500';
24   -$hue-1: '300';
25   -$hue-2: '800';
26   -$hue-3: 'a100';
  22 +$primary-palette-color: "indigo" !default;
  23 +$default: "500" !default;
  24 +$hue-1: "300" !default;
  25 +$hue-2: "800" !default;
  26 +$hue-3: "a100" !default;
27 27
28   -$primary-default: #305680; //material-color($primary-palette-color, $default);
29   -$primary-hue-1: material-color($primary-palette-color, $hue-1);
30   -$primary-hue-2: material-color($primary-palette-color, $hue-2);
31   -$primary-hue-3: rgb(207, 216, 220);
  28 +$primary-default: #305680 !default; //material-color($primary-palette-color, $default);
  29 +$primary-hue-1: material-color($primary-palette-color, $hue-1) !default;
  30 +$primary-hue-2: material-color($primary-palette-color, $hue-2) !default;
  31 +$primary-hue-3: rgb(207, 216, 220) !default;
32 32
33 33 // Layout
34 34
... ...
... ... @@ -14,8 +14,8 @@
14 14 * limitations under the License.
15 15 */
16 16 @font-face {
17   - font-family: 'Segment7Standard';
18   - src: url('data:font/opentype;charset=utf-8;base64,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AAEAAAAIAAAAAAAEAAAAAAAAAAEAAAAAzD2izwAAAADPr89TAAAAAM+4fiECSQAAAkkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABpwBCAC8ALwAvAFwBpwAvAC8ALwBcAC8AXAIBAC8ALwGWAC8ALwBCAC4ALgBYAC8ALwBcAacALwAvAC8ALwAvAC8ALwAvAC8ALwAvAC8ALwGWAC8ALwAvAC8ALwAvAC8AQgAvAC4ALwAvAC8ALwAvAC8ALwAvAEIALwBCAFwBpwAvAC8ALwAvAC8ALwAvAC8BlgAvAC8ALwAvAC8ALwAvAEIALwAuAC8ALwAvAC8ALwAvAC8AAAGWAAAALwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAAAAAAAAAvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC8ALwAAAAAAAAAAAAAAAAAAAC8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') format('opentype');
19   - font-weight: normal;
  17 + font-family: "Segment7Standard";
20 18 font-style: italic;
21   -}
\ No newline at end of file
  19 + font-weight: 400;
  20 + src: url("data:font/opentype;charset=utf-8;base64,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AAEAAAAIAAAAAAAEAAAAAAAAAAEAAAAAzD2izwAAAADPr89TAAAAAM+4fiECSQAAAkkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABpwBCAC8ALwAvAFwBpwAvAC8ALwBcAC8AXAIBAC8ALwGWAC8ALwBCAC4ALgBYAC8ALwBcAacALwAvAC8ALwAvAC8ALwAvAC8ALwAvAC8ALwGWAC8ALwAvAC8ALwAvAC8AQgAvAC4ALwAvAC8ALwAvAC8ALwAvAEIALwBCAFwBpwAvAC8ALwAvAC8ALwAvAC8BlgAvAC8ALwAvAC8ALwAvAEIALwAuAC8ALwAvAC8ALwAvAC8AAAGWAAAALwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAAAAAAAAAvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC8ALwAAAAAAAAAAAAAAAAAAAC8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") format("opentype");
  21 +}
... ...
... ... @@ -14,77 +14,92 @@
14 14 * limitations under the License.
15 15 */
16 16 @import "~compass-sass-mixins/lib/compass";
  17 +
17 18 @import "constants";
  19 +
18 20 @import "animations";
  21 +
19 22 @import "mixins";
  23 +
20 24 @import "fonts";
21 25
22 26 /***************
23 27 * TYPE DEFAULTS
24 28 ***************/
25 29
26   -button, html, input, select, textarea {
27   - font-family: Roboto, 'Helvetica Neue', sans-serif;
  30 +button,
  31 +html,
  32 +input,
  33 +select,
  34 +textarea {
  35 + font-family: Roboto, "Helvetica Neue", sans-serif;
28 36 }
29 37
30 38 .mdi-set {
31 39 line-height: 1;
32   - letter-spacing: normal;
33 40 text-transform: none;
34   - white-space: nowrap;
  41 + letter-spacing: normal;
35 42 word-wrap: normal;
  43 + white-space: nowrap;
36 44 direction: ltr;
37   - -webkit-font-feature-settings: 'liga';
  45 + -webkit-font-feature-settings: "liga";
38 46 }
39 47
40 48 a {
41   - color: #106CC8;
42   - text-decoration: none;
43 49 font-weight: 400;
44   - border-bottom: 1px solid rgba(64, 84, 178, 0.25);
45   - @include transition(border-bottom 0.35s);
  50 + color: #106cc8;
  51 + text-decoration: none;
  52 + border-bottom: 1px solid rgba(64, 84, 178, .25);
  53 +
  54 + @include transition(border-bottom .35s);
46 55 }
47 56
48   -a:hover, a:focus {
49   - border-bottom: 1px solid #4054B2;
  57 +a:hover,
  58 +a:focus {
  59 + border-bottom: 1px solid #4054b2;
50 60 }
51 61
52   -h1, h2, h3, h4, h5, h6 {
53   - margin-bottom: 1rem;
  62 +h1,
  63 +h2,
  64 +h3,
  65 +h4,
  66 +h5,
  67 +h6 {
54 68 margin-top: 1rem;
  69 + margin-bottom: 1rem;
55 70 }
56 71
57 72 h1 {
58   - font-size: 3.400rem;
  73 + font-size: 3.4rem;
59 74 font-weight: 400;
60 75 line-height: 4rem;
61 76 }
62 77
63 78 h2 {
64   - font-size: 2.400rem;
  79 + font-size: 2.4rem;
65 80 font-weight: 400;
66 81 line-height: 3.2rem;
67 82 }
68 83
69 84 h3 {
70   - font-size: 2.000rem;
  85 + font-size: 2rem;
71 86 font-weight: 500;
72   - letter-spacing: 0.005em;
  87 + letter-spacing: .005em;
73 88 }
74 89
75 90 h4 {
76   - font-size: 1.600rem;
  91 + font-size: 1.6rem;
77 92 font-weight: 400;
78   - letter-spacing: 0.010em;
79 93 line-height: 2.4rem;
  94 + letter-spacing: .01em;
80 95 }
81 96
82 97 p {
  98 + margin: .8em 0 1.6em;
83 99 font-size: 1.6rem;
84 100 font-weight: 400;
85   - letter-spacing: 0.010em;
86 101 line-height: 1.6em;
87   - margin: 0.8em 0 1.6em;
  102 + letter-spacing: .01em;
88 103 }
89 104
90 105 strong {
... ... @@ -92,16 +107,16 @@ strong {
92 107 }
93 108
94 109 blockquote {
95   - border-left: 3px solid rgba(0, 0, 0, 0.12);
96   - font-style: italic;
97   - margin-left: 0;
98 110 padding-left: 16px;
  111 + margin-left: 0;
  112 + font-style: italic;
  113 + border-left: 3px solid rgba(0, 0, 0, .12);
99 114 }
100 115
101 116 fieldset {
102   - border: none;
103 117 padding: 0;
104 118 margin: 0;
  119 + border: none;
105 120 }
106 121
107 122 /*********************************
... ... @@ -119,26 +134,28 @@ form {
119 134 }
120 135
121 136 md-bottom-sheet .md-subheader {
122   - font-family: Roboto, 'Helvetica Neue', sans-serif;
  137 + font-family: Roboto, "Helvetica Neue", sans-serif;
123 138 }
124 139
125 140 .md-chips {
126   - font-family: Roboto, 'Helvetica Neue', sans-serif;
  141 + font-family: Roboto, "Helvetica Neue", sans-serif;
127 142 }
128 143
129   -md-content.md-default-theme, md-content {
  144 +md-content.md-default-theme,
  145 +md-content {
130 146 background-color: $gray;
131 147 }
132 148
133 149 md-card {
134 150 background-color: #fff;
  151 +
135 152 h2:first-of-type {
136 153 margin-top: 0;
137 154 }
138 155 }
139 156
140 157 .md-button:not([disabled]).md-icon-button:hover {
141   - background-color: rgba(158, 158, 158, 0.2);
  158 + background-color: rgba(158, 158, 158, .2);
142 159 }
143 160
144 161 md-toolbar:not(.md-hue-1),
... ... @@ -147,11 +164,12 @@ md-toolbar:not(.md-hue-1),
147 164 }
148 165
149 166 md-toolbar md-input-container .md-errors-spacer {
150   - min-height: 0px;
  167 + min-height: 0;
151 168 }
152 169
153 170 md-toolbar {
154   - md-select.md-default-theme:not([disabled]):focus .md-select-value, md-select:not([disabled]):focus .md-select-value {
  171 + md-select.md-default-theme:not([disabled]):focus .md-select-value,
  172 + md-select:not([disabled]):focus .md-select-value {
155 173 color: #fff;
156 174 }
157 175 }
... ... @@ -159,11 +177,10 @@ md-toolbar {
159 177 md-menu-item {
160 178 overflow: hidden;
161 179 fill: #737373;
162   -}
163 180
164   -md-menu-item {
165 181 .md-button {
166 182 display: block;
  183 +
167 184 .tb-alt-text {
168 185 float: right;
169 186 }
... ... @@ -190,13 +207,14 @@ md-sidenav {
190 207 overflow-y: auto;
191 208 }
192 209
193   -.md-radio-interactive input, button {
  210 +.md-radio-interactive input,
  211 +button {
194 212 pointer-events: all;
195 213 }
196 214
197 215 .md-color-picker-input-container {
198 216 md-input-container {
199   - margin-bottom: 0px;
  217 + margin-bottom: 0;
200 218 }
201 219 }
202 220
... ... @@ -204,32 +222,36 @@ md-sidenav {
204 222 * THINGSBOARD SPECIFIC
205 223 ***********************/
206 224
207   -$swift-ease-out-duration: 0.4s !default;
208   -$swift-ease-out-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1) !default;
  225 +$swift-ease-out-duration: .4s !default;
  226 +$swift-ease-out-timing-function: cubic-bezier(.25, .8, .25, 1) !default;
209 227
210 228 $input-label-float-offset: 6px !default;
211   -$input-label-float-scale: 0.75 !default;
  229 +$input-label-float-scale: .75 !default;
212 230
213 231 label {
214 232 &.tb-title {
215   - pointer-events: none;
216   - color: #666;
  233 + padding-bottom: 15px;
217 234 font-size: 13px;
218 235 font-weight: 400;
219   - padding-bottom: 15px;
  236 + color: #666;
  237 + pointer-events: none;
  238 +
220 239 &.no-padding {
221   - padding-bottom: 0px;
  240 + padding-bottom: 0;
222 241 }
223   - &.tb-required:after {
224   - content: ' *';
  242 +
  243 + &.tb-required::after {
225 244 font-size: 13px;
  245 + color: rgba(0, 0, 0, .54);
226 246 vertical-align: top;
227   - color: rgba(0,0,0,0.54);
  247 + content: " *";
228 248 }
  249 +
229 250 &.tb-error {
230   - color: rgb(221,44,0);
231   - &.tb-required:after {
232   - color: rgb(221,44,0);
  251 + color: rgb(221, 44, 0);
  252 +
  253 + &.tb-required::after {
  254 + color: rgb(221, 44, 0);
233 255 }
234 256 }
235 257 }
... ... @@ -246,96 +268,107 @@ label {
246 268 }
247 269
248 270 .tb-readonly-label {
249   - color: rgba(0,0,0,0.54);
  271 + color: rgba(0, 0, 0, .54);
250 272 }
251 273
252 274 .tb-disabled-label {
253   - color: rgba(0,0,0,0.44);
  275 + color: rgba(0, 0, 0, .44);
254 276 }
255 277
  278 +/* stylelint-disable-next-line no-duplicate-selectors */
256 279 label {
257 280 &.tb-small {
258   - pointer-events: none;
259   - color: rgba(0,0,0,0.54);
260 281 font-size: 12px;
  282 + color: rgba(0, 0, 0, .54);
  283 + pointer-events: none;
261 284 }
262 285 }
263 286
264 287 div {
265 288 &.tb-small {
266   - color: rgba(0,0,0,0.54);
267 289 font-size: 14px;
  290 + color: rgba(0, 0, 0, .54);
268 291 }
269 292 }
270 293
271 294 .tb-hint {
  295 + padding-bottom: 15px;
272 296 font-size: 12px;
273 297 line-height: 14px;
274   - transition: all 0.3s cubic-bezier(0.55, 0, 0.55, 0.2);
275   - color: grey;
276   - padding-bottom: 15px;
277   - &.ng-hide, &.ng-enter, &.ng-leave.ng-leave-active {
  298 + color: #808080;
  299 + transition: all .3s cubic-bezier(.55, 0, .55, .2);
  300 +
  301 + &.ng-hide,
  302 + &.ng-enter,
  303 + &.ng-leave.ng-leave-active {
278 304 bottom: 26px;
279 305 opacity: 0;
280 306 }
281   - &.ng-leave, &.ng-enter.ng-enter-active {
  307 +
  308 + &.ng-leave,
  309 + &.ng-enter.ng-enter-active {
282 310 bottom: 7px;
283 311 opacity: 1;
284 312 }
285 313 }
286 314
287 315 .md-caption {
288   - &.tb-required:after {
289   - content: ' *';
  316 + &.tb-required::after {
290 317 font-size: 10px;
  318 + color: rgba(0, 0, 0, .54);
291 319 vertical-align: top;
292   - color: rgba(0,0,0,0.54);
  320 + content: " *";
293 321 }
294 322 }
295 323
296 324 pre.tb-highlight {
297   - background-color: #f7f7f7;
298 325 display: block;
299   - margin: 20px 0;
300 326 padding: 15px;
  327 + margin: 20px 0;
301 328 overflow-x: auto;
  329 + background-color: #f7f7f7;
  330 +
302 331 code {
  332 + box-sizing: border-box;
  333 + display: inline-block;
303 334 padding: 0;
304   - color: #303030;
305 335 font-family: monospace;
306   - display: inline-block;
307   - box-sizing: border-box;
308   - vertical-align: bottom;
309 336 font-size: 16px;
310   - font-weight: bold;
  337 + font-weight: 700;
  338 + color: #303030;
  339 + vertical-align: bottom;
311 340 }
312 341 }
313 342
314 343 .tb-notice {
315   - background-color: #f7f7f7;
316 344 padding: 15px;
317   - border: 1px solid #ccc;
318 345 font-size: 16px;
  346 + background-color: #f7f7f7;
  347 + border: 1px solid #ccc;
319 348 }
320 349
321 350 .tb-data-table {
322 351 md-toolbar {
323 352 z-index: 0;
324 353 }
  354 +
325 355 md-toolbar.md-table-toolbar.md-default-theme:not(.md-menu-toolbar).md-default .md-button[disabled],
326 356 md-toolbar.md-table-toolbar:not(.md-menu-toolbar).md-default .md-button[disabled] {
327   - color: rgba(0,0,0,0.38);
  357 + color: rgba(0, 0, 0, .38);
328 358 }
  359 +
329 360 md-toolbar.md-default-theme:not(.md-menu-toolbar) .md-button[disabled] md-icon,
330 361 md-toolbar:not(.md-menu-toolbar) .md-button[disabled] md-icon {
331   - color: rgba(0,0,0,.28);
  362 + color: rgba(0, 0, 0, .28);
332 363 }
  364 +
333 365 span.no-data-found {
334 366 position: relative;
  367 + display: flex;
335 368 height: calc(100% - 57px);
336 369 text-transform: uppercase;
337   - display: flex;
338 370 }
  371 +
339 372 table.md-table {
340 373 &.md-row-select td.md-cell,
341 374 &.md-row-select th.md-column {
... ... @@ -366,49 +399,55 @@ pre.tb-highlight {
366 399
367 400 td.md-cell,
368 401 th.md-column {
369   -
370 402 &:last-child {
371 403 padding: 0 12px 0 0;
372 404 }
373   -
374 405 }
375 406 }
376 407
377   - table.md-table, table.md-table.md-row-select {
  408 + table.md-table,
  409 + table.md-table.md-row-select {
378 410 tbody {
379 411 &.md-body {
380 412 tr {
381 413 &.md-row:not([disabled]) {
382 414 outline: none;
  415 +
383 416 &:hover {
384   - background-color: rgba(221, 221, 221, 0.3) !important;
  417 + background-color: rgba(221, 221, 221, .3) !important;
385 418 }
  419 +
386 420 &.md-selected {
387   - background-color: rgba(221, 221, 221, 0.5) !important;
  421 + background-color: rgba(221, 221, 221, .5) !important;
388 422 }
389   - &.tb-current, &.tb-current:hover{
390   - background-color: rgba(221, 221, 221, 0.65) !important;
  423 +
  424 + &.tb-current,
  425 + &.tb-current:hover{
  426 + background-color: rgba(221, 221, 221, .65) !important;
391 427 }
392 428 }
393 429 }
394 430 }
  431 +
395 432 tr {
396 433 td {
397 434 &.tb-action-cell {
  435 + width: 72px;
  436 + min-width: 72px;
  437 + max-width: 72px;
398 438 overflow: hidden;
399 439 text-overflow: ellipsis;
400 440 white-space: nowrap;
401   - min-width: 72px;
402   - max-width: 72px;
403   - width: 72px;
  441 +
404 442 .md-button {
405 443 &.md-icon-button {
406   - margin: 0;
407   - padding: 6px;
408 444 width: 36px;
409 445 height: 36px;
  446 + padding: 6px;
  447 + margin: 0;
410 448 }
411 449 }
  450 +
412 451 .tb-spacer {
413 452 padding-left: 38px;
414 453 }
... ... @@ -420,34 +459,40 @@ pre.tb-highlight {
420 459 }
421 460
422 461 .tb-severity {
423   - font-weight: bold;
  462 + font-weight: 700;
  463 +
424 464 &.tb-critical {
425   - color: red !important;
  465 + color: #f00 !important;
426 466 }
  467 +
427 468 &.tb-major {
428   - color: orange !important;
  469 + color: #ffa500 !important;
429 470 }
  471 +
430 472 &.tb-minor {
431 473 color: #ffca3d !important;
432 474 }
  475 +
433 476 &.tb-warning {
434 477 color: #abab00 !important;
435 478 }
  479 +
436 480 &.tb-indeterminate {
437   - color: green !important;
  481 + color: #008000 !important;
438 482 }
439 483 }
440 484
441 485 .tb-card-description {
442   - color: rgba(0,0,0,0.54);
443 486 font-size: 13px;
  487 + color: rgba(0, 0, 0, .54);
  488 +
444 489 @include line-clamp(2, 1.1);
445 490 }
446 491
447 492 /***********************
448 493 * Flow
449 494 ***********************/
450   -$previewSize: 100px;
  495 +$previewSize: 100px !default;
451 496
452 497 .file-input {
453 498 display: none;
... ... @@ -455,17 +500,19 @@ $previewSize: 100px;
455 500
456 501 .tb-flow-drop {
457 502 position: relative;
458   - border: dashed 2px;
459 503 height: $previewSize;
460 504 overflow: hidden;
  505 + border: dashed 2px;
  506 +
461 507 label {
462   - width: 100%;
463   - height: 100%;
464 508 display: flex;
465 509 flex-direction: column;
466 510 justify-content: center;
  511 + width: 100%;
  512 + height: 100%;
467 513 font-size: 16px;
468 514 text-align: center;
  515 +
469 516 @media (min-width: $layout-breakpoint-sm) {
470 517 font-size: 24px;
471 518 }
... ... @@ -474,8 +521,8 @@ $previewSize: 100px;
474 521
475 522 .tb-container {
476 523 position: relative;
477   - margin-top: 32px;
478 524 padding: 10px 0;
  525 + margin-top: 32px;
479 526 }
480 527
481 528 /***********************
... ... @@ -483,12 +530,12 @@ $previewSize: 100px;
483 530 ***********************/
484 531
485 532 .tb-prompt {
486   - color: rgba(0,0,0,0.38);
487   - text-transform: uppercase;
488 533 display: flex;
489 534 font-size: 18px;
490 535 font-weight: 400;
491 536 line-height: 18px;
  537 + color: rgba(0, 0, 0, .38);
  538 + text-transform: uppercase;
492 539 }
493 540
494 541 /***********************
... ... @@ -500,35 +547,37 @@ $previewSize: 100px;
500 547 }
501 548
502 549 .tb-error-message {
  550 + padding: 10px 0 0 10px;
  551 + margin-top: -6px;
  552 + overflow: hidden;
503 553 font-size: 12px;
504 554 line-height: 14px;
505   - overflow: hidden;
506   - padding: 10px 0px 0px 10px;
507   - color: rgb(221,44,0);
508   - margin-top: -6px;
  555 + color: rgb(221, 44, 0);
509 556 }
510 557
511 558 .tb-error-message.ng-animate {
512   - @include transition(all .3s cubic-bezier(.55,0,.55,.2));
  559 + @include transition(all .3s cubic-bezier(.55, 0, .55, .2));
513 560 }
514 561
515   -.tb-error-message.ng-enter-prepare, .tb-error-message.ng-enter {
516   - opacity:0;
  562 +.tb-error-message.ng-enter-prepare,
  563 +.tb-error-message.ng-enter {
517 564 margin-top: -24px;
  565 + opacity: 0;
518 566 }
519 567
520 568 .tb-error-message.ng-enter.ng-enter-active {
521   - opacity:1;
522 569 margin-top: -6px;
  570 + opacity: 1;
523 571 }
524 572
525 573 .tb-error-message.ng-leave {
526   - opacity:1;
527 574 margin-top: -6px;
  575 + opacity: 1;
528 576 }
  577 +
529 578 .tb-error-message.ng-leave.ng-leave-active {
530   - opacity:0;
531 579 margin-top: -24px;
  580 + opacity: 0;
532 581 }
533 582
534 583 /***********************
... ... @@ -545,39 +594,41 @@ md-tabs.tb-headless {
545 594
546 595 .md-button.tb-card-button {
547 596 width: 100%;
548   - height: 100%;
549 597 max-width: 240px;
  598 + height: 100%;
  599 +
550 600 span {
  601 + height: 18px;
  602 + min-height: 18px;
  603 + max-height: 18px;
551 604 padding: 0 0 20px 0;
  605 + margin: auto;
552 606 font-size: 18px;
553 607 font-weight: 400;
554   - white-space: normal;
555 608 line-height: 18px;
556   - max-height: 18px;
557   - min-height: 18px;
558   - height: 18px;
559   - margin: auto;
  609 + white-space: normal;
560 610 }
561 611 }
562 612
563 613 .md-button.tb-layout-button {
564 614 width: 100%;
565   - height: 100%;
566 615 max-width: 240px;
  616 + height: 100%;
  617 +
567 618 span {
568 619 padding: 40px;
569 620 font-size: 18px;
570 621 font-weight: 400;
571   - white-space: normal;
572 622 line-height: 18px;
  623 + white-space: normal;
573 624 }
574 625 }
575 626
576 627 .md-button.tb-add-new-widget {
  628 + padding-right: 12px;
  629 + font-size: 24px;
577 630 border-style: dashed;
578 631 border-width: 2px;
579   - font-size: 24px;
580   - padding-right: 12px;
581 632 }
582 633
583 634 /***********************
... ... @@ -585,11 +636,12 @@ md-tabs.tb-headless {
585 636 ***********************/
586 637
587 638 section.tb-header-buttons {
588   - pointer-events: none;
589 639 position: absolute;
590   - right: 0px;
591 640 top: 86px;
  641 + right: 0;
592 642 z-index: 3;
  643 + pointer-events: none;
  644 +
593 645 @media (min-width: $layout-breakpoint-sm) {
594 646 top: 86px;
595 647 }
... ... @@ -633,6 +685,7 @@ section.tb-footer-buttons {
633 685
634 686 ._md-toast-open-bottom .tb-footer-buttons {
635 687 @include transition(all .4s cubic-bezier(.25, .8, .25, 1));
  688 +
636 689 @include transform(translate3d(0, -42px, 0));
637 690 }
638 691
... ... @@ -641,27 +694,27 @@ section.tb-footer-buttons {
641 694 ***********************/
642 695
643 696 .md-icon-button.tb-md-32 {
644   - vertical-align: middle;
645 697 width: 32px;
646   - height: 32px;
647 698 min-width: 32px;
  699 + height: 32px;
648 700 min-height: 32px;
649   - margin: 0px !important;
650   - padding: 0px !important;
  701 + padding: 0 !important;
  702 + margin: 0 !important;
  703 + vertical-align: middle;
651 704 }
652 705
653 706 .material-icons.tb-md-20 {
654   - font-size: 20px;
655 707 width: 20px;
656   - height: 20px;
657 708 min-width: 20px;
  709 + height: 20px;
658 710 min-height: 20px;
  711 + font-size: 20px;
659 712 }
660 713
661 714 .material-icons.tb-md-96 {
662   - font-size: 96px;
663 715 width: 96px;
664 716 height: 96px;
  717 + font-size: 96px;
665 718 }
666 719
667 720 /***********************
... ... @@ -671,17 +724,17 @@ section.tb-footer-buttons {
671 724 .tb-absolute-fill {
672 725 position: absolute;
673 726 top: 0;
674   - left: 0;
675 727 right: 0;
676 728 bottom: 0;
  729 + left: 0;
677 730 }
678 731
679 732 .tb-progress-cover {
680 733 position: absolute;
681 734 top: 0;
682   - left: 0;
683 735 right: 0;
684 736 bottom: 0;
  737 + left: 0;
685 738 z-index: 6;
686 739 opacity: 1;
687 740 }
... ...
... ... @@ -17,43 +17,49 @@
17 17
18 18 @mixin input-placeholder {
19 19 // replaces compass/css/user-interface/input-placeholder()
  20 +
20 21 &::-webkit-input-placeholder {
21 22 @content;
22 23 }
  24 +
23 25 &:-moz-placeholder {
24 26 @content;
25 27 opacity: 1;
26 28 }
  29 +
27 30 &::-moz-placeholder {
28 31 @content;
29 32 opacity: 1;
30 33 }
  34 +
31 35 &:-ms-input-placeholder {
32 36 @content;
33 37 }
34 38 }
35 39
36 40 @mixin line-clamp($numLines: 1, $lineHeight: 1.412) {
37   - overflow: hidden;
38 41 position: relative;
  42 + max-height: ($numLines * $lineHeight * 1em);
  43 + padding-right: 2em;
  44 + margin-right: -1em;
  45 + overflow: hidden;
39 46 line-height: $lineHeight;
40 47 text-align: justify;
41   - margin-right: -1em;
42   - padding-right: 2em;
43   - max-height: ($numLines*$lineHeight)+em;
44   - &:before {
45   - content: '...';
  48 +
  49 + &::before {
46 50 position: absolute;
47 51 right: 1em;
48 52 bottom: 0;
  53 + content: "...";
49 54 }
50   - &:after {
51   - content: '';
  55 +
  56 + &::after {
52 57 position: absolute;
53 58 right: 1em;
54 59 width: 1em;
55 60 height: 1em;
56   - margin-top: 0.2em;
57   - background: white;
  61 + margin-top: .2em;
  62 + content: "";
  63 + background: #fff;
58 64 }
59 65 }
... ...
... ... @@ -18,6 +18,8 @@
18 18 const HtmlWebpackPlugin = require('html-webpack-plugin');
19 19 const ExtractTextPlugin = require('extract-text-webpack-plugin');
20 20 const CopyWebpackPlugin = require('copy-webpack-plugin');
  21 +const StyleLintPlugin = require('stylelint-webpack-plugin')
  22 +
21 23 const webpack = require('webpack');
22 24 const path = require('path');
23 25 const dirTree = require('directory-tree');
... ... @@ -76,6 +78,7 @@ module.exports = {
76 78 title: 'ThingsBoard',
77 79 inject: 'body',
78 80 }),
  81 + new StyleLintPlugin(),
79 82 new webpack.optimize.OccurrenceOrderPlugin(),
80 83 new webpack.NoErrorsPlugin(),
81 84 new ExtractTextPlugin('style.[contentHash].css', {
... ...