thingsboard-aplcache
Changes
application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsSandboxService.java 59(+44 -15)
application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java 2(+1 -1)
application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java 26(+22 -4)
application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java 8(+4 -4)
dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java 292(+253 -39)
dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java 36(+29 -7)
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java 5(+5 -0)
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNode.java 164(+164 -0)
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNodeConfiguration.java 57(+57 -0)
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java 8(+7 -1)
rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js 6(+3 -3)
ui/.stylelintrc 292(+292 -0)
ui/package.json 12(+9 -3)
ui/src/app/alarm/alarm.scss 23(+12 -11)
ui/src/app/audit/audit-log.scss 27(+15 -12)
ui/src/app/components/dashboard.scss 36(+20 -16)
ui/src/app/components/dashboard-select.scss 30(+18 -12)
ui/src/app/components/datasource.scss 41(+23 -18)
ui/src/app/components/datasource-entity.scss 16(+11 -5)
ui/src/app/components/details-sidenav.scss 34(+19 -15)
ui/src/app/components/expand-fullscreen.scss 29(+15 -14)
ui/src/app/components/grid.scss 13(+9 -4)
ui/src/app/components/js-func.scss 23(+14 -9)
ui/src/app/components/json-content.scss 20(+12 -8)
ui/src/app/components/json-form.scss 5(+3 -2)
ui/src/app/components/kv-map.scss 8(+5 -3)
ui/src/app/components/legend.scss 26(+18 -8)
ui/src/app/components/legend-config.scss 22(+14 -8)
ui/src/app/components/material-icon-select.scss 22(+12 -10)
ui/src/app/components/menu-link.scss 6(+4 -2)
ui/src/app/components/react/json-form.scss 121(+63 -58)
ui/src/app/components/react/json-form-image.scss 91(+47 -44)
ui/src/app/components/side-menu.scss 39(+21 -18)
ui/src/app/components/timeinterval.scss 11(+9 -2)
ui/src/app/components/timewindow.scss 37(+25 -12)
ui/src/app/components/widget/widget.scss 11(+7 -4)
ui/src/app/dashboard/dashboard.scss 79(+46 -33)
ui/src/app/dashboard/dashboard-settings.scss 26(+15 -11)
ui/src/app/dashboard/dashboard-toolbar.scss 81(+56 -25)
ui/src/app/entity/alias/entity-aliases.scss 18(+13 -5)
ui/src/app/entity/attribute/attribute-table.scss 30(+16 -14)
ui/src/app/entity/entity-filter.scss 8(+5 -3)
ui/src/app/entity/entity-list.scss 7(+5 -2)
ui/src/app/entity/entity-select.scss 6(+4 -2)
ui/src/app/entity/relation/relation-filters.scss 29(+18 -11)
ui/src/app/event/event.scss 75(+40 -35)
ui/src/app/extension/extension-table.scss 21(+13 -8)
ui/src/app/help/help.scss 8(+6 -2)
ui/src/app/home/home-links.scss 13(+7 -6)
ui/src/app/jsonform/jsonform.scss 6(+4 -2)
ui/src/app/layout/home.scss 29(+19 -10)
ui/src/app/layout/user-menu.scss 20(+12 -8)
ui/src/app/login/login.scss 5(+4 -1)
ui/src/app/rulechain/link.scss 4(+3 -1)
ui/src/app/rulechain/rulechain.scss 272(+166 -106)
ui/src/app/rulechain/rulenode.scss 6(+3 -3)
ui/src/app/services/toast.scss 13(+7 -6)
ui/src/app/user/user-fieldset.scss 6(+4 -2)
ui/src/app/widget/lib/rpc/knob.scss 141(+78 -63)
ui/src/app/widget/lib/rpc/led-indicator.scss 48(+26 -22)
ui/src/app/widget/lib/rpc/round-switch.scss 214(+118 -96)
ui/src/app/widget/lib/rpc/switch.scss 73(+42 -31)
ui/src/app/widget/widget-editor.scss 60(+32 -28)
ui/src/scss/animations.scss 6(+5 -1)
ui/src/scss/constants.scss 20(+10 -10)
ui/src/scss/fonts.scss 8(+4 -4)
ui/src/scss/main.scss 309(+181 -128)
ui/src/scss/mixins.scss 26(+16 -10)
ui/webpack.config.dev.js 3(+3 -0)
Details
diff --git a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
index 8e7a9ae..6a78f78 100644
--- a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
@@ -116,6 +116,7 @@ public class AppActor extends RuleChainManagerActor {
break;
case ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG:
onToDeviceSessionMsg((BasicActorSystemToDeviceSessionActorMsg) msg);
+ break;
default:
return false;
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
index a6daeec..24608cb 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
@@ -76,7 +76,7 @@ public class DeviceController extends BaseController {
device.setTenantId(getCurrentUser().getTenantId());
if (getCurrentUser().getAuthority() == Authority.CUSTOMER_USER) {
if (device.getId() == null || device.getId().isNullUid() ||
- device.getCustomerId() == null || device.getCustomerId().isNullUid()) {
+ device.getCustomerId() == null || device.getCustomerId().isNullUid()) {
throw new ThingsboardException("You don't have permission to perform this operation!",
ThingsboardErrorCode.PERMISSION_DENIED);
} else {
diff --git a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
index 10a16ec..7062ca9 100644
--- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
@@ -49,15 +49,15 @@ import org.thingsboard.server.common.data.kv.Aggregation;
import org.thingsboard.server.common.data.kv.AttributeKey;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
-import org.thingsboard.server.common.data.kv.BaseTsKvQuery;
+import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.BooleanDataEntry;
import org.thingsboard.server.common.data.kv.DoubleDataEntry;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry;
+import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
-import org.thingsboard.server.common.data.kv.TsKvQuery;
import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
import org.thingsboard.server.common.msg.core.TelemetryUploadRequest;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
@@ -81,7 +81,6 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
@@ -201,7 +200,7 @@ public class TelemetryController extends BaseController {
(result, entityId) -> {
// If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted
Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr);
- List<TsKvQuery> queries = toKeysList(keys).stream().map(key -> new BaseTsKvQuery(key, startTs, endTs, interval, limit, agg))
+ List<ReadTsKvQuery> queries = toKeysList(keys).stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, interval, limit, agg))
.collect(Collectors.toList());
Futures.addCallback(tsService.findAll(entityId, queries), getTsKvListCallback(result));
diff --git a/application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsSandboxService.java b/application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsSandboxService.java
index 7f274ec..724f652 100644
--- a/application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsSandboxService.java
+++ b/application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsSandboxService.java
@@ -22,6 +22,7 @@ import delight.nashornsandbox.NashornSandbox;
import delight.nashornsandbox.NashornSandboxes;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.tuple.Pair;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@@ -42,9 +43,10 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic
private ScriptEngine engine;
private ExecutorService monitorExecutorService;
- private Map<UUID, String> functionsMap = new ConcurrentHashMap<>();
-
- private Map<UUID,AtomicInteger> blackListedFunctions = new ConcurrentHashMap<>();
+ private final Map<UUID, String> functionsMap = new ConcurrentHashMap<>();
+ private final Map<UUID,AtomicInteger> blackListedFunctions = new ConcurrentHashMap<>();
+ private final Map<String, Pair<UUID, AtomicInteger>> scriptToId = new ConcurrentHashMap<>();
+ private final Map<UUID, AtomicInteger> scriptIdToCount = new ConcurrentHashMap<>();
@PostConstruct
public void init() {
@@ -78,19 +80,27 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic
@Override
public ListenableFuture<UUID> eval(JsScriptType scriptType, String scriptBody, String... argNames) {
- UUID scriptId = UUID.randomUUID();
- String functionName = "invokeInternal_" + scriptId.toString().replace('-','_');
- String jsScript = generateJsScript(scriptType, functionName, scriptBody, argNames);
- try {
- if (useJsSandbox()) {
- sandbox.eval(jsScript);
- } else {
- engine.eval(jsScript);
+ Pair<UUID, AtomicInteger> deduplicated = deduplicate(scriptType, scriptBody);
+ UUID scriptId = deduplicated.getLeft();
+ AtomicInteger duplicateCount = deduplicated.getRight();
+
+ if(duplicateCount.compareAndSet(0, 1)) {
+ String functionName = "invokeInternal_" + scriptId.toString().replace('-', '_');
+ String jsScript = generateJsScript(scriptType, functionName, scriptBody, argNames);
+ try {
+ if (useJsSandbox()) {
+ sandbox.eval(jsScript);
+ } else {
+ engine.eval(jsScript);
+ }
+ functionsMap.put(scriptId, functionName);
+ } catch (Exception e) {
+ duplicateCount.decrementAndGet();
+ log.warn("Failed to compile JS script: {}", e.getMessage(), e);
+ return Futures.immediateFailedFuture(e);
}
- functionsMap.put(scriptId, functionName);
- } catch (Exception e) {
- log.warn("Failed to compile JS script: {}", e.getMessage(), e);
- return Futures.immediateFailedFuture(e);
+ } else {
+ duplicateCount.incrementAndGet();
}
return Futures.immediateFuture(scriptId);
}
@@ -122,6 +132,13 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic
@Override
public ListenableFuture<Void> release(UUID scriptId) {
+ AtomicInteger count = scriptIdToCount.get(scriptId);
+ if(count != null) {
+ if(count.decrementAndGet() > 0) {
+ return Futures.immediateFuture(null);
+ }
+ }
+
String functionName = functionsMap.get(scriptId);
if (functionName != null) {
try {
@@ -156,4 +173,16 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic
throw new RuntimeException("No script factory implemented for scriptType: " + scriptType);
}
}
+
+ private Pair<UUID, AtomicInteger> deduplicate(JsScriptType scriptType, String scriptBody) {
+ Pair<UUID, AtomicInteger> precomputed = Pair.of(UUID.randomUUID(), new AtomicInteger());
+
+ Pair<UUID, AtomicInteger> pair = scriptToId.computeIfAbsent(deduplicateKey(scriptType, scriptBody), i -> precomputed);
+ AtomicInteger duplicateCount = scriptIdToCount.computeIfAbsent(pair.getLeft(), i -> pair.getRight());
+ return Pair.of(pair.getLeft(), duplicateCount);
+ }
+
+ private String deduplicateKey(JsScriptType scriptType, String scriptBody) {
+ return scriptType + "_" + scriptBody;
+ }
}
diff --git a/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java b/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java
index 98b0e77..2ba87ec 100644
--- a/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java
+++ b/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java
@@ -45,7 +45,7 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S
try {
this.scriptId = this.sandboxService.eval(JsScriptType.RULE_NODE_SCRIPT, script, argNames).get();
} catch (Exception e) {
- throw new IllegalArgumentException("Can't compile script: " + e.getMessage());
+ throw new IllegalArgumentException("Can't compile script: " + e.getMessage(), e);
}
}
diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java
index 9551f9f..35455b9 100644
--- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java
+++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java
@@ -24,26 +24,33 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
+import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
import org.thingsboard.rule.engine.api.util.DonAsynchron;
+import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
+//<<<<<<< HEAD
import org.thingsboard.server.common.data.id.EntityViewId;
+//=======
+import org.thingsboard.server.common.data.id.TenantId;
+//>>>>>>> d909192071880b7af2137333142bc62ece369ec1
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
-import org.thingsboard.server.common.data.kv.BaseTsKvQuery;
+import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.BooleanDataEntry;
import org.thingsboard.server.common.data.kv.DataType;
import org.thingsboard.server.common.data.kv.DoubleDataEntry;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry;
+import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
-import org.thingsboard.server.common.data.kv.TsKvQuery;
+import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.entityview.EntityViewService;
@@ -108,6 +115,10 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
@Lazy
private DeviceStateService stateService;
+ @Autowired
+ @Lazy
+ private ActorService actorService;
+
private ExecutorService tsCallBackExecutor;
private ExecutorService wsCallBackExecutor;
@@ -213,6 +224,13 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
}
@Override
+ public void onSharedAttributesUpdate(TenantId tenantId, DeviceId deviceId, Set<AttributeKvEntry> attributes) {
+ DeviceAttributesEventNotificationMsg notificationMsg = DeviceAttributesEventNotificationMsg.onUpdate(tenantId,
+ deviceId, DataConstants.SHARED_SCOPE, new ArrayList<>(attributes));
+ actorService.onMsg(new SendToClusterMsg(deviceId, notificationMsg));
+ }
+
+ @Override
public void onNewRemoteSubscription(ServerAddress serverAddress, byte[] data) {
ClusterAPIProtos.SubscriptionProto proto;
try {
@@ -364,9 +382,9 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
e -> log.error("Failed to fetch missed updates.", e), tsCallBackExecutor);
} else if (subscription.getType() == TelemetryFeature.TIMESERIES) {
long curTs = System.currentTimeMillis();
- List<TsKvQuery> queries = new ArrayList<>();
+ List<ReadTsKvQuery> queries = new ArrayList<>();
subscription.getKeyStates().entrySet().forEach(e -> {
- queries.add(new BaseTsKvQuery(e.getKey(), e.getValue() + 1L, curTs));
+ queries.add(new BaseReadTsKvQuery(e.getKey(), e.getValue() + 1L, curTs));
});
DonAsynchron.withCallback(tsService.findAll(entityId, queries),
diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java
index 3b2d690..2ff8e89 100644
--- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java
+++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java
@@ -30,10 +30,10 @@ import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.kv.Aggregation;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
-import org.thingsboard.server.common.data.kv.BaseTsKvQuery;
+import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
+import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.TsKvEntry;
-import org.thingsboard.server.common.data.kv.TsKvQuery;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.service.security.AccessValidator;
@@ -251,7 +251,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
}
EntityId entityId = EntityIdFactory.getByTypeAndId(cmd.getEntityType(), cmd.getEntityId());
List<String> keys = new ArrayList<>(getKeys(cmd).orElse(Collections.emptySet()));
- List<TsKvQuery> queries = keys.stream().map(key -> new BaseTsKvQuery(key, cmd.getStartTs(), cmd.getEndTs(), cmd.getInterval(), getLimit(cmd.getLimit()), getAggregation(cmd.getAgg())))
+ List<ReadTsKvQuery> queries = keys.stream().map(key -> new BaseReadTsKvQuery(key, cmd.getStartTs(), cmd.getEndTs(), cmd.getInterval(), getLimit(cmd.getLimit()), getAggregation(cmd.getAgg())))
.collect(Collectors.toList());
FutureCallback<List<TsKvEntry>> callback = new FutureCallback<List<TsKvEntry>>() {
@@ -337,7 +337,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
log.debug("[{}] fetching timeseries data for last {} ms for keys: ({}) for device : {}", sessionId, cmd.getTimeWindow(), cmd.getKeys(), entityId);
startTs = cmd.getStartTs();
long endTs = cmd.getStartTs() + cmd.getTimeWindow();
- List<TsKvQuery> queries = keys.stream().map(key -> new BaseTsKvQuery(key, startTs, endTs, cmd.getInterval(),
+ List<ReadTsKvQuery> queries = keys.stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, cmd.getInterval(),
getLimit(cmd.getLimit()), getAggregation(cmd.getAgg()))).collect(Collectors.toList());
final FutureCallback<List<TsKvEntry>> callback = getSubscriptionCallback(sessionRef, cmd, sessionId, entityId, startTs, keys);
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java
index 49dd209..d7ab6e3 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java
@@ -1,4 +1,4 @@
-/**
+ /**
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseDeleteTsKvQuery.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseDeleteTsKvQuery.java
new file mode 100644
index 0000000..11b95ca
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseDeleteTsKvQuery.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.data.kv;
+
+import lombok.Data;
+
+@Data
+public class BaseDeleteTsKvQuery extends BaseTsKvQuery implements DeleteTsKvQuery {
+
+ private final Boolean rewriteLatestIfDeleted;
+
+ public BaseDeleteTsKvQuery(String key, long startTs, long endTs, boolean rewriteLatestIfDeleted) {
+ super(key, startTs, endTs);
+ this.rewriteLatestIfDeleted = rewriteLatestIfDeleted;
+ }
+
+ public BaseDeleteTsKvQuery(String key, long startTs, long endTs) {
+ this(key, startTs, endTs, false);
+ }
+
+
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseReadTsKvQuery.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseReadTsKvQuery.java
new file mode 100644
index 0000000..3c48adf
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseReadTsKvQuery.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.data.kv;
+
+import lombok.Data;
+
+@Data
+public class BaseReadTsKvQuery extends BaseTsKvQuery implements ReadTsKvQuery {
+
+ private final long interval;
+ private final int limit;
+ private final Aggregation aggregation;
+ private final String orderBy;
+
+ public BaseReadTsKvQuery(String key, long startTs, long endTs, long interval, int limit, Aggregation aggregation) {
+ this(key, startTs, endTs, interval, limit, aggregation, "DESC");
+ }
+
+ public BaseReadTsKvQuery(String key, long startTs, long endTs, long interval, int limit, Aggregation aggregation,
+ String orderBy) {
+ super(key, startTs, endTs);
+ this.interval = interval;
+ this.limit = limit;
+ this.aggregation = aggregation;
+ this.orderBy = orderBy;
+ }
+
+ public BaseReadTsKvQuery(String key, long startTs, long endTs) {
+ this(key, startTs, endTs, endTs - startTs, 1, Aggregation.AVG, "DESC");
+ }
+
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseTsKvQuery.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseTsKvQuery.java
index 0afe00b..c4fa69c 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseTsKvQuery.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseTsKvQuery.java
@@ -23,21 +23,11 @@ public class BaseTsKvQuery implements TsKvQuery {
private final String key;
private final long startTs;
private final long endTs;
- private final long interval;
- private final int limit;
- private final Aggregation aggregation;
- public BaseTsKvQuery(String key, long startTs, long endTs, long interval, int limit, Aggregation aggregation) {
+ public BaseTsKvQuery(String key, long startTs, long endTs) {
this.key = key;
this.startTs = startTs;
this.endTs = endTs;
- this.interval = interval;
- this.limit = limit;
- this.aggregation = aggregation;
- }
-
- public BaseTsKvQuery(String key, long startTs, long endTs) {
- this(key, startTs, endTs, endTs-startTs, 1, Aggregation.AVG);
}
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/DeleteTsKvQuery.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/DeleteTsKvQuery.java
new file mode 100644
index 0000000..7607a59
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/DeleteTsKvQuery.java
@@ -0,0 +1,22 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.data.kv;
+
+public interface DeleteTsKvQuery extends TsKvQuery {
+
+ Boolean getRewriteLatestIfDeleted();
+
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/ReadTsKvQuery.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/ReadTsKvQuery.java
new file mode 100644
index 0000000..7d54745
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/ReadTsKvQuery.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.data.kv;
+
+public interface ReadTsKvQuery extends TsKvQuery {
+
+ long getInterval();
+
+ int getLimit();
+
+ Aggregation getAggregation();
+
+ String getOrderBy();
+
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/TsKvQuery.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/TsKvQuery.java
index ca9f90c..9df514e 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/kv/TsKvQuery.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/TsKvQuery.java
@@ -23,10 +23,4 @@ public interface TsKvQuery {
long getEndTs();
- long getInterval();
-
- int getLimit();
-
- Aggregation getAggregation();
-
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java
index 9b89fe2..55c5987 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java
@@ -16,9 +16,7 @@
package org.thingsboard.server.dao.relation;
import com.google.common.base.Function;
-import com.google.common.util.concurrent.AsyncFunction;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
@@ -41,7 +39,6 @@ import org.thingsboard.server.dao.exception.DataValidationException;
import javax.annotation.Nullable;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -94,10 +91,10 @@ public class BaseRelationService implements RelationService {
@Caching(evict = {
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.to, #relation.type, #relation.typeGroup}"),
- @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup}"),
- @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup}"),
- @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup}"),
- @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup}")
+ @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup, 'FROM'}"),
+ @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup, 'FROM'}"),
+ @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup, 'TO'}"),
+ @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup, 'TO'}")
})
@Override
public boolean saveRelation(EntityRelation relation) {
@@ -108,10 +105,10 @@ public class BaseRelationService implements RelationService {
@Caching(evict = {
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.to, #relation.type, #relation.typeGroup}"),
- @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup}"),
- @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup}"),
- @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup}"),
- @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup}")
+ @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup, 'FROM'}"),
+ @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup, 'FROM'}"),
+ @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup, 'TO'}"),
+ @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup, 'TO'}")
})
@Override
public ListenableFuture<Boolean> saveRelationAsync(EntityRelation relation) {
@@ -122,10 +119,10 @@ public class BaseRelationService implements RelationService {
@Caching(evict = {
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.to, #relation.type, #relation.typeGroup}"),
- @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup}"),
- @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup}"),
- @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup}"),
- @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup}")
+ @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup, 'FROM'}"),
+ @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup, 'FROM'}"),
+ @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup, 'TO'}"),
+ @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup, 'TO'}")
})
@Override
public boolean deleteRelation(EntityRelation relation) {
@@ -136,10 +133,10 @@ public class BaseRelationService implements RelationService {
@Caching(evict = {
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.to, #relation.type, #relation.typeGroup}"),
- @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup}"),
- @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup}"),
- @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup}"),
- @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup}")
+ @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup, 'FROM'}"),
+ @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup, 'FROM'}"),
+ @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup, 'TO'}"),
+ @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup, 'TO'}")
})
@Override
public ListenableFuture<Boolean> deleteRelationAsync(EntityRelation relation) {
@@ -150,10 +147,10 @@ public class BaseRelationService implements RelationService {
@Caching(evict = {
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #to, #relationType, #typeGroup}"),
- @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup}"),
- @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #typeGroup}"),
- @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #typeGroup}"),
- @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup}")
+ @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup, 'FROM'}"),
+ @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #typeGroup, 'FROM'}"),
+ @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #typeGroup, 'TO'}"),
+ @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup, 'TO'}")
})
@Override
public boolean deleteRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) {
@@ -164,10 +161,10 @@ public class BaseRelationService implements RelationService {
@Caching(evict = {
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #to, #relationType, #typeGroup}"),
- @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup}"),
- @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #typeGroup}"),
- @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #typeGroup}"),
- @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup}")
+ @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup, 'FROM'}"),
+ @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #typeGroup, 'FROM'}"),
+ @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #typeGroup, 'TO'}"),
+ @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup, 'TO'}")
})
@Override
public ListenableFuture<Boolean> deleteRelationAsync(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) {
@@ -250,30 +247,36 @@ public class BaseRelationService implements RelationService {
fromTypeAndTypeGroup.add(relation.getFrom());
fromTypeAndTypeGroup.add(relation.getType());
fromTypeAndTypeGroup.add(relation.getTypeGroup());
+ fromTypeAndTypeGroup.add(EntitySearchDirection.FROM.name());
cache.evict(fromTypeAndTypeGroup);
List<Object> fromAndTypeGroup = new ArrayList<>();
fromAndTypeGroup.add(relation.getFrom());
fromAndTypeGroup.add(relation.getTypeGroup());
+ fromAndTypeGroup.add(EntitySearchDirection.FROM.name());
cache.evict(fromAndTypeGroup);
List<Object> toAndTypeGroup = new ArrayList<>();
toAndTypeGroup.add(relation.getTo());
toAndTypeGroup.add(relation.getTypeGroup());
+ toAndTypeGroup.add(EntitySearchDirection.TO.name());
cache.evict(toAndTypeGroup);
List<Object> toTypeAndTypeGroup = new ArrayList<>();
- fromTypeAndTypeGroup.add(relation.getTo());
- fromTypeAndTypeGroup.add(relation.getType());
- fromTypeAndTypeGroup.add(relation.getTypeGroup());
+ toTypeAndTypeGroup.add(relation.getTo());
+ toTypeAndTypeGroup.add(relation.getType());
+ toTypeAndTypeGroup.add(relation.getTypeGroup());
+ toTypeAndTypeGroup.add(EntitySearchDirection.TO.name());
cache.evict(toTypeAndTypeGroup);
}
- @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #typeGroup}")
+ @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #typeGroup, 'FROM'}")
@Override
public List<EntityRelation> findByFrom(EntityId from, RelationTypeGroup typeGroup) {
+ validate(from);
+ validateTypeGroup(typeGroup);
try {
- return findByFromAsync(from, typeGroup).get();
+ return relationDao.findAllByFrom(from, typeGroup).get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
@@ -284,7 +287,29 @@ public class BaseRelationService implements RelationService {
log.trace("Executing findByFrom [{}][{}]", from, typeGroup);
validate(from);
validateTypeGroup(typeGroup);
- return relationDao.findAllByFrom(from, typeGroup);
+
+ List<Object> fromAndTypeGroup = new ArrayList<>();
+ fromAndTypeGroup.add(from);
+ fromAndTypeGroup.add(typeGroup);
+ fromAndTypeGroup.add(EntitySearchDirection.FROM.name());
+
+ Cache cache = cacheManager.getCache(RELATIONS_CACHE);
+ List<EntityRelation> fromCache = cache.get(fromAndTypeGroup, List.class);
+ if (fromCache != null) {
+ return Futures.immediateFuture(fromCache);
+ } else {
+ ListenableFuture<List<EntityRelation>> relationsFuture = relationDao.findAllByFrom(from, typeGroup);
+ Futures.addCallback(relationsFuture,
+ new FutureCallback<List<EntityRelation>>() {
+ @Override
+ public void onSuccess(@Nullable List<EntityRelation> result) {
+ cache.putIfAbsent(fromAndTypeGroup, result);
+ }
+ @Override
+ public void onFailure(Throwable t) {}
+ });
+ return relationsFuture;
+ }
}
@Override
@@ -305,7 +330,7 @@ public class BaseRelationService implements RelationService {
});
}
- @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup}")
+ @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup, 'FROM'}")
@Override
public List<EntityRelation> findByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup) {
try {
@@ -324,11 +349,13 @@ public class BaseRelationService implements RelationService {
return relationDao.findAllByFromAndType(from, relationType, typeGroup);
}
- @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #typeGroup}")
+ @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #typeGroup, 'TO'}")
@Override
public List<EntityRelation> findByTo(EntityId to, RelationTypeGroup typeGroup) {
+ validate(to);
+ validateTypeGroup(typeGroup);
try {
- return findByToAsync(to, typeGroup).get();
+ return relationDao.findAllByTo(to, typeGroup).get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
@@ -339,7 +366,29 @@ public class BaseRelationService implements RelationService {
log.trace("Executing findByTo [{}][{}]", to, typeGroup);
validate(to);
validateTypeGroup(typeGroup);
- return relationDao.findAllByTo(to, typeGroup);
+
+ List<Object> toAndTypeGroup = new ArrayList<>();
+ toAndTypeGroup.add(to);
+ toAndTypeGroup.add(typeGroup);
+ toAndTypeGroup.add(EntitySearchDirection.TO.name());
+
+ Cache cache = cacheManager.getCache(RELATIONS_CACHE);
+ List<EntityRelation> fromCache = cache.get(toAndTypeGroup, List.class);
+ if (fromCache != null) {
+ return Futures.immediateFuture(fromCache);
+ } else {
+ ListenableFuture<List<EntityRelation>> relationsFuture = relationDao.findAllByTo(to, typeGroup);
+ Futures.addCallback(relationsFuture,
+ new FutureCallback<List<EntityRelation>>() {
+ @Override
+ public void onSuccess(@Nullable List<EntityRelation> result) {
+ cache.putIfAbsent(toAndTypeGroup, result);
+ }
+ @Override
+ public void onFailure(Throwable t) {}
+ });
+ return relationsFuture;
+ }
}
@Override
@@ -371,7 +420,7 @@ public class BaseRelationService implements RelationService {
});
}
- @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup}")
+ @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup, 'TO'}")
@Override
public List<EntityRelation> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup) {
try {
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java
index e5f145f..1eb2f00 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java
@@ -31,9 +31,10 @@ import org.thingsboard.server.common.data.UUIDConverter;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.kv.Aggregation;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
+import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
+import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
-import org.thingsboard.server.common.data.kv.TsKvQuery;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.sql.TsKvEntity;
import org.thingsboard.server.dao.model.sql.TsKvLatestCompositeKey;
@@ -102,7 +103,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
}
@Override
- public ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, List<TsKvQuery> queries) {
+ public ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, List<ReadTsKvQuery> queries) {
List<ListenableFuture<List<TsKvEntry>>> futures = queries
.stream()
.map(query -> findAllAsync(entityId, query))
@@ -121,7 +122,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
}, service);
}
- private ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, TsKvQuery query) {
+ private ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, ReadTsKvQuery query) {
if (query.getAggregation() == Aggregation.NONE) {
return findAllAsyncWithLimit(entityId, query);
} else {
@@ -228,7 +229,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
});
}
- private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, TsKvQuery query) {
+ private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, ReadTsKvQuery query) {
return Futures.immediateFuture(
DaoUtil.convertDataList(
tsKvRepository.findAllWithLimit(
@@ -306,6 +307,36 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
});
}
+ @Override
+ public ListenableFuture<Void> remove(EntityId entityId, DeleteTsKvQuery query) {
+ return service.submit(() -> {
+ tsKvRepository.delete(
+ fromTimeUUID(entityId.getId()),
+ entityId.getEntityType(),
+ query.getKey(),
+ query.getStartTs(),
+ query.getEndTs());
+ return null;
+ });
+ }
+
+ @Override
+ public ListenableFuture<Void> removeLatest(EntityId entityId, DeleteTsKvQuery query) {
+ TsKvLatestEntity latestEntity = new TsKvLatestEntity();
+ latestEntity.setEntityType(entityId.getEntityType());
+ latestEntity.setEntityId(fromTimeUUID(entityId.getId()));
+ latestEntity.setKey(query.getKey());
+ return service.submit(() -> {
+ tsKvLatestRepository.delete(latestEntity);
+ return null;
+ });
+ }
+
+ @Override
+ public ListenableFuture<Void> removePartition(EntityId entityId, DeleteTsKvQuery query) {
+ return service.submit(() -> null);
+ }
+
@PreDestroy
void onDestroy() {
if (insertService != null) {
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/TsKvRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/TsKvRepository.java
index a1d1920..2b39d25 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/TsKvRepository.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/TsKvRepository.java
@@ -16,10 +16,12 @@
package org.thingsboard.server.dao.sql.timeseries;
import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.scheduling.annotation.Async;
+import org.springframework.transaction.annotation.Transactional;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.dao.model.sql.TsKvCompositeKey;
import org.thingsboard.server.dao.model.sql.TsKvEntity;
@@ -41,6 +43,17 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite
@Param("endTs") long endTs,
Pageable pageable);
+ @Transactional
+ @Modifying
+ @Query("DELETE FROM TsKvEntity tskv WHERE tskv.entityId = :entityId " +
+ "AND tskv.entityType = :entityType AND tskv.key = :entityKey " +
+ "AND tskv.ts > :startTs AND tskv.ts < :endTs")
+ void delete(@Param("entityId") String entityId,
+ @Param("entityType") EntityType entityType,
+ @Param("entityKey") String key,
+ @Param("startTs") long startTs,
+ @Param("endTs") long endTs);
+
@Async
@Query("SELECT new TsKvEntity(MAX(tskv.strValue), MAX(tskv.longValue), MAX(tskv.doubleValue)) FROM TsKvEntity tskv " +
"WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " +
@@ -56,30 +69,30 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite
"WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " +
"AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs")
CompletableFuture<TsKvEntity> findMin(@Param("entityId") String entityId,
- @Param("entityType") EntityType entityType,
- @Param("entityKey") String entityKey,
- @Param("startTs") long startTs,
- @Param("endTs") long endTs);
+ @Param("entityType") EntityType entityType,
+ @Param("entityKey") String entityKey,
+ @Param("startTs") long startTs,
+ @Param("endTs") long endTs);
@Async
@Query("SELECT new TsKvEntity(COUNT(tskv.booleanValue), COUNT(tskv.strValue), COUNT(tskv.longValue), COUNT(tskv.doubleValue)) FROM TsKvEntity tskv " +
"WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " +
"AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs")
CompletableFuture<TsKvEntity> findCount(@Param("entityId") String entityId,
- @Param("entityType") EntityType entityType,
- @Param("entityKey") String entityKey,
- @Param("startTs") long startTs,
- @Param("endTs") long endTs);
+ @Param("entityType") EntityType entityType,
+ @Param("entityKey") String entityKey,
+ @Param("startTs") long startTs,
+ @Param("endTs") long endTs);
@Async
@Query("SELECT new TsKvEntity(AVG(tskv.longValue), AVG(tskv.doubleValue)) FROM TsKvEntity tskv " +
"WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " +
"AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs")
CompletableFuture<TsKvEntity> findAvg(@Param("entityId") String entityId,
- @Param("entityType") EntityType entityType,
- @Param("entityKey") String entityKey,
- @Param("startTs") long startTs,
- @Param("endTs") long endTs);
+ @Param("entityType") EntityType entityType,
+ @Param("entityKey") String entityKey,
+ @Param("startTs") long startTs,
+ @Param("endTs") long endTs);
@Async
@@ -87,8 +100,8 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite
"WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " +
"AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs")
CompletableFuture<TsKvEntity> findSum(@Param("entityId") String entityId,
- @Param("entityType") EntityType entityType,
- @Param("entityKey") String entityKey,
- @Param("startTs") long startTs,
- @Param("endTs") long endTs);
+ @Param("entityType") EntityType entityType,
+ @Param("entityKey") String entityKey,
+ @Param("startTs") long startTs,
+ @Param("endTs") long endTs);
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java
index 9b837b2..605c941 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java
@@ -25,9 +25,10 @@ import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityViewId;
-import org.thingsboard.server.common.data.kv.BaseTsKvQuery;
+import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
+import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
+import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.TsKvEntry;
-import org.thingsboard.server.common.data.kv.TsKvQuery;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.service.Validator;
@@ -46,6 +47,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
public class BaseTimeseriesService implements TimeseriesService {
public static final int INSERTS_PER_ENTRY = 3;
+ public static final int DELETES_PER_ENTRY = INSERTS_PER_ENTRY;
@Autowired
private TimeseriesDao timeseriesDao;
@@ -54,9 +56,9 @@ public class BaseTimeseriesService implements TimeseriesService {
private EntityViewService entityViewService;
@Override
- public ListenableFuture<List<TsKvEntry>> findAll(EntityId entityId, List<TsKvQuery> queries) {
+ public ListenableFuture<List<TsKvEntry>> findAll(EntityId entityId, List<ReadTsKvQuery> queries) {
validate(entityId);
- queries.forEach(query -> validate(query));
+ queries.forEach(BaseTimeseriesService::validate);
if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW)) {
EntityView entityView = entityViewService.findEntityViewById((EntityViewId) entityId);
return timeseriesDao.findAllAsync(entityView.getEntityId(), updateQueriesForEntityView(entityView, queries));
@@ -69,13 +71,14 @@ public class BaseTimeseriesService implements TimeseriesService {
validate(entityId);
List<ListenableFuture<TsKvEntry>> futures = Lists.newArrayListWithExpectedSize(keys.size());
keys.forEach(key -> Validator.validateString(key, "Incorrect key " + key));
- if (false/*entityId.getEntityType().equals(EntityType.ENTITY_VIEW)*/) {
+ if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW)) {
EntityView entityView = entityViewService.findEntityViewById((EntityViewId) entityId);
- Collection<String> newKeys = chooseKeysForEntityView(entityView, keys);
- newKeys.forEach(newKey -> futures.add(timeseriesDao.findLatest(entityView.getEntityId(), newKey)));
- } else {
- keys.forEach(key -> futures.add(timeseriesDao.findLatest(entityId, key)));
+ Collection<String> matchingKeys = chooseKeysForEntityView(entityView, keys);
+ List<ReadTsKvQuery> queries = new ArrayList<>();
+ matchingKeys.forEach(key -> queries.add(new BaseReadTsKvQuery(key, entityView.getStartTs(), entityView.getEndTs())));
+ return timeseriesDao.findAllAsync(entityView.getEntityId(), updateQueriesForEntityView(entityView, queries));
}
+ keys.forEach(key -> futures.add(timeseriesDao.findLatest(entityId, key)));
return Futures.allAsList(futures);
}
@@ -129,8 +132,8 @@ public class BaseTimeseriesService implements TimeseriesService {
futures.add(timeseriesDao.save(entityId, tsKvEntry, ttl));
}
- private List<TsKvQuery> updateQueriesForEntityView(EntityView entityView, List<TsKvQuery> queries) {
- List<TsKvQuery> newQueries = new ArrayList<>();
+ private List<ReadTsKvQuery> updateQueriesForEntityView(EntityView entityView, List<ReadTsKvQuery> queries) {
+ List<ReadTsKvQuery> newQueries = new ArrayList<>();
entityView.getKeys().getTimeseries()
.forEach(viewKey -> queries
.forEach(query -> {
@@ -148,7 +151,6 @@ public class BaseTimeseriesService implements TimeseriesService {
return newQueries;
}
- @Deprecated /*Will be a modified*/
private Collection<String> chooseKeysForEntityView(EntityView entityView, Collection<String> keys) {
Collection<String> newKeys = new ArrayList<>();
entityView.getKeys().getTimeseries()
@@ -156,27 +158,53 @@ public class BaseTimeseriesService implements TimeseriesService {
.forEach(key -> {
if (key.equals(viewKey)) {
newKeys.add(key);
- }}));
+ }
+ }));
return newKeys;
}
+ @Override
+ public ListenableFuture<List<Void>> remove(EntityId entityId, List<DeleteTsKvQuery> deleteTsKvQueries) {
+ validate(entityId);
+ deleteTsKvQueries.forEach(BaseTimeseriesService::validate);
+ List<ListenableFuture<Void>> futures = Lists.newArrayListWithExpectedSize(deleteTsKvQueries.size() * DELETES_PER_ENTRY);
+ for (DeleteTsKvQuery tsKvQuery : deleteTsKvQueries) {
+ deleteAndRegisterFutures(futures, entityId, tsKvQuery);
+ }
+ return Futures.allAsList(futures);
+ }
+
+ private void deleteAndRegisterFutures(List<ListenableFuture<Void>> futures, EntityId entityId, DeleteTsKvQuery query) {
+ futures.add(timeseriesDao.remove(entityId, query));
+ futures.add(timeseriesDao.removeLatest(entityId, query));
+ futures.add(timeseriesDao.removePartition(entityId, query));
+ }
+
private static void validate(EntityId entityId) {
Validator.validateEntityId(entityId, "Incorrect entityId " + entityId);
}
- private static void validate(TsKvQuery query) {
+ private static void validate(ReadTsKvQuery query) {
if (query == null) {
- throw new IncorrectParameterException("TsKvQuery can't be null");
+ throw new IncorrectParameterException("ReadTsKvQuery can't be null");
} else if (isBlank(query.getKey())) {
- throw new IncorrectParameterException("Incorrect TsKvQuery. Key can't be empty");
+ throw new IncorrectParameterException("Incorrect ReadTsKvQuery. Key can't be empty");
} else if (query.getAggregation() == null) {
- throw new IncorrectParameterException("Incorrect TsKvQuery. Aggregation can't be empty");
+ throw new IncorrectParameterException("Incorrect ReadTsKvQuery. Aggregation can't be empty");
+ }
+ }
+
+ private static void validate(DeleteTsKvQuery query) {
+ if (query == null) {
+ throw new IncorrectParameterException("DeleteTsKvQuery can't be null");
+ } else if (isBlank(query.getKey())) {
+ throw new IncorrectParameterException("Incorrect DeleteTsKvQuery. Key can't be empty");
}
}
- private static TsKvQuery updateQuery(Long startTs, Long endTs, String viewKey, TsKvQuery query) {
+ private static ReadTsKvQuery updateQuery(Long startTs, Long endTs, String viewKey, ReadTsKvQuery query) {
return startTs <= query.getStartTs() && endTs >= query.getEndTs() ? query :
- new BaseTsKvQuery(viewKey, startTs, endTs, query.getInterval(), query.getLimit(), query.getAggregation());
+ new BaseReadTsKvQuery(viewKey, startTs, endTs, query.getInterval(), query.getLimit(), query.getAggregation());
}
private static void checkForNonEntityView(EntityId entityId) throws Exception {
diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java
index c025e64..03d569d 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java
@@ -20,6 +20,7 @@ import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.ResultSetFuture;
import com.datastax.driver.core.Row;
+import com.datastax.driver.core.Statement;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
import com.google.common.base.Function;
@@ -34,16 +35,17 @@ import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.kv.Aggregation;
-import org.thingsboard.server.common.data.kv.BaseTsKvQuery;
+import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.BooleanDataEntry;
import org.thingsboard.server.common.data.kv.DataType;
+import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
import org.thingsboard.server.common.data.kv.DoubleDataEntry;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry;
+import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
-import org.thingsboard.server.common.data.kv.TsKvQuery;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.nosql.CassandraAbstractAsyncDao;
import org.thingsboard.server.dao.util.NoSqlDao;
@@ -54,11 +56,11 @@ import javax.annotation.PreDestroy;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
-import java.util.ArrayList;
import java.util.Optional;
-import java.util.Collections;
import java.util.stream.Collectors;
import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
@@ -76,8 +78,8 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
public static final String GENERATED_QUERY_FOR_ENTITY_TYPE_AND_ENTITY_ID = "Generated query [{}] for entityType {} and entityId {}";
public static final String SELECT_PREFIX = "SELECT ";
public static final String EQUALS_PARAM = " = ? ";
-
-
+ public static final String ASC_ORDER = "ASC";
+ public static final String DESC_ORDER = "DESC";
private static List<Long> FIXED_PARTITION = Arrays.asList(new Long[]{0L});
@Autowired
@@ -96,9 +98,12 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
private PreparedStatement latestInsertStmt;
private PreparedStatement[] saveStmts;
private PreparedStatement[] saveTtlStmts;
- private PreparedStatement[] fetchStmts;
+ private PreparedStatement[] fetchStmtsAsc;
+ private PreparedStatement[] fetchStmtsDesc;
private PreparedStatement findLatestStmt;
private PreparedStatement findAllLatestStmt;
+ private PreparedStatement deleteStmt;
+ private PreparedStatement deletePartitionStmt;
private boolean isInstall() {
return environment.acceptsProfiles("install");
@@ -108,7 +113,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
public void init() {
super.startExecutor();
if (!isInstall()) {
- getFetchStmt(Aggregation.NONE);
+ getFetchStmt(Aggregation.NONE, DESC_ORDER);
Optional<TsPartitionDate> partition = TsPartitionDate.parse(partitioning);
if (partition.isPresent()) {
tsFormat = partition.get();
@@ -125,7 +130,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
}
@Override
- public ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, List<TsKvQuery> queries) {
+ public ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, List<ReadTsKvQuery> queries) {
List<ListenableFuture<List<TsKvEntry>>> futures = queries.stream().map(query -> findAllAsync(entityId, query)).collect(Collectors.toList());
return Futures.transform(Futures.allAsList(futures), new Function<List<List<TsKvEntry>>, List<TsKvEntry>>() {
@Nullable
@@ -142,7 +147,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
}
- private ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, TsKvQuery query) {
+ private ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, ReadTsKvQuery query) {
if (query.getAggregation() == Aggregation.NONE) {
return findAllAsyncWithLimit(entityId, query);
} else {
@@ -152,7 +157,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
while (stepTs < query.getEndTs()) {
long startTs = stepTs;
long endTs = stepTs + step;
- TsKvQuery subQuery = new BaseTsKvQuery(query.getKey(), startTs, endTs, step, 1, query.getAggregation());
+ ReadTsKvQuery subQuery = new BaseReadTsKvQuery(query.getKey(), startTs, endTs, step, 1, query.getAggregation(), query.getOrderBy());
futures.add(findAndAggregateAsync(entityId, subQuery, toPartitionTs(startTs), toPartitionTs(endTs)));
stepTs = endTs;
}
@@ -171,7 +176,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
return tsFormat.getTruncateUnit().equals(TsPartitionDate.EPOCH_START);
}
- private ListenableFuture<List<Long>> getPartitionsFuture(TsKvQuery query, EntityId entityId, long minPartition, long maxPartition) {
+ private ListenableFuture<List<Long>> getPartitionsFuture(ReadTsKvQuery query, EntityId entityId, long minPartition, long maxPartition) {
if (isFixedPartitioning()) { //no need to fetch partitions from DB
return Futures.immediateFuture(FIXED_PARTITION);
}
@@ -179,11 +184,9 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
return Futures.transform(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor);
}
- private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, TsKvQuery query) {
-
+ private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, ReadTsKvQuery query) {
long minPartition = toPartitionTs(query.getStartTs());
long maxPartition = toPartitionTs(query.getEndTs());
-
final ListenableFuture<List<Long>> partitionsListFuture = getPartitionsFuture(query, entityId, minPartition, maxPartition);
final SimpleListenableFuture<List<TsKvEntry>> resultFuture = new SimpleListenableFuture<>();
@@ -212,7 +215,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
if (cursor.isFull() || !cursor.hasNextPartition()) {
resultFuture.set(cursor.getData());
} else {
- PreparedStatement proto = getFetchStmt(Aggregation.NONE);
+ PreparedStatement proto = getFetchStmt(Aggregation.NONE, cursor.getOrderBy());
BoundStatement stmt = proto.bind();
stmt.setString(0, cursor.getEntityType());
stmt.setUUID(1, cursor.getEntityId());
@@ -237,14 +240,12 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
}
}
- private ListenableFuture<Optional<TsKvEntry>> findAndAggregateAsync(EntityId entityId, TsKvQuery query, long minPartition, long maxPartition) {
+ private ListenableFuture<Optional<TsKvEntry>> findAndAggregateAsync(EntityId entityId, ReadTsKvQuery query, long minPartition, long maxPartition) {
final Aggregation aggregation = query.getAggregation();
final String key = query.getKey();
final long startTs = query.getStartTs();
final long endTs = query.getEndTs();
final long ts = startTs + (endTs - startTs) / 2;
-
-
ListenableFuture<List<Long>> partitionsListFuture = getPartitionsFuture(query, entityId, minPartition, maxPartition);
ListenableFuture<List<ResultSet>> aggregationChunks = Futures.transformAsync(partitionsListFuture,
getFetchChunksAsyncFunction(entityId, key, aggregation, startTs, endTs), readResultsProcessingExecutor);
@@ -260,7 +261,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
private AsyncFunction<List<Long>, List<ResultSet>> getFetchChunksAsyncFunction(EntityId entityId, String key, Aggregation aggregation, long startTs, long endTs) {
return partitions -> {
try {
- PreparedStatement proto = getFetchStmt(aggregation);
+ PreparedStatement proto = getFetchStmt(aggregation, DESC_ORDER);
List<ResultSetFuture> futures = new ArrayList<>(partitions.size());
for (Long partition : partitions) {
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
return getFuture(executeAsyncWrite(stmt), rs -> null);
}
+ @Override
+ public ListenableFuture<Void> remove(EntityId entityId, DeleteTsKvQuery query) {
+ long minPartition = toPartitionTs(query.getStartTs());
+ long maxPartition = toPartitionTs(query.getEndTs());
+
+ ResultSetFuture partitionsFuture = fetchPartitions(entityId, query.getKey(), minPartition, maxPartition);
+
+ final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();
+ final ListenableFuture<List<Long>> partitionsListFuture = Futures.transform(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor);
+
+ Futures.addCallback(partitionsListFuture, new FutureCallback<List<Long>>() {
+ @Override
+ public void onSuccess(@Nullable List<Long> partitions) {
+ QueryCursor cursor = new QueryCursor(entityId.getEntityType().name(), entityId.getId(), query, partitions);
+ deleteAsync(cursor, resultFuture);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ log.error("[{}][{}] Failed to fetch partitions for interval {}-{}", entityId.getEntityType().name(), entityId.getId(), minPartition, maxPartition, t);
+ }
+ }, readResultsProcessingExecutor);
+ return resultFuture;
+ }
+
+ private void deleteAsync(final QueryCursor cursor, final SimpleListenableFuture<Void> resultFuture) {
+ if (!cursor.hasNextPartition()) {
+ resultFuture.set(null);
+ } else {
+ PreparedStatement proto = getDeleteStmt();
+ BoundStatement stmt = proto.bind();
+ stmt.setString(0, cursor.getEntityType());
+ stmt.setUUID(1, cursor.getEntityId());
+ stmt.setString(2, cursor.getKey());
+ stmt.setLong(3, cursor.getNextPartition());
+ stmt.setLong(4, cursor.getStartTs());
+ stmt.setLong(5, cursor.getEndTs());
+
+ Futures.addCallback(executeAsyncWrite(stmt), new FutureCallback<ResultSet>() {
+ @Override
+ public void onSuccess(@Nullable ResultSet result) {
+ deleteAsync(cursor, resultFuture);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ log.error("[{}][{}] Failed to delete data for query {}-{}", stmt, t);
+ }
+ }, readResultsProcessingExecutor);
+ }
+ }
+
+ private PreparedStatement getDeleteStmt() {
+ if (deleteStmt == null) {
+ deleteStmt = prepare("DELETE FROM " + ModelConstants.TS_KV_CF +
+ " WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM
+ + "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM
+ + "AND " + ModelConstants.KEY_COLUMN + EQUALS_PARAM
+ + "AND " + ModelConstants.PARTITION_COLUMN + EQUALS_PARAM
+ + "AND " + ModelConstants.TS_COLUMN + " > ? "
+ + "AND " + ModelConstants.TS_COLUMN + " <= ?");
+ }
+ return deleteStmt;
+ }
+
+ @Override
+ public ListenableFuture<Void> removeLatest(EntityId entityId, DeleteTsKvQuery query) {
+ ListenableFuture<TsKvEntry> latestEntryFuture = findLatest(entityId, query.getKey());
+
+ ListenableFuture<Boolean> booleanFuture = Futures.transformAsync(latestEntryFuture, latestEntry -> {
+ long ts = latestEntry.getTs();
+ if (ts >= query.getStartTs() && ts <= query.getEndTs()) {
+ return Futures.immediateFuture(true);
+ } else {
+ log.trace("Won't be deleted latest value for [{}], key - {}", entityId, query.getKey());
+ }
+ return Futures.immediateFuture(false);
+ }, readResultsProcessingExecutor);
+
+ ListenableFuture<Void> removedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
+ if (isRemove) {
+ return deleteLatest(entityId, query.getKey());
+ }
+ return Futures.immediateFuture(null);
+ }, readResultsProcessingExecutor);
+
+ if (query.getRewriteLatestIfDeleted()) {
+ ListenableFuture<Void> savedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
+ if (isRemove) {
+ return getNewLatestEntryFuture(entityId, query);
+ }
+ return Futures.immediateFuture(null);
+ }, readResultsProcessingExecutor);
+
+ return Futures.transformAsync(Futures.allAsList(Arrays.asList(savedLatestFuture, removedLatestFuture)),
+ list -> Futures.immediateFuture(null), readResultsProcessingExecutor);
+ }
+ return removedLatestFuture;
+ }
+
+ private ListenableFuture<Void> getNewLatestEntryFuture(EntityId entityId, DeleteTsKvQuery query) {
+ long startTs = 0;
+ long endTs = query.getStartTs() - 1;
+ ReadTsKvQuery findNewLatestQuery = new BaseReadTsKvQuery(query.getKey(), startTs, endTs, endTs - startTs, 1,
+ Aggregation.NONE, DESC_ORDER);
+ ListenableFuture<List<TsKvEntry>> future = findAllAsync(entityId, findNewLatestQuery);
+
+ return Futures.transformAsync(future, entryList -> {
+ if (entryList.size() == 1) {
+ return saveLatest(entityId, entryList.get(0));
+ } else {
+ log.trace("Could not find new latest value for [{}], key - {}", entityId, query.getKey());
+ }
+ return Futures.immediateFuture(null);
+ }, readResultsProcessingExecutor);
+ }
+
+ private ListenableFuture<Void> deleteLatest(EntityId entityId, String key) {
+ Statement delete = QueryBuilder.delete().all().from(ModelConstants.TS_KV_LATEST_CF)
+ .where(eq(ModelConstants.ENTITY_TYPE_COLUMN, entityId.getEntityType()))
+ .and(eq(ModelConstants.ENTITY_ID_COLUMN, entityId.getId()))
+ .and(eq(ModelConstants.KEY_COLUMN, key));
+ log.debug("Remove request: {}", delete.toString());
+ return getFuture(executeAsyncWrite(delete), rs -> null);
+ }
+
+ @Override
+ public ListenableFuture<Void> removePartition(EntityId entityId, DeleteTsKvQuery query) {
+ long minPartition = toPartitionTs(query.getStartTs());
+ long maxPartition = toPartitionTs(query.getEndTs());
+ if (minPartition == maxPartition) {
+ return Futures.immediateFuture(null);
+ } else {
+ ResultSetFuture partitionsFuture = fetchPartitions(entityId, query.getKey(), minPartition, maxPartition);
+
+ final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();
+ final ListenableFuture<List<Long>> partitionsListFuture = Futures.transform(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor);
+
+ Futures.addCallback(partitionsListFuture, new FutureCallback<List<Long>>() {
+ @Override
+ public void onSuccess(@Nullable List<Long> partitions) {
+ int index = 0;
+ if (minPartition != query.getStartTs()) {
+ index = 1;
+ }
+ List<Long> partitionsToDelete = new ArrayList<>();
+ for (int i = index; i < partitions.size() - 1; i++) {
+ partitionsToDelete.add(partitions.get(i));
+ }
+ QueryCursor cursor = new QueryCursor(entityId.getEntityType().name(), entityId.getId(), query, partitionsToDelete);
+ deletePartitionAsync(cursor, resultFuture);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ log.error("[{}][{}] Failed to fetch partitions for interval {}-{}", entityId.getEntityType().name(), entityId.getId(), minPartition, maxPartition, t);
+ }
+ }, readResultsProcessingExecutor);
+ return resultFuture;
+ }
+ }
+
+ private void deletePartitionAsync(final QueryCursor cursor, final SimpleListenableFuture<Void> resultFuture) {
+ if (!cursor.hasNextPartition()) {
+ resultFuture.set(null);
+ } else {
+ PreparedStatement proto = getDeletePartitionStmt();
+ BoundStatement stmt = proto.bind();
+ stmt.setString(0, cursor.getEntityType());
+ stmt.setUUID(1, cursor.getEntityId());
+ stmt.setLong(2, cursor.getNextPartition());
+ stmt.setString(3, cursor.getKey());
+
+ Futures.addCallback(executeAsyncWrite(stmt), new FutureCallback<ResultSet>() {
+ @Override
+ public void onSuccess(@Nullable ResultSet result) {
+ deletePartitionAsync(cursor, resultFuture);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ log.error("[{}][{}] Failed to delete data for query {}-{}", stmt, t);
+ }
+ }, readResultsProcessingExecutor);
+ }
+ }
+
+ private PreparedStatement getDeletePartitionStmt() {
+ if (deletePartitionStmt == null) {
+ deletePartitionStmt = prepare("DELETE FROM " + ModelConstants.TS_KV_PARTITIONS_CF +
+ " WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM
+ + "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM
+ + "AND " + ModelConstants.PARTITION_COLUMN + EQUALS_PARAM
+ + "AND " + ModelConstants.KEY_COLUMN + EQUALS_PARAM);
+ }
+ return deletePartitionStmt;
+ }
+
private List<TsKvEntry> convertResultToTsKvEntryList(List<Row> rows) {
List<TsKvEntry> entries = new ArrayList<>(rows.size());
if (!rows.isEmpty()) {
@@ -458,28 +657,43 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
return saveTtlStmts[dataType.ordinal()];
}
- private PreparedStatement getFetchStmt(Aggregation aggType) {
- if (fetchStmts == null) {
- fetchStmts = new PreparedStatement[Aggregation.values().length];
- for (Aggregation type : Aggregation.values()) {
- if (type == Aggregation.SUM && fetchStmts[Aggregation.AVG.ordinal()] != null) {
- fetchStmts[type.ordinal()] = fetchStmts[Aggregation.AVG.ordinal()];
- } else if (type == Aggregation.AVG && fetchStmts[Aggregation.SUM.ordinal()] != null) {
- fetchStmts[type.ordinal()] = fetchStmts[Aggregation.SUM.ordinal()];
- } else {
- fetchStmts[type.ordinal()] = prepare(SELECT_PREFIX +
- String.join(", ", ModelConstants.getFetchColumnNames(type)) + " FROM " + ModelConstants.TS_KV_CF
- + " WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM
- + "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM
- + "AND " + ModelConstants.KEY_COLUMN + EQUALS_PARAM
- + "AND " + ModelConstants.PARTITION_COLUMN + EQUALS_PARAM
- + "AND " + ModelConstants.TS_COLUMN + " > ? "
- + "AND " + ModelConstants.TS_COLUMN + " <= ?"
- + (type == Aggregation.NONE ? " ORDER BY " + ModelConstants.TS_COLUMN + " DESC LIMIT ?" : ""));
+ private PreparedStatement getFetchStmt(Aggregation aggType, String orderBy) {
+ switch (orderBy) {
+ case ASC_ORDER:
+ if (fetchStmtsAsc == null) {
+ fetchStmtsAsc = initFetchStmt(orderBy);
+ }
+ return fetchStmtsAsc[aggType.ordinal()];
+ case DESC_ORDER:
+ if (fetchStmtsDesc == null) {
+ fetchStmtsDesc = initFetchStmt(orderBy);
}
+ return fetchStmtsDesc[aggType.ordinal()];
+ default:
+ throw new RuntimeException("Not supported" + orderBy + "order!");
+ }
+ }
+
+ private PreparedStatement[] initFetchStmt(String orderBy) {
+ PreparedStatement[] fetchStmts = new PreparedStatement[Aggregation.values().length];
+ for (Aggregation type : Aggregation.values()) {
+ if (type == Aggregation.SUM && fetchStmts[Aggregation.AVG.ordinal()] != null) {
+ fetchStmts[type.ordinal()] = fetchStmts[Aggregation.AVG.ordinal()];
+ } else if (type == Aggregation.AVG && fetchStmts[Aggregation.SUM.ordinal()] != null) {
+ fetchStmts[type.ordinal()] = fetchStmts[Aggregation.SUM.ordinal()];
+ } else {
+ fetchStmts[type.ordinal()] = prepare(SELECT_PREFIX +
+ String.join(", ", ModelConstants.getFetchColumnNames(type)) + " FROM " + ModelConstants.TS_KV_CF
+ + " WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM
+ + "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM
+ + "AND " + ModelConstants.KEY_COLUMN + EQUALS_PARAM
+ + "AND " + ModelConstants.PARTITION_COLUMN + EQUALS_PARAM
+ + "AND " + ModelConstants.TS_COLUMN + " > ? "
+ + "AND " + ModelConstants.TS_COLUMN + " <= ?"
+ + (type == Aggregation.NONE ? " ORDER BY " + ModelConstants.TS_COLUMN + " " + orderBy + " LIMIT ?" : ""));
}
}
- return fetchStmts[aggType.ordinal()];
+ return fetchStmts;
}
private PreparedStatement getLatestStmt() {
diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/QueryCursor.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/QueryCursor.java
new file mode 100644
index 0000000..f210608
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/QueryCursor.java
@@ -0,0 +1,60 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.dao.timeseries;
+
+import lombok.Getter;
+import org.thingsboard.server.common.data.kv.TsKvQuery;
+
+import java.util.List;
+import java.util.UUID;
+
+public class QueryCursor {
+
+ @Getter
+ protected final String entityType;
+ @Getter
+ protected final UUID entityId;
+ @Getter
+ protected final String key;
+ @Getter
+ private final long startTs;
+ @Getter
+ private final long endTs;
+
+ final List<Long> partitions;
+ private int partitionIndex;
+
+ public QueryCursor(String entityType, UUID entityId, TsKvQuery baseQuery, List<Long> partitions) {
+ this.entityType = entityType;
+ this.entityId = entityId;
+ this.key = baseQuery.getKey();
+ this.startTs = baseQuery.getStartTs();
+ this.endTs = baseQuery.getEndTs();
+ this.partitions = partitions;
+ this.partitionIndex = partitions.size() - 1;
+ }
+
+ public boolean hasNextPartition() {
+ return partitionIndex >= 0;
+ }
+
+ public long getNextPartition() {
+ long partition = partitions.get(partitionIndex);
+ partitionIndex--;
+ return partition;
+ }
+
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesDao.java
index 1e3f4ce..a8cb547 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesDao.java
@@ -17,8 +17,9 @@ package org.thingsboard.server.dao.timeseries;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
+import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.TsKvEntry;
-import org.thingsboard.server.common.data.kv.TsKvQuery;
import java.util.List;
@@ -27,7 +28,7 @@ import java.util.List;
*/
public interface TimeseriesDao {
- ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, List<TsKvQuery> queries);
+ ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, List<ReadTsKvQuery> queries);
ListenableFuture<TsKvEntry> findLatest(EntityId entityId, String key);
@@ -38,4 +39,10 @@ public interface TimeseriesDao {
ListenableFuture<Void> savePartition(EntityId entityId, long tsKvEntryTs, String key, long ttl);
ListenableFuture<Void> saveLatest(EntityId entityId, TsKvEntry tsKvEntry);
+
+ ListenableFuture<Void> remove(EntityId entityId, DeleteTsKvQuery query);
+
+ ListenableFuture<Void> removeLatest(EntityId entityId, DeleteTsKvQuery query);
+
+ ListenableFuture<Void> removePartition(EntityId entityId, DeleteTsKvQuery query);
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesService.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesService.java
index 2cd2d8d..c5076d2 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesService.java
@@ -17,8 +17,9 @@ package org.thingsboard.server.dao.timeseries;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
+import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.TsKvEntry;
-import org.thingsboard.server.common.data.kv.TsKvQuery;
import java.util.Collection;
import java.util.List;
@@ -28,7 +29,7 @@ import java.util.List;
*/
public interface TimeseriesService {
- ListenableFuture<List<TsKvEntry>> findAll(EntityId entityId, List<TsKvQuery> queries);
+ ListenableFuture<List<TsKvEntry>> findAll(EntityId entityId, List<ReadTsKvQuery> queries);
ListenableFuture<List<TsKvEntry>> findLatest(EntityId entityId, Collection<String> keys);
@@ -37,4 +38,6 @@ public interface TimeseriesService {
ListenableFuture<List<Void>> save(EntityId entityId, TsKvEntry tsKvEntry);
ListenableFuture<List<Void>> save(EntityId entityId, List<TsKvEntry> tsKvEntry, long ttl);
+
+ ListenableFuture<List<Void>> remove(EntityId entityId, List<DeleteTsKvQuery> queries);
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/TsKvQueryCursor.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/TsKvQueryCursor.java
index d6b6bbd..b56be56 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/TsKvQueryCursor.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/TsKvQueryCursor.java
@@ -16,57 +16,53 @@
package org.thingsboard.server.dao.timeseries;
import lombok.Getter;
+import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.TsKvEntry;
-import org.thingsboard.server.common.data.kv.TsKvQuery;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
+import static org.thingsboard.server.dao.timeseries.CassandraBaseTimeseriesDao.DESC_ORDER;
+
/**
* Created by ashvayka on 21.02.17.
*/
-public class TsKvQueryCursor {
- @Getter
- private final String entityType;
- @Getter
- private final UUID entityId;
- @Getter
- private final String key;
- @Getter
- private final long startTs;
- @Getter
- private final long endTs;
- private final List<Long> partitions;
+public class TsKvQueryCursor extends QueryCursor {
+
@Getter
private final List<TsKvEntry> data;
+ @Getter
+ private String orderBy;
private int partitionIndex;
private int currentLimit;
- public TsKvQueryCursor(String entityType, UUID entityId, TsKvQuery baseQuery, List<Long> partitions) {
- this.entityType = entityType;
- this.entityId = entityId;
- this.key = baseQuery.getKey();
- this.startTs = baseQuery.getStartTs();
- this.endTs = baseQuery.getEndTs();
- this.partitions = partitions;
- this.partitionIndex = partitions.size() - 1;
+ public TsKvQueryCursor(String entityType, UUID entityId, ReadTsKvQuery baseQuery, List<Long> partitions) {
+ super(entityType, entityId, baseQuery, partitions);
+ this.orderBy = baseQuery.getOrderBy();
+ this.partitionIndex = isDesc() ? partitions.size() - 1 : 0;
this.data = new ArrayList<>();
this.currentLimit = baseQuery.getLimit();
}
+ @Override
public boolean hasNextPartition() {
- return partitionIndex >= 0;
+ return isDesc() ? partitionIndex >= 0 : partitionIndex <= partitions.size() - 1;
}
public boolean isFull() {
return currentLimit <= 0;
}
+ @Override
public long getNextPartition() {
long partition = partitions.get(partitionIndex);
- partitionIndex--;
+ if (isDesc()) {
+ partitionIndex--;
+ } else {
+ partitionIndex++;
+ }
return partition;
}
@@ -78,4 +74,8 @@ public class TsKvQueryCursor {
currentLimit -= newData.size();
data.addAll(newData);
}
+
+ private boolean isDesc() {
+ return orderBy.equals(DESC_ORDER);
+ }
}
diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseRelationServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseRelationServiceTest.java
index 743aefc..d269126 100644
--- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseRelationServiceTest.java
+++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseRelationServiceTest.java
@@ -227,6 +227,13 @@ public abstract class BaseRelationServiceTest extends AbstractServiceTest {
Assert.assertTrue(relations.contains(relationA));
Assert.assertTrue(relations.contains(relationB));
Assert.assertTrue(relations.contains(relationC));
+
+ //Test from cache
+ relations = relationService.findByQuery(query).get();
+ Assert.assertEquals(3, relations.size());
+ Assert.assertTrue(relations.contains(relationA));
+ Assert.assertTrue(relations.contains(relationB));
+ Assert.assertTrue(relations.contains(relationC));
}
@Test
@@ -253,6 +260,12 @@ public abstract class BaseRelationServiceTest extends AbstractServiceTest {
Assert.assertEquals(2, relations.size());
Assert.assertTrue(relations.contains(relationAB));
Assert.assertTrue(relations.contains(relationBC));
+
+ //Test from cache
+ relations = relationService.findByQuery(query).get();
+ Assert.assertEquals(2, relations.size());
+ Assert.assertTrue(relations.contains(relationAB));
+ Assert.assertTrue(relations.contains(relationBC));
}
diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java
index f045bf1..4528d9a 100644
--- a/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java
+++ b/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java
@@ -21,7 +21,8 @@ import org.junit.Assert;
import org.junit.Test;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.kv.Aggregation;
-import org.thingsboard.server.common.data.kv.BaseTsKvQuery;
+import org.thingsboard.server.common.data.kv.BaseDeleteTsKvQuery;
+import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.BooleanDataEntry;
import org.thingsboard.server.common.data.kv.DoubleDataEntry;
@@ -53,6 +54,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
private static final String BOOLEAN_KEY = "booleanKey";
private static final long TS = 42L;
+ private static final String DESC_ORDER = "DESC";
KvEntry stringKvEntry = new StringDataEntry(STRING_KEY, "value");
KvEntry longKvEntry = new LongDataEntry(LONG_KEY, Long.MAX_VALUE);
@@ -101,6 +103,26 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
}
@Test
+ public void testDeleteDeviceTsData() throws Exception {
+ DeviceId deviceId = new DeviceId(UUIDs.timeBased());
+
+ saveEntries(deviceId, 10000);
+ saveEntries(deviceId, 20000);
+ saveEntries(deviceId, 30000);
+ saveEntries(deviceId, 40000);
+
+ tsService.remove(deviceId, Collections.singletonList(
+ new BaseDeleteTsKvQuery(STRING_KEY, 15000, 45000))).get();
+
+ List<TsKvEntry> list = tsService.findAll(deviceId, Collections.singletonList(
+ new BaseReadTsKvQuery(STRING_KEY, 5000, 45000, 10000, 10, Aggregation.NONE))).get();
+ Assert.assertEquals(1, list.size());
+
+ List<TsKvEntry> latest = tsService.findLatest(deviceId, Collections.singletonList(STRING_KEY)).get();
+ Assert.assertEquals(null, latest.get(0).getValueAsString());
+ }
+
+ @Test
public void testFindDeviceTsData() throws Exception {
DeviceId deviceId = new DeviceId(UUIDs.timeBased());
List<TsKvEntry> entries = new ArrayList<>();
@@ -114,7 +136,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
entries.add(save(deviceId, 45000, 500));
entries.add(save(deviceId, 55000, 600));
- List<TsKvEntry> list = tsService.findAll(deviceId, Collections.singletonList(new BaseTsKvQuery(LONG_KEY, 0,
+ List<TsKvEntry> list = tsService.findAll(deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
60000, 20000, 3, Aggregation.NONE))).get();
assertEquals(3, list.size());
assertEquals(55000, list.get(0).getTs());
@@ -126,7 +148,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
assertEquals(35000, list.get(2).getTs());
assertEquals(java.util.Optional.of(400L), list.get(2).getLongValue());
- list = tsService.findAll(deviceId, Collections.singletonList(new BaseTsKvQuery(LONG_KEY, 0,
+ list = tsService.findAll(deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
60000, 20000, 3, Aggregation.AVG))).get();
assertEquals(3, list.size());
assertEquals(10000, list.get(0).getTs());
@@ -138,7 +160,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
assertEquals(50000, list.get(2).getTs());
assertEquals(java.util.Optional.of(550L), list.get(2).getLongValue());
- list = tsService.findAll(deviceId, Collections.singletonList(new BaseTsKvQuery(LONG_KEY, 0,
+ list = tsService.findAll(deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
60000, 20000, 3, Aggregation.SUM))).get();
assertEquals(3, list.size());
@@ -151,7 +173,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
assertEquals(50000, list.get(2).getTs());
assertEquals(java.util.Optional.of(1100L), list.get(2).getLongValue());
- list = tsService.findAll(deviceId, Collections.singletonList(new BaseTsKvQuery(LONG_KEY, 0,
+ list = tsService.findAll(deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
60000, 20000, 3, Aggregation.MIN))).get();
assertEquals(3, list.size());
@@ -164,7 +186,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
assertEquals(50000, list.get(2).getTs());
assertEquals(java.util.Optional.of(500L), list.get(2).getLongValue());
- list = tsService.findAll(deviceId, Collections.singletonList(new BaseTsKvQuery(LONG_KEY, 0,
+ list = tsService.findAll(deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
60000, 20000, 3, Aggregation.MAX))).get();
assertEquals(3, list.size());
@@ -177,7 +199,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
assertEquals(50000, list.get(2).getTs());
assertEquals(java.util.Optional.of(600L), list.get(2).getLongValue());
- list = tsService.findAll(deviceId, Collections.singletonList(new BaseTsKvQuery(LONG_KEY, 0,
+ list = tsService.findAll(deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
60000, 20000, 3, Aggregation.COUNT))).get();
assertEquals(3, list.size());
diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientConfig.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientConfig.java
index a59d83b..bcb8bd1 100644
--- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientConfig.java
+++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientConfig.java
@@ -40,6 +40,8 @@ public final class MqttClientConfig {
private Class<? extends Channel> channelClass = NioSocketChannel.class;
private boolean reconnect = true;
+ private long reconnectDelay = 1L;
+ private int maxBytesInMessage = 8092;
public MqttClientConfig() {
this(null);
@@ -146,4 +148,38 @@ public final class MqttClientConfig {
public void setReconnect(boolean reconnect) {
this.reconnect = reconnect;
}
+
+ public long getReconnectDelay() {
+ return reconnectDelay;
+ }
+
+ /**
+ * Sets the reconnect delay in seconds. Defaults to 1 second.
+ * @param reconnectDelay
+ * @throws IllegalArgumentException if reconnectDelay is smaller than 1.
+ */
+ public void setReconnectDelay(long reconnectDelay) {
+ if (reconnectDelay <= 0) {
+ throw new IllegalArgumentException("reconnectDelay must be > 0");
+ }
+ this.reconnectDelay = reconnectDelay;
+ }
+
+ public int getMaxBytesInMessage() {
+ return maxBytesInMessage;
+ }
+
+ /**
+ * Sets the maximum number of bytes in the message for the {@link io.netty.handler.codec.mqtt.MqttDecoder}.
+ * Default value is 8092 as specified by Netty. The absolute maximum size is 256MB as set by the MQTT spec.
+ *
+ * @param maxBytesInMessage
+ * @throws IllegalArgumentException if maxBytesInMessage is smaller than 1 or greater than 256_000_000.
+ */
+ public void setMaxBytesInMessage(int maxBytesInMessage) {
+ if (maxBytesInMessage <= 0 || maxBytesInMessage > 256_000_000) {
+ throw new IllegalArgumentException("maxBytesInMessage must be > 0 or < 256_000_000");
+ }
+ this.maxBytesInMessage = maxBytesInMessage;
+ }
}
diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java
index a5df846..b9460b3 100644
--- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java
+++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java
@@ -155,7 +155,7 @@ final class MqttClientImpl implements MqttClient {
if (reconnect) {
this.reconnect = true;
}
- eventLoop.schedule((Runnable) () -> connect(host, port, reconnect), 1L, TimeUnit.SECONDS);
+ eventLoop.schedule((Runnable) () -> connect(host, port, reconnect), clientConfig.getReconnectDelay(), TimeUnit.SECONDS);
}
}
@@ -512,7 +512,7 @@ final class MqttClientImpl implements MqttClient {
ch.pipeline().addLast(sslContext.newHandler(ch.alloc(), host, port));
}
- ch.pipeline().addLast("mqttDecoder", new MqttDecoder());
+ ch.pipeline().addLast("mqttDecoder", new MqttDecoder(clientConfig.getMaxBytesInMessage()));
ch.pipeline().addLast("mqttEncoder", MqttEncoder.INSTANCE);
ch.pipeline().addLast("idleStateHandler", new IdleStateHandler(MqttClientImpl.this.clientConfig.getTimeoutSeconds(), MqttClientImpl.this.clientConfig.getTimeoutSeconds(), 0));
ch.pipeline().addLast("mqttPingHandler", new MqttPingHandler(MqttClientImpl.this.clientConfig.getTimeoutSeconds()));
diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java
index aa57a02..c70b906 100644
--- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java
+++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java
@@ -16,11 +16,14 @@
package org.thingsboard.rule.engine.api;
import com.google.common.util.concurrent.FutureCallback;
+import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import java.util.List;
+import java.util.Set;
/**
* Created by ashvayka on 02.04.18.
@@ -41,4 +44,6 @@ public interface RuleEngineTelemetryService {
void saveAttrAndNotify(EntityId entityId, String scope, String key, boolean value, FutureCallback<Void> callback);
+ void onSharedAttributesUpdate(TenantId tenantId, DeviceId deviceId, Set<AttributeKvEntry> attributes);
+
}
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNode.java
new file mode 100644
index 0000000..d26f178
--- /dev/null
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNode.java
@@ -0,0 +1,164 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.rule.engine.metadata;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.util.concurrent.ListenableFuture;
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.rule.engine.api.*;
+import org.thingsboard.rule.engine.api.util.DonAsynchron;
+import org.thingsboard.rule.engine.api.util.TbNodeUtils;
+import org.thingsboard.server.common.data.kv.BaseTsKvQuery;
+import org.thingsboard.server.common.data.kv.TsKvEntry;
+import org.thingsboard.server.common.data.kv.TsKvQuery;
+import org.thingsboard.server.common.data.plugin.ComponentType;
+import org.thingsboard.server.common.msg.TbMsg;
+
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS;
+import static org.thingsboard.rule.engine.metadata.TbGetTelemetryNodeConfiguration.FETCH_MODE_ALL;
+import static org.thingsboard.rule.engine.metadata.TbGetTelemetryNodeConfiguration.MAX_FETCH_SIZE;
+import static org.thingsboard.server.common.data.kv.Aggregation.NONE;
+
+/**
+ * Created by mshvayka on 04.09.18.
+ */
+@Slf4j
+@RuleNode(type = ComponentType.ENRICHMENT,
+ name = "originator telemetry",
+ configClazz = TbGetTelemetryNodeConfiguration.class,
+ nodeDescription = "Add Message Originator Telemetry for selected time range into Message Metadata\n",
+ 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. " +
+ "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. " +
+ "If selected fetch mode <b>FIRST</b> or <b>LAST</b> Telemetry will be added like string without Timestamp",
+ uiResources = {"static/rulenode/rulenode-core-config.js"},
+ configDirective = "tbEnrichmentNodeGetTelemetryFromDatabase")
+public class TbGetTelemetryNode implements TbNode {
+
+ private TbGetTelemetryNodeConfiguration config;
+ private List<String> tsKeyNames;
+ private long startTsOffset;
+ private long endTsOffset;
+ private int limit;
+ private ObjectMapper mapper;
+
+ @Override
+ public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
+ this.config = TbNodeUtils.convert(configuration, TbGetTelemetryNodeConfiguration.class);
+ tsKeyNames = config.getLatestTsKeyNames();
+ startTsOffset = TimeUnit.valueOf(config.getStartIntervalTimeUnit()).toMillis(config.getStartInterval());
+ endTsOffset = TimeUnit.valueOf(config.getEndIntervalTimeUnit()).toMillis(config.getEndInterval());
+ limit = config.getFetchMode().equals(FETCH_MODE_ALL) ? MAX_FETCH_SIZE : 1;
+ mapper = new ObjectMapper();
+ mapper.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, false);
+ mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
+ }
+
+ @Override
+ public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException, TbNodeException {
+ if (tsKeyNames.isEmpty()) {
+ ctx.tellFailure(msg, new IllegalStateException("Telemetry is not selected!"));
+ } else {
+ try {
+ List<TsKvQuery> queries = buildQueries();
+ ListenableFuture<List<TsKvEntry>> list = ctx.getTimeseriesService().findAll(msg.getOriginator(), queries);
+ DonAsynchron.withCallback(list, data -> {
+ process(data, msg);
+ TbMsg newMsg = ctx.newMsg(msg.getType(), msg.getOriginator(), msg.getMetaData(), msg.getData());
+ ctx.tellNext(newMsg, SUCCESS);
+ }, error -> ctx.tellFailure(msg, error), ctx.getDbCallbackExecutor());
+ } catch (Exception e) {
+ ctx.tellFailure(msg, e);
+ }
+ }
+ }
+
+ //TODO: handle direction;
+ private List<TsKvQuery> buildQueries() {
+ long ts = System.currentTimeMillis();
+ long startTs = ts - startTsOffset;
+ long endTs = ts - endTsOffset;
+
+ return tsKeyNames.stream()
+ .map(key -> new BaseTsKvQuery(key, startTs, endTs, 1, limit, NONE))
+ .collect(Collectors.toList());
+ }
+
+ private void process(List<TsKvEntry> entries, TbMsg msg) {
+ ObjectNode resultNode = mapper.createObjectNode();
+ if (limit == MAX_FETCH_SIZE) {
+ entries.forEach(entry -> processArray(resultNode, entry));
+ } else {
+ entries.forEach(entry -> processSingle(resultNode, entry));
+ }
+
+ for (String key : tsKeyNames) {
+ if(resultNode.has(key)){
+ msg.getMetaData().putValue(key, resultNode.get(key).toString());
+ }
+ }
+ }
+
+ private void processSingle(ObjectNode node, TsKvEntry entry) {
+ node.put(entry.getKey(), entry.getValueAsString());
+ }
+
+ private void processArray(ObjectNode node, TsKvEntry entry) {
+ if(node.has(entry.getKey())){
+ ArrayNode arrayNode = (ArrayNode) node.get(entry.getKey());
+ ObjectNode obj = buildNode(entry);
+ arrayNode.add(obj);
+ }else {
+ ArrayNode arrayNode = mapper.createArrayNode();
+ ObjectNode obj = buildNode(entry);
+ arrayNode.add(obj);
+ node.set(entry.getKey(), arrayNode);
+ }
+ }
+
+ private ObjectNode buildNode(TsKvEntry entry) {
+ ObjectNode obj = mapper.createObjectNode()
+ .put("ts", entry.getTs());
+ switch (entry.getDataType()) {
+ case STRING:
+ obj.put("value", entry.getValueAsString());
+ break;
+ case LONG:
+ obj.put("value", entry.getLongValue().get());
+ break;
+ case BOOLEAN:
+ obj.put("value", entry.getBooleanValue().get());
+ break;
+ case DOUBLE:
+ obj.put("value", entry.getDoubleValue().get());
+ break;
+ }
+ return obj;
+ }
+
+ @Override
+ public void destroy() {
+
+ }
+}
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNodeConfiguration.java
new file mode 100644
index 0000000..37ce0af
--- /dev/null
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNodeConfiguration.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.rule.engine.metadata;
+
+import lombok.Data;
+import org.thingsboard.rule.engine.api.NodeConfiguration;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Created by mshvayka on 04.09.18.
+ */
+@Data
+public class TbGetTelemetryNodeConfiguration implements NodeConfiguration<TbGetTelemetryNodeConfiguration> {
+
+ public static final String FETCH_MODE_FIRST = "FIRST";
+ public static final String FETCH_MODE_LAST = "LAST";
+ public static final String FETCH_MODE_ALL = "ALL";
+ public static final int MAX_FETCH_SIZE = 1000;
+
+ private int startInterval;
+ private int endInterval;
+ private String startIntervalTimeUnit;
+ private String endIntervalTimeUnit;
+ private String fetchMode; //FIRST, LAST, LATEST
+
+ private List<String> latestTsKeyNames;
+
+
+
+ @Override
+ public TbGetTelemetryNodeConfiguration defaultConfiguration() {
+ TbGetTelemetryNodeConfiguration configuration = new TbGetTelemetryNodeConfiguration();
+ configuration.setLatestTsKeyNames(Collections.emptyList());
+ configuration.setFetchMode("FIRST");
+ configuration.setStartIntervalTimeUnit(TimeUnit.MINUTES.name());
+ configuration.setStartInterval(2);
+ configuration.setEndIntervalTimeUnit(TimeUnit.MINUTES.name());
+ configuration.setEndInterval(1);
+ return configuration;
+ }
+}
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java
index 4f82884..5cdb04e 100644
--- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java
@@ -17,12 +17,15 @@ package org.thingsboard.rule.engine.telemetry;
import com.google.gson.JsonParser;
import lombok.extern.slf4j.Slf4j;
-import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.rule.engine.api.RuleNode;
import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNode;
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException;
+import org.thingsboard.rule.engine.api.util.TbNodeUtils;
+import org.thingsboard.server.common.data.DataConstants;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg;
@@ -62,6 +65,9 @@ public class TbMsgAttributesNode implements TbNode {
String src = msg.getData();
Set<AttributeKvEntry> attributes = JsonConverter.convertToAttributes(new JsonParser().parse(src)).getAttributes();
ctx.getTelemetryService().saveAndNotify(msg.getOriginator(), config.getScope(), new ArrayList<>(attributes), new TelemetryNodeCallback(ctx, msg));
+ if (msg.getOriginator().getEntityType() == EntityType.DEVICE && DataConstants.SHARED_SCOPE.equals(config.getScope())) {
+ ctx.getTelemetryService().onSharedAttributesUpdate(ctx.getTenantId(), new DeviceId(msg.getOriginator().getId()), attributes);
+ }
}
@Override
diff --git a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js
index e06d253..2e0ab78 100644
--- a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js
+++ b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js
@@ -1,4 +1,4 @@
-!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> ';
-},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:'...'}}" }\'>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> </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",{
-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}]));
+!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> ';
+},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:'...'}}" }\'>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> </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);
+}),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}]));
//# sourceMappingURL=rulenode-core-config.js.map
\ No newline at end of file
ui/.stylelintrc 292(+292 -0)
diff --git a/ui/.stylelintrc b/ui/.stylelintrc
new file mode 100644
index 0000000..c145c8b
--- /dev/null
+++ b/ui/.stylelintrc
@@ -0,0 +1,292 @@
+{
+ "extends": ["stylelint-config-standard", "stylelint-config-recommended-scss"],
+ "plugins": [
+ "stylelint-order"
+ ],
+ "rules": {
+ "at-rule-empty-line-before": ["always", {
+ "except": ["first-nested"],
+ "ignore": ["after-comment"]
+ }],
+ "at-rule-name-space-after": "always",
+ "at-rule-no-vendor-prefix": true,
+ "at-rule-semicolon-space-before": "never",
+ "block-closing-brace-empty-line-before": "never",
+ "block-closing-brace-newline-after": null,
+ "block-opening-brace-space-before": null,
+ "color-named": "never",
+ "declaration-block-semicolon-newline-after": "always-multi-line",
+ "declaration-block-semicolon-newline-before": "never-multi-line",
+ "declaration-block-semicolon-space-after": "always-single-line",
+ "declaration-empty-line-before": null,
+ "declaration-no-important": null,
+ "font-family-name-quotes": "always-where-recommended",
+ "font-weight-notation": [
+ "numeric", {
+ "ignore": ["relative"]
+ }],
+ "function-url-no-scheme-relative": true,
+ "function-url-quotes": "always",
+ "length-zero-no-unit": true,
+ "max-empty-lines": 2,
+ "max-line-length": null,
+ "media-feature-name-no-vendor-prefix": true,
+ "media-feature-parentheses-space-inside": "never",
+ "media-feature-range-operator-space-after": "always",
+ "media-feature-range-operator-space-before": "never",
+ "no-descending-specificity": null,
+ "no-duplicate-selectors": true,
+ "number-leading-zero": "never",
+ "media-feature-name-no-unknown": [true, {
+ "ignoreMediaFeatureNames": ["prefers-reduced-motion"]
+ }],
+ "order/properties-order": [
+ "position",
+ "top",
+ "right",
+ "bottom",
+ "left",
+ "z-index",
+ "box-sizing",
+ "display",
+ "flex",
+ "flex-align",
+ "flex-basis",
+ "flex-direction",
+ "flex-wrap",
+ "flex-flow",
+ "flex-shrink",
+ "flex-grow",
+ "flex-order",
+ "flex-pack",
+ "align-content",
+ "align-items",
+ "align-self",
+ "justify-content",
+ "order",
+ "float",
+ "width",
+ "min-width",
+ "max-width",
+ "height",
+ "min-height",
+ "max-height",
+ "padding",
+ "padding-top",
+ "padding-right",
+ "padding-bottom",
+ "padding-left",
+ "margin",
+ "margin-top",
+ "margin-right",
+ "margin-bottom",
+ "margin-left",
+ "overflow",
+ "overflow-x",
+ "overflow-y",
+ "-webkit-overflow-scrolling",
+ "-ms-overflow-x",
+ "-ms-overflow-y",
+ "-ms-overflow-style",
+ "columns",
+ "column-count",
+ "column-fill",
+ "column-gap",
+ "column-rule",
+ "column-rule-width",
+ "column-rule-style",
+ "column-rule-color",
+ "column-span",
+ "column-width",
+ "orphans",
+ "widows",
+ "clip",
+ "clear",
+ "font",
+ "font-family",
+ "font-size",
+ "font-style",
+ "font-weight",
+ "font-variant",
+ "font-size-adjust",
+ "font-stretch",
+ "font-effect",
+ "font-emphasize",
+ "font-emphasize-position",
+ "font-emphasize-style",
+ "font-smooth",
+ "src",
+ "hyphens",
+ "line-height",
+ "color",
+ "text-align",
+ "text-align-last",
+ "text-emphasis",
+ "text-emphasis-color",
+ "text-emphasis-style",
+ "text-emphasis-position",
+ "text-decoration",
+ "text-indent",
+ "text-justify",
+ "text-outline",
+ "-ms-text-overflow",
+ "text-overflow",
+ "text-overflow-ellipsis",
+ "text-overflow-mode",
+ "text-shadow",
+ "text-transform",
+ "text-wrap",
+ "-webkit-text-size-adjust",
+ "-ms-text-size-adjust",
+ "letter-spacing",
+ "-ms-word-break",
+ "word-break",
+ "word-spacing",
+ "-ms-word-wrap",
+ "word-wrap",
+ "overflow-wrap",
+ "tab-size",
+ "white-space",
+ "vertical-align",
+ "direction",
+ "unicode-bidi",
+ "list-style",
+ "list-style-position",
+ "list-style-type",
+ "list-style-image",
+ "pointer-events",
+ "-ms-touch-action",
+ "touch-action",
+ "cursor",
+ "visibility",
+ "zoom",
+ "table-layout",
+ "empty-cells",
+ "caption-side",
+ "border-spacing",
+ "border-collapse",
+ "content",
+ "quotes",
+ "counter-reset",
+ "counter-increment",
+ "resize",
+ "user-select",
+ "nav-index",
+ "nav-up",
+ "nav-right",
+ "nav-down",
+ "nav-left",
+ "background",
+ "background-color",
+ "background-image",
+ "filter",
+ "background-repeat",
+ "background-attachment",
+ "background-position",
+ "background-position-x",
+ "background-position-y",
+ "background-clip",
+ "background-origin",
+ "background-size",
+ "border",
+ "border-color",
+ "border-style",
+ "border-width",
+ "border-top",
+ "border-top-color",
+ "border-top-style",
+ "border-top-width",
+ "border-right",
+ "border-right-color",
+ "border-right-style",
+ "border-right-width",
+ "border-bottom",
+ "border-bottom-color",
+ "border-bottom-style",
+ "border-bottom-width",
+ "border-left",
+ "border-left-color",
+ "border-left-style",
+ "border-left-width",
+ "border-radius",
+ "border-top-left-radius",
+ "border-top-right-radius",
+ "border-bottom-right-radius",
+ "border-bottom-left-radius",
+ "border-image",
+ "border-image-source",
+ "border-image-slice",
+ "border-image-width",
+ "border-image-outset",
+ "border-image-repeat",
+ "outline",
+ "outline-width",
+ "outline-style",
+ "outline-color",
+ "outline-offset",
+ "box-shadow",
+ "opacity",
+ "-ms-interpolation-mode",
+ "page-break-after",
+ "page-break-before",
+ "page-break-inside",
+ "transition",
+ "transition-delay",
+ "transition-timing-function",
+ "transition-duration",
+ "transition-property",
+ "transform",
+ "transform-origin",
+ "perspective",
+ "appearance",
+ "animation",
+ "animation-name",
+ "animation-duration",
+ "animation-play-state",
+ "animation-timing-function",
+ "animation-delay",
+ "animation-iteration-count",
+ "animation-direction",
+ "animation-fill-mode",
+ "fill",
+ "stroke"
+ ],
+ "property-no-vendor-prefix": null,
+ "rule-empty-line-before": ["always", {
+ "except": ["first-nested"],
+ "ignore": ["after-comment"]
+ }],
+ "scss/dollar-variable-default": [true, { "ignore": "local" }],
+ "selector-attribute-quotes": "always",
+ "selector-list-comma-newline-after": "always",
+ "selector-list-comma-newline-before": "never-multi-line",
+ "selector-list-comma-space-after": "always-single-line",
+ "selector-list-comma-space-before": "never-single-line",
+ "selector-max-attribute": 2,
+ "selector-max-class": 6,
+ "selector-max-combinators": 8,
+ "selector-max-compound-selectors": 9,
+ "selector-max-empty-lines": 1,
+ "selector-max-id": 1,
+ "selector-max-specificity": null,
+ "selector-max-type": 5,
+ "selector-max-universal": 1,
+ "selector-no-qualifying-type": null,
+ "selector-no-vendor-prefix": null,
+ "selector-type-no-unknown": [true, {
+ "ignoreTypes": [
+ "/^md-/",
+ "/^mdp-/",
+ "/^ng-/",
+ "/^tb-/",
+ "/^v-pane/"
+ ]
+ }],
+ "string-quotes": "double",
+ "value-keyword-case": "lower",
+ "value-list-comma-newline-after": "always-multi-line",
+ "value-list-comma-newline-before": "never-multi-line",
+ "value-list-comma-space-after": "always-single-line",
+ "value-no-vendor-prefix": null
+ }
+}
ui/package.json 12(+9 -3)
diff --git a/ui/package.json b/ui/package.json
index 1b8ceda..498de06 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -100,6 +100,7 @@
"copy-webpack-plugin": "^3.0.1",
"cross-env": "^3.2.4",
"css-loader": "^0.25.0",
+ "directory-tree": "^2.1.0",
"eslint": "^3.4.0",
"eslint-config-angular": "^0.5.0",
"eslint-loader": "^1.5.0",
@@ -112,6 +113,7 @@
"html-minifier-loader": "^1.3.4",
"html-webpack-plugin": "^2.30.1",
"img-loader": "^1.3.1",
+ "jsonminify": "^0.4.1",
"less": "^2.7.1",
"less-loader": "^2.2.3",
"ng-annotate-loader": "^0.1.1",
@@ -122,14 +124,18 @@
"react-hot-loader": "^3.0.0-beta.6",
"sass-loader": "^4.0.2",
"style-loader": "^0.13.1",
+ "stylelint": "^9.5.0",
+ "stylelint-config-recommended-scss": "^3.2.0",
+ "stylelint-config-standard": "^18.2.0",
+ "stylelint-order": "^1.0.0",
+ "stylelint-scss": "^3.3.0",
+ "stylelint-webpack-plugin": "^0.10.5",
"url-loader": "^0.5.7",
"webpack": "^1.13.2",
"webpack-dev-middleware": "^1.6.1",
"webpack-dev-server": "^1.15.1",
"webpack-hot-middleware": "^2.12.2",
- "webpack-material-design-icons": "^0.1.0",
- "directory-tree": "^2.1.0",
- "jsonminify": "^0.4.1"
+ "webpack-material-design-icons": "^0.1.0"
},
"engine": "node >= 5.9.0",
"nyc": {
ui/src/app/alarm/alarm.scss 23(+12 -11)
diff --git a/ui/src/app/alarm/alarm.scss b/ui/src/app/alarm/alarm.scss
index faf7feb..2f6f459 100644
--- a/ui/src/app/alarm/alarm.scss
+++ b/ui/src/app/alarm/alarm.scss
@@ -13,26 +13,27 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-alarm-container {
overflow-x: auto;
}
md-list.tb-alarm-table {
- padding: 0px;
min-width: 700px;
+ padding: 0;
md-list-item {
- padding: 0px;
+ padding: 0;
}
.tb-row {
height: 48px;
- padding: 0px;
+ padding: 0;
overflow: hidden;
}
.tb-row:hover {
- background-color: #EEEEEE;
+ background-color: #eee;
}
.tb-header:hover {
@@ -41,9 +42,9 @@ md-list.tb-alarm-table {
.tb-header {
.tb-cell {
- color: rgba(0,0,0,.54);
font-size: 12px;
font-weight: 700;
+ color: rgba(0, 0, 0, .54);
white-space: nowrap;
background: none;
}
@@ -52,11 +53,12 @@ md-list.tb-alarm-table {
.tb-cell {
padding: 0 24px;
margin: auto 0;
- color: rgba(0,0,0,.87);
+ overflow: hidden;
font-size: 13px;
- vertical-align: middle;
+ color: rgba(0, 0, 0, .87);
text-align: left;
- overflow: hidden;
+ vertical-align: middle;
+
.md-button {
padding: 0;
margin: 0;
@@ -66,12 +68,11 @@ md-list.tb-alarm-table {
.tb-cell.tb-number {
text-align: right;
}
-
}
#tb-alarm-content {
- min-width: 400px;
- min-height: 50px;
width: 100%;
+ min-width: 400px;
height: 100%;
+ min-height: 50px;
}
diff --git a/ui/src/app/alarm/alarm-details-dialog.scss b/ui/src/app/alarm/alarm-details-dialog.scss
index b7d3cd0..ddae1f1 100644
--- a/ui/src/app/alarm/alarm-details-dialog.scss
+++ b/ui/src/app/alarm/alarm-details-dialog.scss
@@ -13,14 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-alarm-details-panel {
- margin-left: 15px;
- border: 1px solid #C0C0C0;
height: 100%;
+ margin-left: 15px;
+ border: 1px solid #c0c0c0;
+
#tb-alarm-details {
- min-width: 600px;
- min-height: 200px;
width: 100%;
+ min-width: 600px;
height: 100%;
+ min-height: 200px;
}
}
ui/src/app/audit/audit-log.scss 27(+15 -12)
diff --git a/ui/src/app/audit/audit-log.scss b/ui/src/app/audit/audit-log.scss
index 7cae107..671cdc2 100644
--- a/ui/src/app/audit/audit-log.scss
+++ b/ui/src/app/audit/audit-log.scss
@@ -13,17 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-audit-logs {
background-color: #fff;
+
.tb-audit-log-margin-18px {
margin-bottom: 18px;
}
+
.tb-audit-log-toolbar {
font-size: 20px;
}
+
md-input-container.tb-audit-log-search-input {
.md-errors-spacer {
- min-height: 0px;
+ min-height: 0;
}
}
}
@@ -32,27 +36,26 @@
overflow-x: auto;
}
-
-
md-list.tb-audit-log-table {
- padding: 0px;
min-width: 700px;
+ padding: 0;
+
&.tb-audit-log-table-full {
min-width: 900px;
}
md-list-item {
- padding: 0px;
+ padding: 0;
}
.tb-row {
height: 48px;
- padding: 0px;
+ padding: 0;
overflow: hidden;
}
.tb-row:hover {
- background-color: #EEEEEE;
+ background-color: #eee;
}
.tb-header:hover {
@@ -61,9 +64,9 @@ md-list.tb-audit-log-table {
.tb-header {
.tb-cell {
- color: rgba(0,0,0,.54);
font-size: 12px;
font-weight: 700;
+ color: rgba(0, 0, 0, .54);
white-space: nowrap;
background: none;
}
@@ -72,11 +75,12 @@ md-list.tb-audit-log-table {
.tb-cell {
padding: 0 24px;
margin: auto 0;
- color: rgba(0,0,0,.87);
+ overflow: hidden;
font-size: 13px;
- vertical-align: middle;
+ color: rgba(0, 0, 0, .87);
text-align: left;
- overflow: hidden;
+ vertical-align: middle;
+
.md-button {
padding: 0;
margin: 0;
@@ -86,5 +90,4 @@ md-list.tb-audit-log-table {
.tb-cell.tb-number {
text-align: right;
}
-
}
diff --git a/ui/src/app/audit/audit-log-details-dialog.scss b/ui/src/app/audit/audit-log-details-dialog.scss
index 1a1507c..e15fafe 100644
--- a/ui/src/app/audit/audit-log-details-dialog.scss
+++ b/ui/src/app/audit/audit-log-details-dialog.scss
@@ -13,10 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#tb-audit-log-action-data, #tb-audit-log-failure-details {
- min-width: 400px;
- min-height: 50px;
+
+#tb-audit-log-action-data,
+#tb-audit-log-failure-details {
width: 100%;
+ min-width: 400px;
height: 100%;
- border: 1px solid #C0C0C0;
-}
\ No newline at end of file
+ min-height: 50px;
+ border: 1px solid #c0c0c0;
+}
ui/src/app/components/dashboard.scss 36(+20 -16)
diff --git a/ui/src/app/components/dashboard.scss b/ui/src/app/components/dashboard.scss
index ca99fcc..e0a9afd 100644
--- a/ui/src/app/components/dashboard.scss
+++ b/ui/src/app/components/dashboard.scss
@@ -21,6 +21,7 @@ div.tb-widget {
margin: 0;
overflow: hidden;
outline: none;
+
@include transition(all .2s ease-in-out);
.tb-widget-title {
@@ -32,7 +33,7 @@ div.tb-widget {
tb-timewindow {
font-size: 14px;
- opacity: 0.85;
+ opacity: .85;
}
}
@@ -44,17 +45,19 @@ div.tb-widget {
margin: 0;
.md-button.md-icon-button {
- margin: 0 !important;
- padding: 0 !important;
- line-height: 20px;
width: 32px;
- height: 32px;
min-width: 32px;
+ height: 32px;
min-height: 32px;
- md-icon, ng-md-icon {
+ padding: 0 !important;
+ margin: 0 !important;
+ line-height: 20px;
+
+ md-icon,
+ ng-md-icon {
width: 20px;
- height: 20px;
min-width: 20px;
+ height: 20px;
min-height: 20px;
font-size: 20px;
}
@@ -63,8 +66,8 @@ div.tb-widget {
.tb-widget-content {
tb-widget {
- width: 100%;
position: relative;
+ width: 100%;
}
}
}
@@ -75,40 +78,41 @@ div.tb-widget.tb-highlighted {
}
div.tb-widget.tb-not-highlighted {
- opacity: 0.5;
+ opacity: .5;
}
tb-dashboard {
position: absolute;
top: 0;
- left: 0;
right: 0;
bottom: 0;
+ left: 0;
}
md-content.tb-dashboard-content {
position: absolute;
top: 0;
- left: 0;
right: 0;
bottom: 0;
- outline: none;
+ left: 0;
background: none;
+ outline: none;
+
.gridster-item {
- @include transition(none);
+ @include transition(none);
}
}
.tb-widget-error-container {
position: absolute;
- background-color: #fff;
width: 100%;
height: 100%;
+ background-color: #fff;
}
.tb-widget-error-msg {
- color: red;
+ padding: 5px;
font-size: 16px;
+ color: #f00;
word-wrap: break-word;
- padding: 5px;
}
diff --git a/ui/src/app/components/dashboard-autocomplete.scss b/ui/src/app/components/dashboard-autocomplete.scss
index 9541fe1..8727fa6 100644
--- a/ui/src/app/components/dashboard-autocomplete.scss
+++ b/ui/src/app/components/dashboard-autocomplete.scss
@@ -13,16 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-dashboard-autocomplete {
.tb-not-found {
display: block;
- line-height: 1.5;
height: 48px;
+ line-height: 1.5;
}
+
.tb-dashboard-item {
display: block;
height: 48px;
}
+
li {
height: auto !important;
white-space: normal !important;
ui/src/app/components/dashboard-select.scss 30(+18 -12)
diff --git a/ui/src/app/components/dashboard-select.scss b/ui/src/app/components/dashboard-select.scss
index 1dee53d..9ca36b6 100644
--- a/ui/src/app/components/dashboard-select.scss
+++ b/ui/src/app/components/dashboard-select.scss
@@ -13,18 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@import '../../scss/constants';
+@import "../../scss/constants";
tb-dashboard-select {
min-width: 52px;
+
md-select {
- pointer-events: all;
max-width: 300px;
+ pointer-events: all;
}
}
.tb-dashboard-select {
min-height: 32px;
+
span {
pointer-events: all;
cursor: pointer;
@@ -38,23 +40,27 @@ tb-dashboard-select {
}
.tb-dashboard-select-panel {
+ min-width: 300px;
+ max-width: 320px;
max-height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+ background: #fff;
+ border-radius: 4px;
+ box-shadow:
+ 0 7px 8px -4px rgba(0, 0, 0, .2),
+ 0 13px 19px 2px rgba(0, 0, 0, .14),
+ 0 5px 24px 4px rgba(0, 0, 0, .12);
+
@media (min-height: 350px) {
max-height: 250px;
}
- max-width: 320px;
+
@media (min-width: $layout-breakpoint-xs) {
max-width: 100%;
}
- min-width: 300px;
- background: white;
- border-radius: 4px;
- box-shadow: 0 7px 8px -4px rgba(0, 0, 0, 0.2),
- 0 13px 19px 2px rgba(0, 0, 0, 0.14),
- 0 5px 24px 4px rgba(0, 0, 0, 0.12);
- overflow-x: hidden;
- overflow-y: auto;
+
md-content {
background-color: #fff;
}
-}
\ No newline at end of file
+}
diff --git a/ui/src/app/components/datakey-config.scss b/ui/src/app/components/datakey-config.scss
index 0de8b63..646a18a 100644
--- a/ui/src/app/components/datakey-config.scss
+++ b/ui/src/app/components/datakey-config.scss
@@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-datakey-config {
min-width: 500px !important;
min-height: 500px !important;
+
md-content {
background-color: #fff;
}
ui/src/app/components/datasource.scss 41(+23 -18)
diff --git a/ui/src/app/components/datasource.scss b/ui/src/app/components/datasource.scss
index bb5f827..d8096bd 100644
--- a/ui/src/app/components/datasource.scss
+++ b/ui/src/app/components/datasource.scss
@@ -13,39 +13,43 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-datasource {
- #entity-autocomplete {
- height: 30px;
- margin-top: 18px;
- md-autocomplete-wrap {
- height: 30px;
- }
- input, input:not(.md-input) {
- height: 30px;
- }
- }
- #datasourceType {
+ #entity-autocomplete {
+ height: 30px;
+ margin-top: 18px;
+
+ md-autocomplete-wrap {
+ height: 30px;
+ }
+
+ input,
+ input:not(.md-input) {
+ height: 30px;
+ }
}
}
@mixin tb-checkered-bg() {
background-color: #fff;
- background-image: linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%, #ddd),
- linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%, #ddd);
- background-size: 8px 8px;
+ background-image:
+ linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%, #ddd),
+ linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%, #ddd);
background-position: 0 0, 4px 4px;
+ background-size: 8px 8px;
}
.tb-color-preview {
- content: '';
- min-width: 24px;
+ position: relative;
width: 24px;
+ min-width: 24px;
height: 24px;
+ overflow: hidden;
+ content: "";
border: 2px solid #fff;
border-radius: 50%;
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);
- position: relative;
- overflow: hidden;
+
@include tb-checkered-bg();
.tb-color-result {
@@ -60,6 +64,7 @@
text-overflow: ellipsis;
white-space: nowrap;
}
+
.tb-chip-separator {
white-space: pre;
}
ui/src/app/components/datasource-entity.scss 16(+11 -5)
diff --git a/ui/src/app/components/datasource-entity.scss b/ui/src/app/components/datasource-entity.scss
index f054cab..681fed1 100644
--- a/ui/src/app/components/datasource-entity.scss
+++ b/ui/src/app/components/datasource-entity.scss
@@ -13,17 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@import '../../scss/constants';
+@import "../../scss/constants";
-.tb-entity-alias-autocomplete, .tb-timeseries-datakey-autocomplete, .tb-attribute-datakey-autocomplete, .tb-alarm-datakey-autocomplete {
+.tb-entity-alias-autocomplete,
+.tb-timeseries-datakey-autocomplete,
+.tb-attribute-datakey-autocomplete,
+.tb-alarm-datakey-autocomplete {
.tb-not-found {
display: block;
- line-height: 1.5;
height: 48px;
+ line-height: 1.5;
+
.tb-no-entries {
line-height: 48px;
}
}
+
li {
height: auto !important;
white-space: normal !important;
@@ -32,13 +37,14 @@
tb-datasource-entity {
@media (min-width: $layout-breakpoint-sm) {
- padding-left: 4px;
padding-right: 4px;
+ padding-left: 4px;
}
+
tb-entity-alias-select {
@media (min-width: $layout-breakpoint-sm) {
width: 200px;
max-width: 200px;
}
}
-}
\ No newline at end of file
+}
diff --git a/ui/src/app/components/datasource-func.scss b/ui/src/app/components/datasource-func.scss
index 4fc4281..ce4ca2b 100644
--- a/ui/src/app/components/datasource-func.scss
+++ b/ui/src/app/components/datasource-func.scss
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@import '../../scss/constants';
+@import "../../scss/constants";
.tb-datasource-func {
@media (min-width: $layout-breakpoint-sm) {
@@ -22,19 +22,22 @@
md-input-container.tb-datasource-name {
.md-errors-spacer {
- display: none;
+ display: none;
}
}
- .tb-func-datakey-autocomplete, .tb-alarm-datakey-autocomplete {
+ .tb-func-datakey-autocomplete,
+ .tb-alarm-datakey-autocomplete {
.tb-not-found {
display: block;
- line-height: 1.5;
height: 48px;
+ line-height: 1.5;
+
.tb-no-entries {
line-height: 48px;
}
}
+
li {
height: auto !important;
white-space: normal !important;
diff --git a/ui/src/app/components/datetime-period.scss b/ui/src/app/components/datetime-period.scss
index 61009ad..8a690e4 100644
--- a/ui/src/app/components/datetime-period.scss
+++ b/ui/src/app/components/datetime-period.scss
@@ -13,13 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
tb-datetime-period {
md-input-container {
- margin-bottom: 0px;
+ margin-bottom: 0;
+
.md-errors-spacer {
- min-height: 0px;
+ min-height: 0;
}
}
+
mdp-date-picker {
.md-input {
width: 150px !important;
ui/src/app/components/details-sidenav.scss 34(+19 -15)
diff --git a/ui/src/app/components/details-sidenav.scss b/ui/src/app/components/details-sidenav.scss
index c7e9919..0873cfd 100644
--- a/ui/src/app/components/details-sidenav.scss
+++ b/ui/src/app/components/details-sidenav.scss
@@ -13,47 +13,51 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@import '../../scss/constants';
+@import "../../scss/constants";
.tb-details-title {
- font-size: 1.000rem;
- @media (min-width: $layout-breakpoint-gt-sm) {
- font-size: 1.600rem;
- }
- font-weight: 400;
- text-transform: uppercase;
+ width: inherit;
margin: 20px 8px 0 0;
overflow: hidden;
+ font-size: 1rem;
+ font-weight: 400;
text-overflow: ellipsis;
+ text-transform: uppercase;
white-space: nowrap;
- width: inherit;
+
+ @media (min-width: $layout-breakpoint-gt-sm) {
+ font-size: 1.6rem;
+ }
}
.tb-details-subtitle {
- font-size: 1.000rem;
+ width: inherit;
margin: 10px 0;
- opacity: 0.8;
overflow: hidden;
+ font-size: 1rem;
text-overflow: ellipsis;
white-space: nowrap;
- width: inherit;
+ opacity: .8;
}
md-sidenav.tb-sidenav-details {
.md-toolbar-tools {
- min-height: 100px;
- max-height: 120px;
- height: 100%;
+ height: 100%;
+ min-height: 100px;
+ max-height: 120px;
}
+ z-index: 59 !important;
width: 100% !important;
max-width: 100% !important;
- z-index: 59 !important;
+
@media (min-width: $layout-breakpoint-gt-sm) {
width: 80% !important;
}
+
@media (min-width: $layout-breakpoint-gt-md) {
width: 65% !important;
}
+
tb-dashboard {
md-content {
background-color: $primary-hue-3;
diff --git a/ui/src/app/components/entity-alias-select.scss b/ui/src/app/components/entity-alias-select.scss
index d143933..d7f6278 100644
--- a/ui/src/app/components/entity-alias-select.scss
+++ b/ui/src/app/components/entity-alias-select.scss
@@ -13,15 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-entity-alias-autocomplete {
.tb-not-found {
display: block;
- line-height: 1.5;
height: 48px;
+ line-height: 1.5;
+
.tb-no-entries {
line-height: 48px;
}
}
+
li {
height: auto !important;
white-space: normal !important;
ui/src/app/components/expand-fullscreen.scss 29(+15 -14)
diff --git a/ui/src/app/components/expand-fullscreen.scss b/ui/src/app/components/expand-fullscreen.scss
index eef599c..ae7e8b8 100644
--- a/ui/src/app/components/expand-fullscreen.scss
+++ b/ui/src/app/components/expand-fullscreen.scss
@@ -16,37 +16,38 @@
@import "../../scss/constants";
.tb-fullscreen {
- section.header-buttons {
- top: 25px;
- }
-}
-
-.tb-fullscreen {
- width: 100% !important;
- height: 100% !important;
position: fixed !important;
top: 0;
left: 0;
+ width: 100% !important;
+ height: 100% !important;
+
+ section.header-buttons {
+ top: 25px;
+ }
}
.tb-fullscreen-parent {
- background-color: $gray;
- width: 100%;
- height: 100%;
position: fixed;
top: 0;
left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: $gray;
}
-.md-button.tb-fullscreen-button-style, .tb-fullscreen-button-style {
+.md-button.tb-fullscreen-button-style,
+.tb-fullscreen-button-style {
background: #ccc;
- opacity: 0.85;
+ opacity: .85;
+
ng-md-icon {
color: #666;
}
}
-.md-button.tb-fullscreen-button-pos, .tb-fullscreen-button-pos {
+.md-button.tb-fullscreen-button-pos,
+.tb-fullscreen-button-pos {
position: absolute;
top: 10px;
right: 10px;
ui/src/app/components/grid.scss 13(+9 -4)
diff --git a/ui/src/app/components/grid.scss b/ui/src/app/components/grid.scss
index 17054d5..4313848 100644
--- a/ui/src/app/components/grid.scss
+++ b/ui/src/app/components/grid.scss
@@ -21,15 +21,19 @@
.tb-card-item {
@include transition(all .2s ease-in-out);
+
md-card-content {
- padding-top: 0px;
max-height: 53px;
+ padding-top: 0;
}
+
md-card-title {
width: 100%;
+
md-card-title-text {
- max-height: 32px;
min-width: 50%;
+ max-height: 32px;
+
.md-headline {
overflow: hidden;
text-overflow: ellipsis;
@@ -40,14 +44,15 @@
}
.tb-current-item {
- opacity: 0.5;
+ opacity: .5;
+
@include transform(scale(1.05));
}
#tb-vertical-container {
position: absolute;
top: 0;
- left: 0;
right: 0;
bottom: 0;
+ left: 0;
}
ui/src/app/components/js-func.scss 23(+14 -9)
diff --git a/ui/src/app/components/js-func.scss b/ui/src/app/components/js-func.scss
index ade2830..affdc5b 100644
--- a/ui/src/app/components/js-func.scss
+++ b/ui/src/app/components/js-func.scss
@@ -13,11 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
tb-js-func {
position: relative;
+
.tb-disabled {
- color: rgba(0,0,0,0.38);
+ color: rgba(0, 0, 0, .38);
}
+
.fill-height {
height: 100%;
}
@@ -25,25 +28,27 @@ tb-js-func {
.tb-js-func-toolbar {
.md-button.tidy {
- color: #7B7B7B;
min-width: 32px;
min-height: 15px;
- line-height: 15px;
- font-size: 0.800rem;
- margin: 0 5px 0 0;
padding: 4px;
- background: rgba(220, 220, 220, 0.35);
+ margin: 0 5px 0 0;
+ font-size: .8rem;
+ line-height: 15px;
+ color: #7b7b7b;
+ background: rgba(220, 220, 220, .35);
}
}
.tb-js-func-panel {
- margin-left: 15px;
- border: 1px solid #C0C0C0;
height: calc(100% - 80px);
+ margin-left: 15px;
+ border: 1px solid #c0c0c0;
+
#tb-javascript-input {
- min-width: 200px;
width: 100%;
+ min-width: 200px;
height: 100%;
+
&:not(.fill-height) {
min-height: 200px;
}
ui/src/app/components/json-content.scss 20(+12 -8)
diff --git a/ui/src/app/components/json-content.scss b/ui/src/app/components/json-content.scss
index 287c7e3..4f245af 100644
--- a/ui/src/app/components/json-content.scss
+++ b/ui/src/app/components/json-content.scss
@@ -13,8 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
tb-json-content {
position: relative;
+
.fill-height {
height: 100%;
}
@@ -22,25 +24,27 @@ tb-json-content {
.tb-json-content-toolbar {
.md-button.tidy {
- color: #7B7B7B;
min-width: 32px;
min-height: 15px;
- line-height: 15px;
- font-size: 0.800rem;
- margin: 0 5px 0 0;
padding: 4px;
- background: rgba(220, 220, 220, 0.35);
+ margin: 0 5px 0 0;
+ font-size: .8rem;
+ line-height: 15px;
+ color: #7b7b7b;
+ background: rgba(220, 220, 220, .35);
}
}
.tb-json-content-panel {
- margin-left: 15px;
- border: 1px solid #C0C0C0;
height: 100%;
+ margin-left: 15px;
+ border: 1px solid #c0c0c0;
+
#tb-json-input {
- min-width: 200px;
width: 100%;
+ min-width: 200px;
height: 100%;
+
&:not(.fill-height) {
min-height: 200px;
}
ui/src/app/components/json-form.scss 5(+3 -2)
diff --git a/ui/src/app/components/json-form.scss b/ui/src/app/components/json-form.scss
index 66a5c1a..f102d4c 100644
--- a/ui/src/app/components/json-form.scss
+++ b/ui/src/app/components/json-form.scss
@@ -13,7 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
tb-json-form {
- overflow: auto;
padding-bottom: 14px !important;
-}
\ No newline at end of file
+ overflow: auto;
+}
diff --git a/ui/src/app/components/json-object-edit.scss b/ui/src/app/components/json-object-edit.scss
index 232d69a..a2bdb6a 100644
--- a/ui/src/app/components/json-object-edit.scss
+++ b/ui/src/app/components/json-object-edit.scss
@@ -13,21 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
tb-json-object-edit {
position: relative;
+
.fill-height {
height: 100%;
}
}
.tb-json-object-panel {
- margin-left: 15px;
- border: 1px solid #C0C0C0;
height: 100%;
+ margin-left: 15px;
+ border: 1px solid #c0c0c0;
+
#tb-json-input {
- min-width: 200px;
width: 100%;
+ min-width: 200px;
height: 100%;
+
&:not(.fill-height) {
min-height: 200px;
}
ui/src/app/components/kv-map.scss 8(+5 -3)
diff --git a/ui/src/app/components/kv-map.scss b/ui/src/app/components/kv-map.scss
index d51653c..35267d4 100644
--- a/ui/src/app/components/kv-map.scss
+++ b/ui/src/app/components/kv-map.scss
@@ -13,14 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-kv-map {
span.no-data-found {
position: relative;
+ display: flex;
height: 40px;
text-transform: uppercase;
- display: flex;
+
&.disabled {
- color: rgba(0,0,0,0.38);
+ color: rgba(0, 0, 0, .38);
}
}
-}
\ No newline at end of file
+}
ui/src/app/components/legend.scss 26(+18 -8)
diff --git a/ui/src/app/components/legend.scss b/ui/src/app/components/legend.scss
index 257a77f..e5e3f04 100644
--- a/ui/src/app/components/legend.scss
+++ b/ui/src/app/components/legend.scss
@@ -13,39 +13,49 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
table.tb-legend {
width: 100%;
font-size: 12px;
- .tb-legend-header, .tb-legend-value {
- text-align: right;
+
+ .tb-legend-header,
+ .tb-legend-value {
+ text-align: right;
}
+
.tb-legend-header {
th {
- color: rgb(255,110,64);
- white-space: nowrap;
padding: 0 10px 1px 0;
+ color: rgb(255, 110, 64);
+ white-space: nowrap;
}
}
+
.tb-legend-keys {
- td.tb-legend-label, td.tb-legend-value {
- white-space: nowrap;
+ td.tb-legend-label,
+ td.tb-legend-value {
padding: 2px 10px;
+ white-space: nowrap;
}
+
.tb-legend-line {
+ display: inline-block;
width: 15px;
height: 3px;
- display: inline-block;
vertical-align: middle;
}
+
.tb-legend-label {
text-align: left;
outline: none;
+
&.tb-horizontal {
width: 95%;
}
+
&.tb-hidden-label {
text-decoration: line-through;
- opacity: 0.6;
+ opacity: .6;
}
}
}
ui/src/app/components/legend-config.scss 22(+14 -8)
diff --git a/ui/src/app/components/legend-config.scss b/ui/src/app/components/legend-config.scss
index fb10928..205d67d 100644
--- a/ui/src/app/components/legend-config.scss
+++ b/ui/src/app/components/legend-config.scss
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.md-panel {
&.tb-legend-config-panel {
position: absolute;
@@ -20,21 +21,26 @@
}
.tb-legend-config-panel {
- max-height: 220px;
min-width: 220px;
- background: white;
- border-radius: 4px;
- box-shadow: 0 7px 8px -4px rgba(0, 0, 0, 0.2),
- 0 13px 19px 2px rgba(0, 0, 0, 0.14),
- 0 5px 24px 4px rgba(0, 0, 0, 0.12);
+ max-height: 220px;
overflow: hidden;
- form, fieldset {
+ background: #fff;
+ border-radius: 4px;
+ box-shadow:
+ 0 7px 8px -4px rgba(0, 0, 0, .2),
+ 0 13px 19px 2px rgba(0, 0, 0, .14),
+ 0 5px 24px 4px rgba(0, 0, 0, .12);
+
+ form,
+ fieldset {
height: 100%;
}
+
md-content {
- background-color: #fff;
overflow: hidden;
+ background-color: #fff;
}
+
.md-padding {
padding: 0 16px;
}
diff --git a/ui/src/app/components/material-icons-dialog.scss b/ui/src/app/components/material-icons-dialog.scss
index 3048fd1..e50caff 100644
--- a/ui/src/app/components/material-icons-dialog.scss
+++ b/ui/src/app/components/material-icons-dialog.scss
@@ -13,18 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-material-icons-dialog {
button.md-icon-button.tb-select-icon-button {
- border: solid 1px orange;
- border-radius: 0%;
- padding: 16px;
- height: 56px;
width: 56px;
+ height: 56px;
+ padding: 16px;
margin: 10px;
+ border: solid 1px #ffa500;
+ border-radius: 0%;
}
+
.tb-icons-load {
top: 64px;
- background: rgba(255,255,255,0.75);
z-index: 3;
+ background: rgba(255, 255, 255, .75);
}
-}
\ No newline at end of file
+}
ui/src/app/components/material-icon-select.scss 22(+12 -10)
diff --git a/ui/src/app/components/material-icon-select.scss b/ui/src/app/components/material-icon-select.scss
index e12e1d0..2814bb3 100644
--- a/ui/src/app/components/material-icon-select.scss
+++ b/ui/src/app/components/material-icon-select.scss
@@ -13,14 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-material-icon-select {
- md-icon {
- padding: 4px;
- margin: 8px 4px 4px;
- cursor: pointer;
- border: solid 1px rgba(0,0,0,0.27);
- }
- md-input-container {
- margin-bottom: 0px;
- }
-}
\ No newline at end of file
+ md-icon {
+ padding: 4px;
+ margin: 8px 4px 4px;
+ cursor: pointer;
+ border: solid 1px rgba(0, 0, 0, .27);
+ }
+
+ md-input-container {
+ margin-bottom: 0;
+ }
+}
ui/src/app/components/menu-link.scss 6(+4 -2)
diff --git a/ui/src/app/components/menu-link.scss b/ui/src/app/components/menu-link.scss
index b21c0f4..299797c 100644
--- a/ui/src/app/components/menu-link.scss
+++ b/ui/src/app/components/menu-link.scss
@@ -24,9 +24,11 @@
}
.tb-menu-toggle-list {
- overflow: hidden;
position: relative;
z-index: 1;
- @include transition(0.75s cubic-bezier(0.35, 0, 0.25, 1));
+ overflow: hidden;
+
+ @include transition(.75s cubic-bezier(.35, 0, .25, 1));
+
@include transition-property(height);
}
ui/src/app/components/react/json-form.scss 121(+63 -58)
diff --git a/ui/src/app/components/react/json-form.scss b/ui/src/app/components/react/json-form.scss
index f84fb85..cca9d07 100644
--- a/ui/src/app/components/react/json-form.scss
+++ b/ui/src/app/components/react/json-form.scss
@@ -15,87 +15,92 @@
*/
@import "~compass-sass-mixins/lib/compass";
-$swift-ease-out-duration: 0.4s !default;
-$swift-ease-out-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1) !default;
+$swift-ease-out-duration: .4s !default;
+$swift-ease-out-timing-function: cubic-bezier(.25, .8, .25, 1) !default;
$input-label-float-offset: 6px !default;
-$input-label-float-scale: 0.75 !default;
+$input-label-float-scale: .75 !default;
.json-form-error {
- position: relative;
- bottom: -5px;
- font-size: 12px;
- line-height: 12px;
- color: rgb(244, 67, 54);
- @include transition(all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms);
+ position: relative;
+ bottom: -5px;
+ font-size: 12px;
+ line-height: 12px;
+ color: rgb(244, 67, 54);
+
+ @include transition(all 450ms cubic-bezier(.23, 1, .32, 1) 0ms);
}
.tb-container {
- position: relative;
- margin-top: 32px;
- padding: 10px 0;
+ position: relative;
+ padding: 10px 0;
+ margin-top: 32px;
}
.tb-field {
- &.tb-required {
- label:after {
- content: ' *';
- font-size: 13px;
- vertical-align: top;
- color: rgba(0,0,0,0.54);
- }
+ &.tb-required {
+ label::after {
+ font-size: 13px;
+ color: rgba(0, 0, 0, .54);
+ vertical-align: top;
+ content: " *";
}
- &.tb-focused:not(.tb-readonly) {
- label:after {
- color: rgb(221,44,0);
- }
+ }
+
+ &.tb-focused:not(.tb-readonly) {
+ label::after {
+ color: rgb(221, 44, 0);
}
+ }
}
.tb-date-field {
- &.tb-required {
- div>div:first-child:after {
- content: ' *';
- font-size: 13px;
- vertical-align: top;
- color: rgba(0,0,0,0.54);
- }
+ &.tb-required {
+ div > div:first-child::after {
+ font-size: 13px;
+ color: rgba(0, 0, 0, .54);
+ vertical-align: top;
+ content: " *";
}
- &.tb-focused:not(.tb-readonly) {
- div>div:first-child:after {
- color: rgb(221,44,0);
- }
+ }
+
+ &.tb-focused:not(.tb-readonly) {
+ div > div:first-child::after {
+ color: rgb(221, 44, 0);
}
+ }
}
label.tb-label {
- color: rgba(0,0,0,0.54);
- -webkit-font-smoothing: antialiased;
- position: absolute;
- bottom: 100%;
- left: 0;
- right: auto;
- @include transform(translate3d(0, $input-label-float-offset, 0) scale($input-label-float-scale));
- @include transition(transform $swift-ease-out-timing-function $swift-ease-out-duration,
- width $swift-ease-out-timing-function $swift-ease-out-duration);
- transform-origin: left top;
-
- &.tb-focused {
- color: rgb(96,125,139);
- }
+ position: absolute;
+ right: auto;
+ bottom: 100%;
+ left: 0;
+ color: rgba(0, 0, 0, .54);
+ transform-origin: left top;
+ -webkit-font-smoothing: antialiased;
- &.tb-required:after {
- content: ' *';
- font-size: 13px;
- vertical-align: top;
- color: rgba(0,0,0,0.54);
- }
+ @include transform(translate3d(0, $input-label-float-offset, 0) scale($input-label-float-scale));
- &.tb-focused:not(.tb-readonly):after {
- color: rgb(221,44,0);
- }
+ @include transition(transform $swift-ease-out-timing-function $swift-ease-out-duration,
+ width $swift-ease-out-timing-function $swift-ease-out-duration);
+
+ &.tb-focused {
+ color: rgb(96, 125, 139);
+ }
+
+ &.tb-required::after {
+ font-size: 13px;
+ color: rgba(0, 0, 0, .54);
+ vertical-align: top;
+ content: " *";
+ }
+
+ &.tb-focused:not(.tb-readonly)::after {
+ color: rgb(221, 44, 0);
+ }
}
.tb-head-label {
- color: rgba(0,0,0,0.54);
+ color: rgba(0, 0, 0, .54);
}
diff --git a/ui/src/app/components/react/json-form-ace-editor.scss b/ui/src/app/components/react/json-form-ace-editor.scss
index 42ab8c5..c08eba1 100644
--- a/ui/src/app/components/react/json-form-ace-editor.scss
+++ b/ui/src/app/components/react/json-form-ace-editor.scss
@@ -13,30 +13,35 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.json-form-ace-editor {
- position: relative;
- border: 1px solid #C0C0C0;
- height: 100%;
- .title-panel {
- position: absolute;
- font-size: 0.800rem;
- font-weight: 500;
- top: 10px;
- right: 20px;
- z-index: 5;
- label {
- color: #00acc1;
- background: rgba(220, 220, 220, 0.35);
- border-radius: 5px;
- padding: 4px;
- text-transform: uppercase;
- }
- button.tidy-button {
- background: rgba(220, 220, 220, 0.35) !important;
- span {
- padding: 0px !important;
- font-size: 12px !important;
- }
- }
+ position: relative;
+ height: 100%;
+ border: 1px solid #c0c0c0;
+
+ .title-panel {
+ position: absolute;
+ top: 10px;
+ right: 20px;
+ z-index: 5;
+ font-size: .8rem;
+ font-weight: 500;
+
+ label {
+ padding: 4px;
+ color: #00acc1;
+ text-transform: uppercase;
+ background: rgba(220, 220, 220, .35);
+ border-radius: 5px;
}
-}
\ No newline at end of file
+
+ button.tidy-button {
+ background: rgba(220, 220, 220, .35) !important;
+
+ span {
+ padding: 0 !important;
+ font-size: 12px !important;
+ }
+ }
+ }
+}
diff --git a/ui/src/app/components/react/json-form-color.scss b/ui/src/app/components/react/json-form-color.scss
index ee55ec3..7bc0826 100644
--- a/ui/src/app/components/react/json-form-color.scss
+++ b/ui/src/app/components/react/json-form-color.scss
@@ -15,21 +15,23 @@
*/
@mixin tb-checkered-bg() {
background-color: #fff;
- background-image: linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%, #ddd),
- linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%, #ddd);
- background-size: 8px 8px;
+ background-image:
+ linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%, #ddd),
+ linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%, #ddd);
background-position: 0 0, 4px 4px;
+ background-size: 8px 8px;
}
.tb-color-preview {
- content: '';
+ position: relative;
width: 24px;
height: 24px;
+ overflow: hidden;
+ content: "";
border: 2px solid #fff;
border-radius: 50%;
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);
- position: relative;
- overflow: hidden;
+
@include tb-checkered-bg();
.tb-color-result {
ui/src/app/components/react/json-form-image.scss 91(+47 -44)
diff --git a/ui/src/app/components/react/json-form-image.scss b/ui/src/app/components/react/json-form-image.scss
index 8872b09..e602f52 100644
--- a/ui/src/app/components/react/json-form-image.scss
+++ b/ui/src/app/components/react/json-form-image.scss
@@ -13,66 +13,69 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-$previewSize: 100px;
+$previewSize: 100px !default;
.tb-image-select-container {
position: relative;
- height: $previewSize;
width: 100%;
+ height: $previewSize;
}
.tb-image-preview {
- max-width: $previewSize;
- max-height: $previewSize;
- width: 100%;
- height: 100%;
+ width: 100%;
+ max-width: $previewSize;
+ height: 100%;
+ max-height: $previewSize;
}
.tb-image-preview-container {
- position: relative;
- width: $previewSize;
- height: $previewSize;
- margin-right: 12px;
- border: solid 1px;
- vertical-align: top;
- float: left;
- div {
- width: 100%;
- font-size: 18px;
- text-align: center;
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%,-50%);
- }
+ position: relative;
+ float: left;
+ width: $previewSize;
+ height: $previewSize;
+ margin-right: 12px;
+ vertical-align: top;
+ border: solid 1px;
+
+ div {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 100%;
+ font-size: 18px;
+ text-align: center;
+ transform: translate(-50%, -50%);
+ }
}
.tb-dropzone {
- position: relative;
- border: dashed 2px;
- height: $previewSize;
- vertical-align: top;
- padding: 0 8px;
- overflow: hidden;
- div {
- width: 100%;
- font-size: 24px;
- text-align: center;
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%,-50%);
- }
+ position: relative;
+ height: $previewSize;
+ padding: 0 8px;
+ overflow: hidden;
+ vertical-align: top;
+ border: dashed 2px;
+
+ div {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 100%;
+ font-size: 24px;
+ text-align: center;
+ transform: translate(-50%, -50%);
+ }
}
.tb-image-clear-container {
- width: 48px;
- height: $previewSize;
- position: relative;
- float: right;
+ position: relative;
+ float: right;
+ width: 48px;
+ height: $previewSize;
}
+
.tb-image-clear-btn {
- position: absolute !important;
- top: 50%;
- transform: translate(0%,-50%) !important;
+ position: absolute !important;
+ top: 50%;
+ transform: translate(0%, -50%) !important;
}
diff --git a/ui/src/app/components/related-entity-autocomplete.scss b/ui/src/app/components/related-entity-autocomplete.scss
index 8d4683a..74f652a 100644
--- a/ui/src/app/components/related-entity-autocomplete.scss
+++ b/ui/src/app/components/related-entity-autocomplete.scss
@@ -13,16 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-related-entity-autocomplete {
.tb-not-found {
display: block;
- line-height: 1.5;
height: 48px;
+ line-height: 1.5;
}
+
.tb-entity-item {
display: block;
height: 48px;
}
+
li {
height: auto !important;
white-space: normal !important;
ui/src/app/components/side-menu.scss 39(+21 -18)
diff --git a/ui/src/app/components/side-menu.scss b/ui/src/app/components/side-menu.scss
index 469c575..46da8db 100644
--- a/ui/src/app/components/side-menu.scss
+++ b/ui/src/app/components/side-menu.scss
@@ -16,21 +16,22 @@
@import "~compass-sass-mixins/lib/compass";
.tb-side-menu .md-button.tb-active {
- background-color: rgba(255, 255, 255, 0.15);
font-weight: 500;
+ background-color: rgba(255, 255, 255, .15);
}
-.tb-side-menu, .tb-side-menu ul {
- list-style: none;
+.tb-side-menu,
+.tb-side-menu ul {
padding: 0;
margin-top: 0;
+ list-style: none;
}
.tb-side-menu .tb-menu-toggle-list a.md-button {
padding: 0 16px 0 32px;
+ font-weight: 500;
text-transform: none;
text-rendering: optimizeLegibility;
- font-weight: 500;
}
.tb-side-menu .tb-menu-toggle-list .md-button {
@@ -43,36 +44,38 @@
}
.tb-side-menu > li {
- border-bottom: 1px solid rgba(0, 0, 0, 0.12);
+ border-bottom: 1px solid rgba(0, 0, 0, .12);
}
.tb-side-menu .md-button-toggle .md-toggle-icon {
- background-size: 100% auto;
display: inline-block;
- margin: auto 0 auto auto;
width: 15px;
+ margin: auto 0 auto auto;
+ background-size: 100% auto;
+
@include transition(transform .3s, ease-in-out);
}
.tb-side-menu .md-button {
display: flex;
- border-radius: 0;
- color: inherit;
- cursor: pointer;
- line-height: 40px;
- margin: 0;
+ width: 100%;
max-height: 40px;
+ padding: 0 16px;
+ margin: 0;
overflow: hidden;
- padding: 0px 16px;
+ line-height: 40px;
+ color: inherit;
text-align: left;
text-decoration: none;
- white-space: nowrap;
text-overflow: ellipsis;
- width: 100%;
+ white-space: nowrap;
+ cursor: pointer;
+ border-radius: 0;
+
span {
overflow: hidden;
- white-space: nowrap;
text-overflow: ellipsis;
+ white-space: nowrap;
}
}
@@ -83,10 +86,10 @@
.tb-side-menu ng-md-icon {
margin-right: 8px;
- margin-left: 0px;
+ margin-left: 0;
}
.tb-side-menu md-icon {
margin-right: 8px;
- margin-left: 0px;
+ margin-left: 0;
}
ui/src/app/components/timeinterval.scss 11(+9 -2)
diff --git a/ui/src/app/components/timeinterval.scss b/ui/src/app/components/timeinterval.scss
index 4bae01d..c5c0683 100644
--- a/ui/src/app/components/timeinterval.scss
+++ b/ui/src/app/components/timeinterval.scss
@@ -13,25 +13,32 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
tb-timeinterval {
min-width: 355px;
+
md-input-container {
- margin-bottom: 0px;
+ margin-bottom: 0;
+
.md-errors-spacer {
- min-height: 0px;
+ min-height: 0;
}
}
+
mdp-date-picker {
.md-input {
width: 150px;
}
}
+
.md-input {
width: 70px !important;
}
+
.advanced-switch {
margin-top: 0;
}
+
.advanced-label {
margin: 5px 0;
}
ui/src/app/components/timewindow.scss 37(+25 -12)
diff --git a/ui/src/app/components/timewindow.scss b/ui/src/app/components/timewindow.scss
index 01ad5f9..8d4ed89 100644
--- a/ui/src/app/components/timewindow.scss
+++ b/ui/src/app/components/timewindow.scss
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.md-panel {
&.tb-timewindow-panel {
position: absolute;
@@ -20,38 +21,48 @@
}
.tb-timewindow-panel {
- max-height: 440px;
min-width: 417px;
- background: white;
- border-radius: 4px;
- box-shadow: 0 7px 8px -4px rgba(0, 0, 0, 0.2),
- 0 13px 19px 2px rgba(0, 0, 0, 0.14),
- 0 5px 24px 4px rgba(0, 0, 0, 0.12);
+ max-height: 440px;
overflow: hidden;
- form, fieldset {
+ background: #fff;
+ border-radius: 4px;
+ box-shadow:
+ 0 7px 8px -4px rgba(0, 0, 0, .2),
+ 0 13px 19px 2px rgba(0, 0, 0, .14),
+ 0 5px 24px 4px rgba(0, 0, 0, .12);
+
+ form,
+ fieldset {
height: 100%;
}
+
md-content {
- background-color: #fff;
overflow: hidden;
+ background-color: #fff;
}
+
.md-padding {
padding: 0 16px;
}
+
.md-radio-interactive {
- md-select, md-switch {
+ md-select,
+ md-switch {
pointer-events: all;
}
}
+
md-radio-button {
.md-label {
width: 100%;
}
+
tb-timeinterval {
width: 355px;
+
.advanced-switch {
- min-height: 30px;
max-width: 44px;
+ min-height: 30px;
}
}
}
@@ -59,15 +70,17 @@
tb-timewindow {
min-width: 52px;
+
section.tb-timewindow {
min-height: 32px;
padding: 0 6px;
+
span {
- pointer-events: all;
- cursor: pointer;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
+ pointer-events: all;
+ cursor: pointer;
}
}
}
diff --git a/ui/src/app/components/widget/action/manage-widget-actions.scss b/ui/src/app/components/widget/action/manage-widget-actions.scss
index 91d587b..69891cc 100644
--- a/ui/src/app/components/widget/action/manage-widget-actions.scss
+++ b/ui/src/app/components/widget/action/manage-widget-actions.scss
@@ -13,18 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-manage-widget-actions {
table.md-table {
tbody {
tr {
td {
&.tb-action-cell {
+ width: 100px;
+ min-width: 100px;
+ max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
- min-width: 100px;
- max-width: 100px;
- width: 100px;
}
}
}
ui/src/app/components/widget/widget.scss 11(+7 -4)
diff --git a/ui/src/app/components/widget/widget.scss b/ui/src/app/components/widget/widget.scss
index 347417b..3d5a9b4 100644
--- a/ui/src/app/components/widget/widget.scss
+++ b/ui/src/app/components/widget/widget.scss
@@ -13,18 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-widget {
.tb-widget-error {
display: flex;
- justify-content: center;
align-items: center;
- background: rgba(255,255,255,0.5);
+ justify-content: center;
+ background: rgba(255, 255, 255, .5);
+
span {
- color: red;
+ color: #f00;
}
}
+
.tb-widget-loading {
- background: rgba(255,255,255,0.15);
z-index: 3;
+ background: rgba(255, 255, 255, .15);
}
}
diff --git a/ui/src/app/components/widget/widget-config.scss b/ui/src/app/components/widget/widget-config.scss
index 7ff2051..4ac8561 100644
--- a/ui/src/app/components/widget/widget-config.scss
+++ b/ui/src/app/components/widget/widget-config.scss
@@ -13,11 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-widget-config {
md-tab-content.md-active > div {
height: 100%;
}
+
.tb-advanced-widget-config {
height: 100%;
}
-}
\ No newline at end of file
+}
diff --git a/ui/src/app/components/widgets-bundle-select.scss b/ui/src/app/components/widgets-bundle-select.scss
index 41c4ee7..f5d1f22 100644
--- a/ui/src/app/components/widgets-bundle-select.scss
+++ b/ui/src/app/components/widgets-bundle-select.scss
@@ -17,9 +17,10 @@
tb-widgets-bundle-select {
md-select {
- margin: 0;
padding: 5px 20px;
+ margin: 0;
}
+
.tb-bundle-item {
height: 24px;
line-height: 24px;
@@ -33,24 +34,29 @@ tb-widgets-bundle-select {
}
}
-tb-widgets-bundle-select, .tb-widgets-bundle-select {
+tb-widgets-bundle-select,
+.tb-widgets-bundle-select {
.md-text {
display: block;
width: 100%;
}
+
.tb-bundle-item {
display: inline-block;
width: 100%;
+
span {
display: inline-block;
vertical-align: middle;
}
+
.tb-bundle-system {
- font-size: 0.8rem;
- opacity: 0.8;
float: right;
+ font-size: .8rem;
+ opacity: .8;
}
}
+
md-option {
height: auto !important;
white-space: normal !important;
@@ -60,19 +66,23 @@ tb-widgets-bundle-select, .tb-widgets-bundle-select {
md-toolbar {
tb-widgets-bundle-select {
md-select {
- background: rgba(255,255,255,0.2);
+ background: rgba(255, 255, 255, .2);
+
.md-select-value {
- color: #fff;
font-size: 1.2rem;
- span:first-child:after {
- color: #fff;
+ color: #fff;
+
+ span:first-child::after {
+ color: #fff;
}
}
+
.md-select-value.md-select-placeholder {
color: #fff;
- opacity: 0.8;
+ opacity: .8;
}
}
+
md-select.ng-invalid.ng-touched {
.md-select-value {
color: #fff !important;
ui/src/app/dashboard/dashboard.scss 79(+46 -33)
diff --git a/ui/src/app/dashboard/dashboard.scss b/ui/src/app/dashboard/dashboard.scss
index 224dcf7..00a0653 100644
--- a/ui/src/app/dashboard/dashboard.scss
+++ b/ui/src/app/dashboard/dashboard.scss
@@ -14,11 +14,12 @@
* limitations under the License.
*/
@import "~compass-sass-mixins/lib/compass";
-@import '../../scss/constants';
-$toolbar-height: 50px;
-$fullscreen-toolbar-height: 64px;
-$mobile-toolbar-height: 84px;
+@import "../../scss/constants";
+
+$toolbar-height: 50px !default;
+$fullscreen-toolbar-height: 64px !default;
+$mobile-toolbar-height: 84px !default;
section.tb-dashboard-title {
position: absolute;
@@ -27,10 +28,10 @@ section.tb-dashboard-title {
}
input.tb-dashboard-title {
- font-size: 2.000rem;
- font-weight: 500;
- letter-spacing: 0.005em;
height: 38px;
+ font-size: 2rem;
+ font-weight: 500;
+ letter-spacing: .005em;
}
div.tb-padded {
@@ -50,9 +51,11 @@ tb-details-sidenav.tb-widget-details-sidenav {
@media (min-width: $layout-breakpoint-sm) {
width: 85% !important;
}
+
@media (min-width: $layout-breakpoint-md) {
width: 75% !important;
}
+
@media (min-width: $layout-breakpoint-lg) {
width: 60% !important;
}
@@ -65,47 +68,55 @@ tb-details-sidenav.tb-widget-details-sidenav {
section.tb-dashboard-toolbar {
position: absolute;
- top: 0px;
- left: 0px;
+ top: 0;
+ left: 0;
z-index: 13;
pointer-events: none;
+
&.tb-dashboard-toolbar-opened {
- right: 0px;
- // @include transition(right .3s cubic-bezier(.55,0,.55,.2));
+ right: 0;
+ // @include transition(right .3s cubic-bezier(.55,0,.55,.2));
}
+
&.tb-dashboard-toolbar-closed {
right: 18px;
+
@include transition(right .3s cubic-bezier(.55,0,.55,.2) .2s);
}
}
.tb-dashboard-container {
- &.tb-dashboard-toolbar-opened {
- &.is-fullscreen {
- margin-top: $mobile-toolbar-height;
- @media (min-width: $layout-breakpoint-sm) {
- margin-top: $fullscreen-toolbar-height;
- }
- }
- &:not(.is-fullscreen) {
- margin-top: $mobile-toolbar-height;
- @media (min-width: $layout-breakpoint-sm) {
- margin-top: $toolbar-height;
- }
- @include transition(margin-top .3s cubic-bezier(.55,0,.55,.2));
- }
- }
- &.tb-dashboard-toolbar-closed {
- margin-top: 0px;
- @include transition(margin-top .3s cubic-bezier(.55,0,.55,.2) .2s);
+ &.tb-dashboard-toolbar-opened {
+ &.is-fullscreen {
+ margin-top: $mobile-toolbar-height;
+
+ @media (min-width: $layout-breakpoint-sm) {
+ margin-top: $fullscreen-toolbar-height;
+ }
+ }
+
+ &:not(.is-fullscreen) {
+ margin-top: $mobile-toolbar-height;
+
+ @media (min-width: $layout-breakpoint-sm) {
+ margin-top: $toolbar-height;
+ }
+
+ @include transition(margin-top .3s cubic-bezier(.55,0,.55,.2));
+ }
+ }
+
+ &.tb-dashboard-toolbar-closed {
+ margin-top: 0;
+
+ @include transition(margin-top .3s cubic-bezier(.55,0,.55,.2) .2s);
}
+
.tb-dashboard-layouts {
md-backdrop {
z-index: 1;
}
- #tb-main-layout {
- }
#tb-right-layout {
md-sidenav {
z-index: 1;
@@ -124,13 +135,15 @@ section.tb-powered-by-footer {
bottom: 5px;
z-index: 3;
pointer-events: none;
+
span {
font-size: 12px;
+
a {
- font-weight: bold;
+ font-weight: 700;
text-decoration: none;
- border: none;
pointer-events: all;
+ border: none;
}
}
}
diff --git a/ui/src/app/dashboard/dashboard-card.scss b/ui/src/app/dashboard/dashboard-card.scss
index 0dac213..8e6626c 100644
--- a/ui/src/app/dashboard/dashboard-card.scss
+++ b/ui/src/app/dashboard/dashboard-card.scss
@@ -13,14 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-dashboard-assigned-customers {
display: block;
display: -webkit-box;
height: 34px;
+ margin-bottom: 4px;
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
- margin-bottom: 4px;
}
ui/src/app/dashboard/dashboard-settings.scss 26(+15 -11)
diff --git a/ui/src/app/dashboard/dashboard-settings.scss b/ui/src/app/dashboard/dashboard-settings.scss
index 37da008..e278240 100644
--- a/ui/src/app/dashboard/dashboard-settings.scss
+++ b/ui/src/app/dashboard/dashboard-settings.scss
@@ -13,50 +13,54 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-$previewSize: 100px;
+$previewSize: 100px !default;
.tb-image-select-container {
position: relative;
- height: $previewSize;
width: 100%;
+ height: $previewSize;
}
.tb-image-preview {
- max-width: $previewSize;
- max-height: $previewSize;
width: auto;
+ max-width: $previewSize;
height: auto;
+ max-height: $previewSize;
}
.tb-image-preview-container {
position: relative;
+ float: left;
width: $previewSize;
height: $previewSize;
margin-right: 12px;
- border: solid 1px;
vertical-align: top;
- float: left;
+ border: solid 1px;
+
div {
width: 100%;
font-size: 18px;
text-align: center;
}
- div, .tb-image-preview {
+
+ div,
+ .tb-image-preview {
position: absolute;
top: 50%;
left: 50%;
- transform: translate(-50%,-50%);
+ transform: translate(-50%, -50%);
}
}
.tb-image-clear-container {
- width: 48px;
- height: $previewSize;
position: relative;
float: right;
+ width: 48px;
+ height: $previewSize;
}
+
.tb-image-clear-btn {
position: absolute !important;
top: 50%;
- transform: translate(0%,-50%) !important;
+ transform: translate(0%, -50%) !important;
}
ui/src/app/dashboard/dashboard-toolbar.scss 81(+56 -25)
diff --git a/ui/src/app/dashboard/dashboard-toolbar.scss b/ui/src/app/dashboard/dashboard-toolbar.scss
index 40c17c4..80fd876 100644
--- a/ui/src/app/dashboard/dashboard-toolbar.scss
+++ b/ui/src/app/dashboard/dashboard-toolbar.scss
@@ -14,13 +14,14 @@
* limitations under the License.
*/
@import "~compass-sass-mixins/lib/compass";
-@import '../../scss/constants';
-$toolbar-height: 50px;
-$fullscreen-toolbar-height: 64px;
-$mobile-toolbar-height: 80px;
-$half-mobile-toolbar-height: 40px;
-$mobile-toolbar-height-total: 84px;
+@import "../../scss/constants";
+
+$toolbar-height: 50px !default;
+$fullscreen-toolbar-height: 64px !default;
+$mobile-toolbar-height: 80px !default;
+$half-mobile-toolbar-height: 40px !default;
+$mobile-toolbar-height-total: 84px !default;
tb-dashboard-toolbar {
md-fab-toolbar {
@@ -29,126 +30,156 @@ tb-dashboard-toolbar {
.md-button {
&.md-fab {
opacity: 1;
+
@include transition(opacity .3s cubic-bezier(.55,0,.55,.2));
+
.md-fab-toolbar-background {
- background-color: $primary-default !important;
+ background-color: $primary-default !important;
}
}
}
}
}
+
md-fab-trigger {
.md-button {
&.md-fab {
- line-height: 36px;
width: 36px;
height: 36px;
margin: 4px 0 0 4px;
- opacity: 0.5;
+ line-height: 36px;
+ opacity: .5;
+
@include transition(opacity .3s cubic-bezier(.55,0,.55,.2) .2s);
+
md-icon {
position: absolute;
top: 25%;
- margin: 0;
- line-height: 18px;
- height: 18px;
width: 18px;
- min-height: 18px;
min-width: 18px;
+ height: 18px;
+ min-height: 18px;
+ margin: 0;
+ line-height: 18px;
}
}
}
}
+
&.is-fullscreen {
&.md-is-open {
md-fab-trigger {
.md-button {
&.md-fab {
.md-fab-toolbar-background {
- transition-delay: 0ms !important;
- transition-duration: 0ms !important;
+ transition-delay: 0ms !important;
+ transition-duration: 0ms !important;
}
}
}
}
}
+
.md-fab-toolbar-wrapper {
height: $mobile-toolbar-height-total;
+
@media (min-width: $layout-breakpoint-sm) {
height: $fullscreen-toolbar-height;
}
+
md-toolbar {
- min-height: $mobile-toolbar-height;
height: $mobile-toolbar-height;
+ min-height: $mobile-toolbar-height;
+
@media (min-width: $layout-breakpoint-sm) {
- min-height: $fullscreen-toolbar-height;
height: $fullscreen-toolbar-height;
+ min-height: $fullscreen-toolbar-height;
}
}
}
}
+
.md-fab-toolbar-wrapper {
height: $mobile-toolbar-height-total;
+
@media (min-width: $layout-breakpoint-sm) {
height: $toolbar-height;
}
+
md-toolbar {
- min-height: $mobile-toolbar-height;
height: $mobile-toolbar-height;
+ min-height: $mobile-toolbar-height;
+
@media (min-width: $layout-breakpoint-sm) {
- min-height: $toolbar-height;
height: $toolbar-height;
+ min-height: $toolbar-height;
}
+
md-fab-actions {
+ margin-top: 0;
font-size: 16px;
- margin-top: 0px;
+
@media (max-width: $layout-breakpoint-sm) {
height: $mobile-toolbar-height;
max-height: $mobile-toolbar-height;
}
+
.close-action {
margin-right: -18px;
}
+
.md-fab-action-item {
width: 100%;
height: $mobile-toolbar-height;
+
@media (min-width: $layout-breakpoint-sm) {
height: 46px;
}
+
.tb-dashboard-action-panels {
+ flex-direction: column-reverse;
height: $mobile-toolbar-height;
+
@media (min-width: $layout-breakpoint-sm) {
height: 46px;
}
- flex-direction: column-reverse;
+
@media (min-width: $layout-breakpoint-sm) {
flex-direction: row-reverse;
}
+
.tb-dashboard-action-panel {
- min-width: 0px;
+ flex-direction: row-reverse;
+ min-width: 0;
height: $half-mobile-toolbar-height;
+
@media (min-width: $layout-breakpoint-sm) {
height: 46px;
}
- flex-direction: row-reverse;
+
div {
height: $half-mobile-toolbar-height;
+
@media (min-width: $layout-breakpoint-sm) {
height: 46px;
}
}
+
md-select {
pointer-events: all;
}
+
tb-states-component {
pointer-events: all;
}
+
button.md-icon-button {
min-width: 40px;
+
@media (max-width: $layout-breakpoint-sm) {
min-width: 28px;
- margin: 0px;
padding: 2px;
+ margin: 0;
}
}
}
@@ -158,4 +189,4 @@ tb-dashboard-toolbar {
}
}
}
-}
\ No newline at end of file
+}
diff --git a/ui/src/app/dashboard/states/default-state-controller.scss b/ui/src/app/dashboard/states/default-state-controller.scss
index 5d6ae97..0fafd3b 100644
--- a/ui/src/app/dashboard/states/default-state-controller.scss
+++ b/ui/src/app/dashboard/states/default-state-controller.scss
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
md-select.default-state-controller {
- margin: 0px;
-}
\ No newline at end of file
+ margin: 0;
+}
diff --git a/ui/src/app/dashboard/states/entity-state-controller.scss b/ui/src/app/dashboard/states/entity-state-controller.scss
index 4c2d0c3..5756182 100644
--- a/ui/src/app/dashboard/states/entity-state-controller.scss
+++ b/ui/src/app/dashboard/states/entity-state-controller.scss
@@ -13,34 +13,37 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@import '../../../scss/constants';
+@import "../../../scss/constants";
tb-states-component {
- min-width: 0px;
+ min-width: 0;
}
.entity-state-controller {
- .state-divider {
- font-size: 18px;
- padding-left: 15px;
- padding-right: 15px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- pointer-events: none;
- }
- .state-entry {
- font-size: 18px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- outline: none;
- }
- md-select {
- margin: 0px;
- .md-text {
- font-size: 18px;
- font-weight: bold;
- }
+ .state-divider {
+ padding-right: 15px;
+ padding-left: 15px;
+ overflow: hidden;
+ font-size: 18px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ pointer-events: none;
+ }
+
+ .state-entry {
+ overflow: hidden;
+ font-size: 18px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ outline: none;
+ }
+
+ md-select {
+ margin: 0;
+
+ .md-text {
+ font-size: 18px;
+ font-weight: 700;
}
-}
\ No newline at end of file
+ }
+}
diff --git a/ui/src/app/dashboard/states/manage-dashboard-states.scss b/ui/src/app/dashboard/states/manage-dashboard-states.scss
index 2aa1ca2..bf139be 100644
--- a/ui/src/app/dashboard/states/manage-dashboard-states.scss
+++ b/ui/src/app/dashboard/states/manage-dashboard-states.scss
@@ -13,21 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.manage-dashboard-states {
table.md-table {
tbody {
tr {
td {
&.tb-action-cell {
+ width: 100px;
+ min-width: 100px;
+ max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
- min-width: 100px;
- max-width: 100px;
- width: 100px;
}
}
}
}
}
-}
\ No newline at end of file
+}
diff --git a/ui/src/app/entity/alias/aliases-entity-select.scss b/ui/src/app/entity/alias/aliases-entity-select.scss
index 978bb3e..1244437 100644
--- a/ui/src/app/entity/alias/aliases-entity-select.scss
+++ b/ui/src/app/entity/alias/aliases-entity-select.scss
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@import '../../../scss/constants';
+@import "../../../scss/constants";
tb-aliases-entity-select {
min-width: 52px;
@@ -26,18 +26,21 @@ tb-aliases-entity-select {
}
.tb-aliases-entity-select-panel {
+ min-width: 300px;
max-height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+ background: #fff;
+ border-radius: 4px;
+ box-shadow:
+ 0 7px 8px -4px rgba(0, 0, 0, .2),
+ 0 13px 19px 2px rgba(0, 0, 0, .14),
+ 0 5px 24px 4px rgba(0, 0, 0, .12);
+
@media (min-height: 350px) {
max-height: 250px;
}
- min-width: 300px;
- background: white;
- border-radius: 4px;
- box-shadow: 0 7px 8px -4px rgba(0, 0, 0, 0.2),
- 0 13px 19px 2px rgba(0, 0, 0, 0.14),
- 0 5px 24px 4px rgba(0, 0, 0, 0.12);
- overflow-x: hidden;
- overflow-y: auto;
+
md-content {
background-color: #fff;
}
@@ -46,15 +49,17 @@ tb-aliases-entity-select {
section.tb-aliases-entity-select {
min-height: 32px;
padding: 0 6px;
+
@media (max-width: $layout-breakpoint-sm) {
- padding: 0px;
+ padding: 0;
}
+
span {
max-width: 200px;
- pointer-events: all;
- cursor: pointer;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
+ pointer-events: all;
+ cursor: pointer;
}
}
diff --git a/ui/src/app/entity/alias/entity-alias-dialog.scss b/ui/src/app/entity/alias/entity-alias-dialog.scss
index bd4cd51..c72854f 100644
--- a/ui/src/app/entity/alias/entity-alias-dialog.scss
+++ b/ui/src/app/entity/alias/entity-alias-dialog.scss
@@ -13,14 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-entity-alias-dialog {
.tb-resolve-multiple-switch {
padding-left: 10px;
+
.resolve-multiple-switch {
margin: 0;
}
+
.resolve-multiple-label {
margin: 5px 0;
}
}
-}
\ No newline at end of file
+}
ui/src/app/entity/alias/entity-aliases.scss 18(+13 -5)
diff --git a/ui/src/app/entity/alias/entity-aliases.scss b/ui/src/app/entity/alias/entity-aliases.scss
index 4b1fbeb..0f66d89 100644
--- a/ui/src/app/entity/alias/entity-aliases.scss
+++ b/ui/src/app/entity/alias/entity-aliases.scss
@@ -13,35 +13,43 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-aliases-dialog {
.md-dialog-content {
- padding-bottom: 0px;
- padding-top: 0px;
+ padding-top: 0;
+ padding-bottom: 0;
}
+
.tb-aliases-header {
min-height: 40px;
padding: 0 34px 0 34px;
margin: 5px;
+
.tb-header-label {
font-size: 14px;
- color: rgba(0, 0, 0, 0.570588);
+ color: rgba(0, 0, 0, .570588);
}
}
+
.tb-alias {
padding: 0 0 0 10px;
margin: 5px;
+
md-input-container {
- margin: 0px;
+ margin: 0;
}
+
.tb-resolve-multiple-switch {
padding-left: 10px;
+
.resolve-multiple-switch {
margin: 0;
}
}
+
.md-button {
&.md-icon-button {
- margin: 0px;
+ margin: 0;
}
}
}
ui/src/app/entity/attribute/attribute-table.scss 30(+16 -14)
diff --git a/ui/src/app/entity/attribute/attribute-table.scss b/ui/src/app/entity/attribute/attribute-table.scss
index 5d5c0be..ef42f4e 100644
--- a/ui/src/app/entity/attribute/attribute-table.scss
+++ b/ui/src/app/entity/attribute/attribute-table.scss
@@ -13,46 +13,48 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@import '../../../scss/constants';
+@import "../../../scss/constants";
-$md-light: rgba(255, 255, 255, 100%);
-$md-edit-icon-fill: #757575;
+$md-light: rgba(255, 255, 255, 100%) !default;
+$md-edit-icon-fill: #757575 !default;
md-toolbar.md-table-toolbar.alternate {
.md-toolbar-tools {
- md-icon {
- color: $md-light;
- }
+ md-icon {
+ color: $md-light;
+ }
}
}
.md-table {
&.tb-attribute-table {
- table-layout: fixed;
- td.md-cell {
- &.tb-value-cell {
- overflow: auto;
- }
+ table-layout: fixed;
+
+ td.md-cell {
+ &.tb-value-cell {
+ overflow: auto;
}
+ }
}
+
.md-cell {
ng-md-icon {
- fill: $md-edit-icon-fill;
float: right;
height: 16px;
+ fill: $md-edit-icon-fill;
}
}
}
.widgets-carousel {
position: relative;
- margin: 0px;
height: calc(100% - 100px);
min-height: 150px !important;
+ margin: 0;
tb-dashboard {
#gridster-parent {
padding: 0 7px;
}
}
-}
\ No newline at end of file
+}
diff --git a/ui/src/app/entity/entity-autocomplete.scss b/ui/src/app/entity/entity-autocomplete.scss
index dad3d97..6617fcb 100644
--- a/ui/src/app/entity/entity-autocomplete.scss
+++ b/ui/src/app/entity/entity-autocomplete.scss
@@ -13,11 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-entity-autocomplete {
.tb-entity-item {
display: block;
height: 48px;
}
+
li {
height: auto !important;
white-space: normal !important;
ui/src/app/entity/entity-filter.scss 8(+5 -3)
diff --git a/ui/src/app/entity/entity-filter.scss b/ui/src/app/entity/entity-filter.scss
index bbaa108..950ed0a 100644
--- a/ui/src/app/entity/entity-filter.scss
+++ b/ui/src/app/entity/entity-filter.scss
@@ -13,10 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-.tb-entity-filter {
+.tb-entity-filter {
#relationsQueryFilter {
padding-top: 20px;
+
tb-entity-select {
min-height: 92px;
}
@@ -24,12 +25,13 @@
.tb-root-state-entity-switch {
padding-left: 10px;
+
.root-state-entity-switch {
margin: 0;
}
+
.root-state-entity-label {
margin: 5px 0;
}
}
-
-}
\ No newline at end of file
+}
diff --git a/ui/src/app/entity/entity-filter-view.scss b/ui/src/app/entity/entity-filter-view.scss
index 69922c8..e85128a 100644
--- a/ui/src/app/entity/entity-filter-view.scss
+++ b/ui/src/app/entity/entity-filter-view.scss
@@ -13,20 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-entity-filter-view {
.entity-filter-empty {
- color: rgba(221, 44, 0, 0.87);
font-size: 14px;
line-height: 16px;
+ color: rgba(221, 44, 0, .87);
}
+
.entity-filter-type {
font-size: 14px;
line-height: 16px;
- color: rgba(0, 0, 0, 0.570588);
+ color: rgba(0, 0, 0, .570588);
}
+
.entity-filter-value {
font-size: 14px;
line-height: 16px;
- color: rgba(0, 0, 0, 0.570588);
+ color: rgba(0, 0, 0, .570588);
}
-}
\ No newline at end of file
+}
ui/src/app/entity/entity-list.scss 7(+5 -2)
diff --git a/ui/src/app/entity/entity-list.scss b/ui/src/app/entity/entity-list.scss
index 6e1c892..a3c1bc0 100644
--- a/ui/src/app/entity/entity-list.scss
+++ b/ui/src/app/entity/entity-list.scss
@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/*.tb-entity-list {
+
+/*
+.tb-entity-list {
#entity_list_chips {
.md-chips {
padding-bottom: 1px;
@@ -26,4 +28,5 @@
padding-left: 1px;
}
}
-}*/
\ No newline at end of file
+}
+*/
ui/src/app/entity/entity-select.scss 6(+4 -2)
diff --git a/ui/src/app/entity/entity-select.scss b/ui/src/app/entity/entity-select.scss
index ab7bd3d..5d1b043 100644
--- a/ui/src/app/entity/entity-select.scss
+++ b/ui/src/app/entity/entity-select.scss
@@ -13,6 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-.tb-entity-select {
-}
\ No newline at end of file
+/*
+.tb-entity-select {
+}
+*/
diff --git a/ui/src/app/entity/entity-subtype-autocomplete.scss b/ui/src/app/entity/entity-subtype-autocomplete.scss
index c8e3edc..6dc3ec6 100644
--- a/ui/src/app/entity/entity-subtype-autocomplete.scss
+++ b/ui/src/app/entity/entity-subtype-autocomplete.scss
@@ -13,11 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-entity-subtype-autocomplete {
.tb-entity-subtype-item {
display: block;
height: 48px;
}
+
li {
height: auto !important;
white-space: normal !important;
diff --git a/ui/src/app/entity/entity-subtype-list.scss b/ui/src/app/entity/entity-subtype-list.scss
index f024956..001ca3a 100644
--- a/ui/src/app/entity/entity-subtype-list.scss
+++ b/ui/src/app/entity/entity-subtype-list.scss
@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/*.tb-entity-subtype-list {
+
+/*
+.tb-entity-subtype-list {
#entity_subtype_list_chips {
.md-chips {
padding-bottom: 1px;
@@ -26,4 +28,5 @@
padding-left: 1px;
}
}
-}*/
+}
+*/
diff --git a/ui/src/app/entity/entity-subtype-select.scss b/ui/src/app/entity/entity-subtype-select.scss
index 15b3f2a..9b085e2 100644
--- a/ui/src/app/entity/entity-subtype-select.scss
+++ b/ui/src/app/entity/entity-subtype-select.scss
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
md-select.tb-entity-subtype-select {
min-width: 200px;
}
diff --git a/ui/src/app/entity/entity-type-list.scss b/ui/src/app/entity/entity-type-list.scss
index 21a88ee..a270f38 100644
--- a/ui/src/app/entity/entity-type-list.scss
+++ b/ui/src/app/entity/entity-type-list.scss
@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/*.tb-entity-type-list {
+
+/*
+.tb-entity-type-list {
#entity_type_list_chips {
.md-chips {
padding-bottom: 1px;
@@ -26,4 +28,5 @@
padding-left: 1px;
}
}
-}*/
\ No newline at end of file
+}
+*/
diff --git a/ui/src/app/entity/entity-type-select.scss b/ui/src/app/entity/entity-type-select.scss
index 3a60865..15f2d89 100644
--- a/ui/src/app/entity/entity-type-select.scss
+++ b/ui/src/app/entity/entity-type-select.scss
@@ -13,5 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+/*
md-select.tb-entity-type-select {
}
+*/
diff --git a/ui/src/app/entity/relation/relation-dialog.scss b/ui/src/app/entity/relation/relation-dialog.scss
index 1c0bf2e..6dcfdfd 100644
--- a/ui/src/app/entity/relation/relation-dialog.scss
+++ b/ui/src/app/entity/relation/relation-dialog.scss
@@ -13,14 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-relation-additional-info-panel {
- margin-left: 15px;
- border: 1px solid #C0C0C0;
height: 100%;
+ margin-left: 15px;
+ border: 1px solid #c0c0c0;
+
#tb-relation-additional-info {
- min-width: 200px;
- min-height: 200px;
width: 100%;
+ min-width: 200px;
height: 100%;
+ min-height: 200px;
}
}
ui/src/app/entity/relation/relation-filters.scss 29(+18 -11)
diff --git a/ui/src/app/entity/relation/relation-filters.scss b/ui/src/app/entity/relation/relation-filters.scss
index 34254d0..affea5d 100644
--- a/ui/src/app/entity/relation/relation-filters.scss
+++ b/ui/src/app/entity/relation/relation-filters.scss
@@ -13,32 +13,37 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-relation-filters {
.header {
- padding-left: 5px;
padding-right: 5px;
padding-bottom: 5px;
+ padding-left: 5px;
+
.cell {
- padding-left: 5px;
padding-right: 5px;
- color: rgba(0,0,0,.54);
+ padding-left: 5px;
font-size: 12px;
font-weight: 700;
+ color: rgba(0, 0, 0, .54);
white-space: nowrap;
}
}
+
.body {
- padding-left: 5px;
- padding-right: 5px;
max-height: 300px;
- overflow: auto;
+ padding-right: 5px;
padding-bottom: 20px;
+ padding-left: 5px;
+ overflow: auto;
+
.row {
padding-top: 5px;
}
+
.cell {
- padding-left: 5px;
padding-right: 5px;
+ padding-left: 5px;
md-select {
margin: 0 0 24px;
@@ -49,25 +54,27 @@
}
md-chips-wrap {
- padding: 0px;
+ padding: 0;
margin: 0 0 24px;
+
md-autocomplete {
height: 30px;
+
md-autocomplete-wrap {
height: 30px;
}
}
}
+
.md-chips .md-chip-input-container input {
- padding: 2px 2px 2px;
height: 26px;
+ padding: 2px 2px 2px;
line-height: 26px;
}
-
}
.md-button {
margin: 0;
}
}
-}
\ No newline at end of file
+}
diff --git a/ui/src/app/entity/relation/relation-table.scss b/ui/src/app/entity/relation/relation-table.scss
index 2d618fb..873c9d7 100644
--- a/ui/src/app/entity/relation/relation-table.scss
+++ b/ui/src/app/entity/relation/relation-table.scss
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@import '../../../scss/constants';
+@import "../../../scss/constants";
-$md-light: rgba(255, 255, 255, 100%);
+$md-light: rgba(255, 255, 255, 100%) !default;
.tb-relation-table {
md-toolbar.md-table-toolbar.alternate {
diff --git a/ui/src/app/entity/relation/relation-type-autocomplete.scss b/ui/src/app/entity/relation/relation-type-autocomplete.scss
index 2293d79..6a06aa3 100644
--- a/ui/src/app/entity/relation/relation-type-autocomplete.scss
+++ b/ui/src/app/entity/relation/relation-type-autocomplete.scss
@@ -13,11 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-relation-type-autocomplete {
.tb-relation-type-item {
display: block;
height: 48px;
}
+
li {
height: auto !important;
white-space: normal !important;
ui/src/app/event/event.scss 75(+40 -35)
diff --git a/ui/src/app/event/event.scss b/ui/src/app/event/event.scss
index b0fc46f..5a13d1f 100644
--- a/ui/src/app/event/event.scss
+++ b/ui/src/app/event/event.scss
@@ -13,24 +13,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
md-list.tb-event-table {
- padding: 0px;
+ padding: 0;
- md-list-item {
- padding: 0px;
- }
+ md-list-item {
+ padding: 0;
+ }
.tb-row {
height: 48px;
- padding: 0px;
+ padding: 0;
overflow: hidden;
+
.tb-cell {
text-overflow: ellipsis;
+
&.tb-scroll {
- white-space: nowrap;
- overflow-y: hidden;
overflow-x: auto;
+ overflow-y: hidden;
+ white-space: nowrap;
}
+
&.tb-nowrap {
white-space: nowrap;
}
@@ -38,7 +42,7 @@ md-list.tb-event-table {
}
.tb-row:hover {
- background-color: #EEEEEE;
+ background-color: #eee;
}
.tb-header:hover {
@@ -46,44 +50,45 @@ md-list.tb-event-table {
}
.tb-header {
- .tb-cell {
- color: rgba(0,0,0,.54);
- font-size: 12px;
- font-weight: 700;
- background: none;
- white-space: nowrap;
- }
+ .tb-cell {
+ font-size: 12px;
+ font-weight: 700;
+ color: rgba(0, 0, 0, .54);
+ white-space: nowrap;
+ background: none;
+ }
}
.tb-cell {
- &:first-child {
- padding-left: 14px;
- }
- &:last-child {
- padding-right: 14px;
- }
- padding: 0 6px;
- margin: auto 0;
- color: rgba(0,0,0,.87);
- font-size: 13px;
- vertical-align: middle;
- text-align: left;
- overflow: hidden;
- .md-button {
- padding: 0;
- margin: 0;
- }
+ &:first-child {
+ padding-left: 14px;
+ }
+
+ &:last-child {
+ padding-right: 14px;
+ }
+ padding: 0 6px;
+ margin: auto 0;
+ overflow: hidden;
+ font-size: 13px;
+ color: rgba(0, 0, 0, .87);
+ text-align: left;
+ vertical-align: middle;
+
+ .md-button {
+ padding: 0;
+ margin: 0;
+ }
}
.tb-cell.tb-number {
text-align: right;
}
-
}
#tb-event-content {
- min-width: 400px;
- min-height: 50px;
width: 100%;
+ min-width: 400px;
height: 100%;
+ min-height: 50px;
}
diff --git a/ui/src/app/extension/extensions-forms/extension-form.scss b/ui/src/app/extension/extensions-forms/extension-form.scss
index c7fcbf4..2332cf5 100644
--- a/ui/src/app/extension/extensions-forms/extension-form.scss
+++ b/ui/src/app/extension/extensions-forms/extension-form.scss
@@ -13,28 +13,34 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.extension-form {
li > .md-button {
- color: rgba(0, 0, 0, 0.7);
margin: 0;
+ color: rgba(0, 0, 0, .7);
}
+
.vAccordion--default {
- margin-top: 0;
padding-left: 3px;
+ margin-top: 0;
}
+
.tb-container {
- width:100%;
+ width: 100%;
}
+
.dropdown-messages {
.tb-error-message {
padding: 5px 0 0 0;
}
}
+
.dropdown-section {
margin-bottom: 30px;
}
+
v-pane.inner-invalid > v-pane-header {
- border-bottom: 2px solid rgb(221,44,0);
+ border-bottom: 2px solid rgb(221, 44, 0);
}
}
@@ -45,17 +51,19 @@
}
.tb-extension-custom-transformer-panel {
- margin-left: 15px;
- border: 1px solid #C0C0C0;
height: 100%;
+ margin-left: 15px;
+ border: 1px solid #c0c0c0;
+
.tb-extension-custom-transformer {
- min-width: 600px;
- min-height: 200px;
width: 100%;
+ min-width: 600px;
height: 100%;
+ min-height: 200px;
}
+
.ace_text-input {
- position:absolute!important
+ position: absolute !important;
}
}
@@ -68,9 +76,9 @@
}
.tb-drop-file-input-hide {
- height: 200%;
- display: block;
position: absolute;
bottom: 0;
+ display: block;
width: 100%;
-}
\ No newline at end of file
+ height: 200%;
+}
ui/src/app/extension/extension-table.scss 21(+13 -8)
diff --git a/ui/src/app/extension/extension-table.scss b/ui/src/app/extension/extension-table.scss
index c8946df..b809622 100644
--- a/ui/src/app/extension/extension-table.scss
+++ b/ui/src/app/extension/extension-table.scss
@@ -13,23 +13,26 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@import '../../scss/constants';
+@import "../../scss/constants";
.extension-table {
-
md-input-container .md-errors-spacer {
min-height: 0;
}
- /*&.tb-data-table table.md-table tbody tr td.tb-action-cell,
+ /*
+ &.tb-data-table table.md-table tbody tr td.tb-action-cell,
&.tb-data-table table.md-table.md-row-select tbody tr td.tb-action-cell {
width: 114px;
- }*/
+ }
+ */
+
.sync-widget {
max-height: 90px;
overflow: hidden;
}
+
.toolbar-widget {
min-height: 39px;
max-height: 39px;
@@ -37,11 +40,13 @@
}
.extension__syncStatus--black {
- color: #000000!important;
+ color: #000 !important;
}
+
.extension__syncStatus--green {
- color: #228634!important;
+ color: #228634 !important;
}
+
.extension__syncStatus--red {
- color: #862222!important;
-}
\ No newline at end of file
+ color: #862222 !important;
+}
ui/src/app/help/help.scss 8(+6 -2)
diff --git a/ui/src/app/help/help.scss b/ui/src/app/help/help.scss
index b37dc31..0dd2bc3 100644
--- a/ui/src/app/help/help.scss
+++ b/ui/src/app/help/help.scss
@@ -15,8 +15,12 @@
*/
@import "../../scss/constants";
-.md-button.tb-help-button-style, .tb-help-button-style {
+/*
+.md-button.tb-help-button-style,
+.tb-help-button-style {
}
-.md-button.tb-help-button-pos, .tb-help-button-pos {
+.md-button.tb-help-button-pos,
+.tb-help-button-pos {
}
+*/
ui/src/app/home/home-links.scss 13(+7 -6)
diff --git a/ui/src/app/home/home-links.scss b/ui/src/app/home/home-links.scss
index 8476eb4..677620d 100644
--- a/ui/src/app/home/home-links.scss
+++ b/ui/src/app/home/home-links.scss
@@ -16,10 +16,11 @@
@import "../../scss/constants";
.tb-home-links {
- .md-headline {
- font-size: 20px;
- @media (min-width: $layout-breakpoint-xmd) {
- font-size: 24px;
- }
+ .md-headline {
+ font-size: 20px;
+
+ @media (min-width: $layout-breakpoint-xmd) {
+ font-size: 24px;
}
-}
\ No newline at end of file
+ }
+}
diff --git a/ui/src/app/import-export/import-dialog.scss b/ui/src/app/import-export/import-dialog.scss
index c1561f3..511a910 100644
--- a/ui/src/app/import-export/import-dialog.scss
+++ b/ui/src/app/import-export/import-dialog.scss
@@ -13,29 +13,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-$previewSize: 100px;
+$previewSize: 100px !default;
.tb-file-select-container {
position: relative;
- height: $previewSize;
width: 100%;
+ height: $previewSize;
}
.tb-file-preview {
- max-width: $previewSize;
- max-height: $previewSize;
width: auto;
+ max-width: $previewSize;
height: auto;
+ max-height: $previewSize;
}
.tb-file-clear-container {
- width: 48px;
- height: $previewSize;
position: relative;
float: right;
+ width: 48px;
+ height: $previewSize;
}
+
.tb-file-clear-btn {
position: absolute !important;
top: 50%;
- transform: translate(0%,-50%) !important;
+ transform: translate(0%, -50%) !important;
}
ui/src/app/jsonform/jsonform.scss 6(+4 -2)
diff --git a/ui/src/app/jsonform/jsonform.scss b/ui/src/app/jsonform/jsonform.scss
index 8205fa4..6b1c258 100644
--- a/ui/src/app/jsonform/jsonform.scss
+++ b/ui/src/app/jsonform/jsonform.scss
@@ -13,5 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-.form { height: 400px; }
-.schema { height: 800px; }
\ No newline at end of file
+
+.form { height: 400px; }
+
+.schema { height: 800px; }
ui/src/app/layout/home.scss 29(+19 -10)
diff --git a/ui/src/app/layout/home.scss b/ui/src/app/layout/home.scss
index b1bb554..48c1553 100644
--- a/ui/src/app/layout/home.scss
+++ b/ui/src/app/layout/home.scss
@@ -29,23 +29,31 @@
.tb-breadcrumb {
font-size: 18px !important;
font-weight: 400 !important;
- h1, a, span {
+
+ h1,
+ a,
+ span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
+
a {
border: none;
- opacity: 0.75;
- @include transition(opacity 0.35s);
+ opacity: .75;
+
+ @include transition(opacity .35s);
}
- a:hover, a:focus {
- opacity: 1;
+
+ a:hover,
+ a:focus {
text-decoration: none !important;
border: none;
+ opacity: 1;
}
+
.divider {
- padding: 0px 30px;
+ padding: 0 30px;
}
}
@@ -54,21 +62,22 @@ md-sidenav.tb-site-sidenav {
}
md-icon.tb-logo-title {
- height: 36px;
width: 200px;
+ height: 36px;
}
.tb-nav-header {
- flex-shrink: 0;
z-index: 2;
+ flex-shrink: 0;
white-space: nowrap;
}
.tb-nav-header-toolbar {
- border-bottom: 1px solid rgba(0, 0, 0, 0.12);
- flex-shrink: 0;
z-index: 2;
+ flex-shrink: 0;
white-space: nowrap;
+ border-bottom: 1px solid rgba(0, 0, 0, .12);
+
.md-toolbar-tools {
flex-basis: auto;
}
ui/src/app/layout/user-menu.scss 20(+12 -8)
diff --git a/ui/src/app/layout/user-menu.scss b/ui/src/app/layout/user-menu.scss
index 9512bca..8b684ee 100644
--- a/ui/src/app/layout/user-menu.scss
+++ b/ui/src/app/layout/user-menu.scss
@@ -13,28 +13,32 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
div.tb-user-info {
line-height: 1.5;
+
span {
- text-transform: none;
text-align: left;
+ text-transform: none;
}
+
span.tb-user-display-name {
- font-size: 0.800rem;
+ font-size: .8rem;
font-weight: 300;
- letter-spacing: 0.008em;
+ letter-spacing: .008em;
}
+
span.tb-user-authority {
- font-size: 0.800rem;
+ font-size: .8rem;
font-weight: 300;
- letter-spacing: 0.005em;
- opacity: 0.8;
+ letter-spacing: .005em;
+ opacity: .8;
}
}
md-icon.tb-mini-avatar {
+ width: 36px;
+ height: 36px;
margin: auto 8px;
font-size: 36px;
- height: 36px;
- width: 36px;
}
ui/src/app/login/login.scss 5(+4 -1)
diff --git a/ui/src/app/login/login.scss b/ui/src/app/login/login.scss
index 373a68f..2930ab9 100644
--- a/ui/src/app/login/login.scss
+++ b/ui/src/app/login/login.scss
@@ -13,18 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@import '../../scss/constants';
+@import "../../scss/constants";
md-card.tb-login-card {
width: 330px !important;
+
@media (min-width: $layout-breakpoint-sm) {
width: 450px !important;
}
+
md-card-title {
img.tb-login-logo {
height: 50px;
}
}
+
md-card-content {
margin-top: -50px;
}
ui/src/app/rulechain/link.scss 4(+3 -1)
diff --git a/ui/src/app/rulechain/link.scss b/ui/src/app/rulechain/link.scss
index 3e86619..b37eaf3 100644
--- a/ui/src/app/rulechain/link.scss
+++ b/ui/src/app/rulechain/link.scss
@@ -17,12 +17,14 @@
.tb-link-label-autocomplete {
.tb-not-found {
display: block;
- line-height: 1.5;
height: 48px;
+ line-height: 1.5;
+
.tb-no-entries {
line-height: 48px;
}
}
+
li {
height: auto !important;
white-space: normal !important;
diff --git a/ui/src/app/rulechain/message-type-autocomplete.scss b/ui/src/app/rulechain/message-type-autocomplete.scss
index 4cad9cb..fc702d4 100644
--- a/ui/src/app/rulechain/message-type-autocomplete.scss
+++ b/ui/src/app/rulechain/message-type-autocomplete.scss
@@ -13,11 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-message-type-autocomplete {
.tb-message-type-item {
display: block;
height: 48px;
}
+
li {
height: auto !important;
white-space: normal !important;
ui/src/app/rulechain/rulechain.scss 272(+166 -106)
diff --git a/ui/src/app/rulechain/rulechain.scss b/ui/src/app/rulechain/rulechain.scss
index 5999b7e..8f77300 100644
--- a/ui/src/app/rulechain/rulechain.scss
+++ b/ui/src/app/rulechain/rulechain.scss
@@ -18,88 +18,106 @@
.tb-fullscreen-button-style {
z-index: 1;
}
+
section.tb-header-buttons.tb-library-open {
- pointer-events: none;
position: absolute;
- left: 0px;
- top: 0px;
+ top: 0;
+ left: 0;
z-index: 1;
+ pointer-events: none;
+
.md-button.tb-btn-open-library {
- left: 0px;
- top: 0px;
- line-height: 36px;
+ top: 0;
+ left: 0;
width: 36px;
height: 36px;
margin: 4px 0 0 4px;
- opacity: 0.5;
+ line-height: 36px;
+ opacity: .5;
}
}
+
.tb-rulechain-library {
+ z-index: 1;
width: 250px;
min-width: 250px;
- z-index: 1;
+
md-toolbar {
- min-height: 48px;
height: 48px;
- .md-toolbar-tools>.md-button:last-child {
- margin-right: 0px;
+ min-height: 48px;
+
+ .md-toolbar-tools > .md-button:last-child {
+ margin-right: 0;
}
+
.md-toolbar-tools {
- font-size: 14px;
- padding: 0px 6px;
height: 48px;
+ padding: 0 6px;
+ font-size: 14px;
+
.md-button.md-icon-button {
- margin: 0px;
+ margin: 0;
+
&.tb-small {
+ width: 32px;
height: 32px;
min-height: 32px;
- line-height: 20px;
padding: 6px;
- width: 32px;
+ line-height: 20px;
+
md-icon {
- line-height: 20px;
- font-size: 20px;
- height: 20px;
width: 20px;
- min-height: 20px;
min-width: 20px;
+ height: 20px;
+ min-height: 20px;
+ font-size: 20px;
+ line-height: 20px;
}
}
}
}
}
+
.tb-rulechain-library-panel-group {
- overflow-y: auto;
overflow-x: hidden;
+ overflow-y: auto;
+
.tb-panel-title {
+ min-width: 150px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
- min-width: 150px;
}
+
.fc-canvas {
background: #f9f9f9;
}
+
md-icon.md-expansion-panel-icon {
- margin-right: 0px;
+ margin-right: 0;
}
- md-expansion-panel-collapsed, .md-expansion-panel-header-container {
+
+ md-expansion-panel-collapsed,
+ .md-expansion-panel-header-container {
+ position: static;
background: #e6e6e6;
border-color: #909090;
- position: static;
}
+
md-expansion-panel {
&.md-open {
margin-top: 0;
margin-bottom: 0;
}
}
+
md-expansion-panel-content {
- padding: 0px;
+ padding: 0;
}
}
}
+
.tb-rulechain-graph {
z-index: 0;
overflow: auto;
@@ -107,29 +125,35 @@
}
#tb-rule-chain-context-menu {
- padding-top: 0px;
- border-radius: 8px;
max-height: 404px;
+ padding-top: 0;
+ border-radius: 8px;
+
.tb-context-menu-header {
- padding: 8px 5px 5px;
- font-size: 14px;
display: flex;
flex-direction: row;
height: 36px;
min-height: 36px;
+ padding: 8px 5px 5px;
+ font-size: 14px;
+
&.tb-rulechain {
background-color: #aac7e4;
}
+
&.tb-link {
background-color: #aac7e4;
}
+
md-icon {
- padding-left: 2px;
padding-right: 10px;
+ padding-left: 2px;
}
+
.tb-context-menu-title {
font-weight: 500;
}
+
.tb-context-menu-subtitle {
font-size: 12px;
}
@@ -139,37 +163,45 @@
.fc-canvas {
min-width: 100%;
min-height: 100%;
- outline: none;
- -webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
+ outline: none;
+ -webkit-touch-callout: none;
+
svg {
display: block;
}
}
-.tb-rule-node, #tb-rule-chain-context-menu .tb-context-menu-header {
+.tb-rule-node,
+#tb-rule-chain-context-menu .tb-context-menu-header {
&.tb-filter-type {
background-color: #f1e861;
}
+
&.tb-enrichment-type {
background-color: #cdf14e;
}
+
&.tb-transformation-type {
background-color: #79cef1;
}
+
&.tb-action-type {
background-color: #f1928f;
}
+
&.tb-external-type {
background-color: #fbc766;
}
+
&.tb-rule-chain-type {
background-color: #d6c4f1;
}
+
&.tb-unknown-type {
background-color: #f16c29;
}
@@ -180,102 +212,112 @@
flex-direction: row;
min-width: 150px;
max-width: 150px;
+ height: 32px;
min-height: 32px;
max-height: 32px;
- height: 32px;
padding: 5px 10px;
- border-radius: 5px;
- background-color: #F15B26;
- pointer-events: none;
- color: #333;
- border: solid 1px #777;
font-size: 12px;
line-height: 16px;
+ color: #333;
+ pointer-events: none;
+ background-color: #f15b26;
+ border: solid 1px #777;
+ border-radius: 5px;
+
&.tb-rule-node-highlighted:not(.tb-rule-node-invalid) {
box-shadow: 0 0 10px 6px #51cbee;
+
.tb-node-title {
+ font-weight: 700;
text-decoration: underline;
- font-weight: bold;
}
}
+
&.tb-rule-node-invalid {
box-shadow: 0 0 10px 6px #ff5c50;
}
+
&.tb-input-type {
- background-color: #a3eaa9;
user-select: none;
+ background-color: #a3eaa9;
}
md-icon {
- font-size: 20px;
width: 20px;
+ min-width: 20px;
height: 20px;
min-height: 20px;
- min-width: 20px;
padding-right: 4px;
+ font-size: 20px;
}
- .tb-node-type {
- }
.tb-node-title {
font-weight: 500;
}
- .tb-node-type, .tb-node-title {
+
+ .tb-node-type,
+ .tb-node-title {
overflow: hidden;
- white-space: nowrap;
text-overflow: ellipsis;
+ white-space: nowrap;
}
}
.fc-node {
z-index: 1;
- outline: none;
border-radius: 8px;
+ outline: none;
+
&.fc-dragging {
z-index: 10;
}
+
p {
padding: 0 15px;
text-align: center;
}
+
.fc-node-overlay {
position: absolute;
- pointer-events: none;
- left: 0;
top: 0;
right: 0;
bottom: 0;
+ left: 0;
+ pointer-events: none;
background-color: #000;
- opacity: 0;
border-radius: 5px;
+ opacity: 0;
}
+
&.fc-hover {
.fc-node-overlay {
- opacity: 0.25;
+ opacity: .25;
}
}
+
&.fc-selected {
.fc-node-overlay {
- opacity: 0.25;
+ opacity: .25;
}
- }
- &.fc-selected {
+
&:not(.fc-edit) {
- border: solid 3px red;
margin: -3px;
+ border: solid 3px #f00;
}
}
}
-.fc-leftConnectors, .fc-rightConnectors {
+.fc-leftConnectors,
+.fc-rightConnectors {
position: absolute;
top: 0;
- height: 100%;
+
+ z-index: 0;
display: flex;
flex-direction: column;
+ height: 100%;
- z-index: 0;
.fc-magnet {
align-items: center;
}
@@ -292,18 +334,18 @@
.fc-magnet {
display: flex;
flex-grow: 1;
- height: 60px;
justify-content: center;
+ height: 60px;
}
.fc-connector {
width: 14px;
height: 14px;
- border: 1px solid #333;
margin: 10px;
- border-radius: 5px;
- background-color: #ccc;
pointer-events: all;
+ background-color: #ccc;
+ border: 1px solid #333;
+ border-radius: 5px;
}
.fc-connector.fc-hover {
@@ -312,45 +354,49 @@
.fc-arrow-marker {
polygon {
- stroke: gray;
- fill: gray;
+ fill: #808080;
+ stroke: #808080;
}
}
.fc-arrow-marker-selected {
polygon {
- stroke: red;
- fill: red;
+ fill: #f00;
+ stroke: #f00;
}
}
.fc-edge {
outline: none;
- stroke: gray;
- stroke-width: 4;
- fill: transparent;
transition: stroke-width .2s;
+ fill: transparent;
+ stroke: #808080;
+ stroke-width: 4;
+
&.fc-selected {
- stroke: red;
- stroke-width: 4;
fill: transparent;
+ stroke: #f00;
+ stroke-width: 4;
}
+
&.fc-active {
animation: dash 3s linear infinite;
stroke-dasharray: 20;
}
+
&.fc-hover {
- stroke: gray;
- stroke-width: 6;
fill: transparent;
+ stroke: #808080;
+ stroke-width: 6;
}
+
&.fc-dragging {
pointer-events: none;
}
}
.edge-endpoint {
- fill: gray;
+ fill: #808080;
}
.fc-nodedelete {
@@ -364,22 +410,23 @@
}
.fc-edit {
- .fc-nodedelete, .fc-nodeedit {
- outline: none;
- display: block;
+ .fc-nodedelete,
+ .fc-nodeedit {
position: absolute;
- border: solid 2px white;
- border-radius: 50%;
- font-weight: 600;
- line-height: 20px;
+ display: block;
+ width: 22px;
height: 20px;
padding-top: 2px;
- width: 22px;
- background: #f83e05;
+ font-weight: 600;
+ line-height: 20px;
color: #fff;
text-align: center;
vertical-align: bottom;
cursor: pointer;
+ background: #f83e05;
+ border: solid 2px #fff;
+ border-radius: 50%;
+ outline: none;
}
.fc-nodeedit {
@@ -391,7 +438,6 @@
top: -24px;
right: -13px;
}
-
}
.fc-noselect {
@@ -407,31 +453,37 @@
.fc-edge-label {
position: absolute;
transition: transform .2s;
-// opacity: 0.8;
+ // opacity: 0.8;
+
&.ng-leave {
transition: 0s none;
}
+
&.fc-hover {
transform: scale(1.25);
}
+
&.fc-selected {
.fc-edge-label-text {
span {
- border: solid red;
- color: #fff;
font-weight: 600;
- background-color: red;
+ color: #fff;
+ background-color: #f00;
+ border: solid #f00;
}
}
}
+
.fc-nodeedit {
top: -30px;
right: 14px;
}
+
.fc-nodedelete {
top: -30px;
right: -13px;
}
+
&:focus {
outline: 0;
}
@@ -439,27 +491,28 @@
.fc-edge-label-text {
position: absolute;
- -webkit-transform: translate(-50%, -50%);
- transform: translate(-50%, -50%);
- white-space: nowrap;
- text-align: center;
font-size: 14px;
font-weight: 600;
+ text-align: center;
+ white-space: nowrap;
+ -webkit-transform: translate(-50%, -50%);
+ transform: translate(-50%, -50%);
+
span {
+ padding: 3px 5px;
+ color: #003a79;
cursor: default;
+ background-color: #fff;
border: solid 2px #003a79;
border-radius: 10px;
- color: #003a79;
- background-color: #fff;
- padding: 3px 5px;
}
}
.fc-select-rectangle {
- border: 2px dashed #5262ff;
position: absolute;
- background: rgba(20,125,255,0.1);
z-index: 2;
+ background: rgba(20, 125, 255, .1);
+ border: 2px dashed #5262ff;
}
@keyframes dash {
@@ -468,13 +521,15 @@
}
}
-.tb-rule-node-tooltip, .tb-rule-node-help {
+.tb-rule-node-tooltip,
+.tb-rule-node-help {
color: #333;
}
.tb-rule-node-tooltip {
- font-size: 14px;
max-width: 300px;
+ font-size: 14px;
+
&.tb-lib-tooltip {
width: 300px;
}
@@ -489,28 +544,33 @@
color: #ea0d0d;
}
-.tb-rule-node-tooltip, .tb-rule-node-error-tooltip, .tb-rule-node-help {
+.tb-rule-node-tooltip,
+.tb-rule-node-error-tooltip,
+.tb-rule-node-help {
#tb-node-content {
.tb-node-title {
font-weight: 600;
}
+
.tb-node-description {
font-style: italic;
color: #555;
}
+
.tb-node-details {
padding-top: 10px;
padding-bottom: 10px;
}
+
code {
- padding: 0px 3px 2px 3px;
+ padding: 0 3px 2px 3px;
margin: 1px;
- color: #AD1625;
+ font-size: 12px;
+ color: #ad1625;
white-space: nowrap;
background-color: #f7f7f9;
border: 1px solid #e1e1e8;
border-radius: 2px;
- font-size: 12px;
}
}
-}
\ No newline at end of file
+}
ui/src/app/rulechain/rulenode.scss 6(+3 -3)
diff --git a/ui/src/app/rulechain/rulenode.scss b/ui/src/app/rulechain/rulenode.scss
index 0466673..0bdb3ab 100644
--- a/ui/src/app/rulechain/rulenode.scss
+++ b/ui/src/app/rulechain/rulenode.scss
@@ -16,13 +16,13 @@
.tb-rulenode {
tb-json-object-edit.tb-rule-node-configuration-json {
- height: 300px;
display: block;
+ height: 300px;
}
}
.tb-rulenode-directive-error {
- color: rgb(221,44,0);
font-size: 13px;
font-weight: 400;
-}
\ No newline at end of file
+ color: rgb(221, 44, 0);
+}
diff --git a/ui/src/app/rulechain/script/node-script-test.scss b/ui/src/app/rulechain/script/node-script-test.scss
index a75ae59..1dd174d 100644
--- a/ui/src/app/rulechain/script/node-script-test.scss
+++ b/ui/src/app/rulechain/script/node-script-test.scss
@@ -13,24 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@import '../../../scss/constants';
+@import "../../../scss/constants";
+
@import "~compass-sass-mixins/lib/compass";
md-dialog.tb-node-script-test-dialog {
&.md-dialog-fullscreen {
- min-height: 100%;
+ width: 100%;
min-width: 100%;
- max-height: 100%;
max-width: 100%;
- width: 100%;
height: 100%;
+ min-height: 100%;
+ max-height: 100%;
border-radius: 0;
}
.tb-split {
@include box-sizing(border-box);
- overflow-y: auto;
overflow-x: hidden;
+ overflow-y: auto;
}
.ace_editor {
@@ -38,13 +39,13 @@ md-dialog.tb-node-script-test-dialog {
}
.tb-content {
- border: 1px solid #C0C0C0;
padding-top: 5px;
padding-left: 5px;
+ border: 1px solid #c0c0c0;
}
.gutter {
- background-color: #eeeeee;
+ background-color: #eee;
background-repeat: no-repeat;
background-position: 50%;
@@ -52,21 +53,23 @@ md-dialog.tb-node-script-test-dialog {
.gutter.gutter-horizontal {
cursor: col-resize;
- background-image: url('../../../../node_modules/split.js/grips/vertical.png');
+ background-image: url("../../../../node_modules/split.js/grips/vertical.png");
}
.gutter.gutter-vertical {
cursor: row-resize;
- background-image: url('../../../../node_modules/split.js/grips/horizontal.png');
+ background-image: url("../../../../node_modules/split.js/grips/horizontal.png");
}
- .tb-split.tb-split-horizontal, .gutter.gutter-horizontal {
- height: 100%;
+ .tb-split.tb-split-horizontal,
+ .gutter.gutter-horizontal {
float: left;
+ height: 100%;
}
.tb-split.tb-split-vertical {
display: flex;
+
.tb-split.tb-content {
height: 100%;
}
@@ -74,42 +77,44 @@ md-dialog.tb-node-script-test-dialog {
div.tb-editor-area-title-panel {
position: absolute;
- font-size: 0.800rem;
- font-weight: 500;
top: 13px;
right: 40px;
z-index: 5;
+ font-size: .8rem;
+ font-weight: 500;
+
&.tb-js-function {
right: 80px;
}
+
label {
- color: #00acc1;
- background: rgba(220, 220, 220, 0.35);
- border-radius: 5px;
padding: 4px;
+ color: #00acc1;
text-transform: uppercase;
+ background: rgba(220, 220, 220, .35);
+ border-radius: 5px;
}
+
.md-button {
- color: #7B7B7B;
min-width: 32px;
min-height: 15px;
- line-height: 15px;
- font-size: 0.800rem;
- margin: 0;
padding: 4px;
- background: rgba(220, 220, 220, 0.35);
+ margin: 0;
+ font-size: .8rem;
+ line-height: 15px;
+ color: #7b7b7b;
+ background: rgba(220, 220, 220, .35);
}
}
.tb-resize-container {
- overflow-y: auto;
- height: 100%;
- width: 100%;
position: relative;
+ width: 100%;
+ height: 100%;
+ overflow-y: auto;
.ace_editor {
height: 100%;
}
}
-
}
ui/src/app/services/toast.scss 13(+7 -6)
diff --git a/ui/src/app/services/toast.scss b/ui/src/app/services/toast.scss
index 428e94e..952a42d 100644
--- a/ui/src/app/services/toast.scss
+++ b/ui/src/app/services/toast.scss
@@ -13,20 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
md-toast.tb-info-toast .md-toast-content {
- font-size: 18px;
- padding: 18px;
height: 100%;
+ padding: 18px;
+ font-size: 18px;
}
md-toast.tb-success-toast .md-toast-content {
- font-size: 18px !important;
- background-color: green;
height: 100%;
+ font-size: 18px !important;
+ background-color: #008000;
}
md-toast.tb-error-toast .md-toast-content {
- font-size: 18px !important;
- background-color: maroon;
height: 100%;
+ font-size: 18px !important;
+ background-color: #800000;
}
ui/src/app/user/user-fieldset.scss 6(+4 -2)
diff --git a/ui/src/app/user/user-fieldset.scss b/ui/src/app/user/user-fieldset.scss
index cf21abc..93b8b51 100644
--- a/ui/src/app/user/user-fieldset.scss
+++ b/ui/src/app/user/user-fieldset.scss
@@ -13,18 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@import '../../scss/constants';
+@import "../../scss/constants";
.tb-default-dashboard {
.tb-default-dashboard-label {
padding-bottom: 8px;
}
+
tb-dashboard-autocomplete {
@media (min-width: $layout-breakpoint-sm) {
padding-right: 12px;
}
+
@media (max-width: $layout-breakpoint-sm) {
padding-bottom: 12px;
}
}
-}
\ No newline at end of file
+}
diff --git a/ui/src/app/widget/lib/alarms-table-widget.scss b/ui/src/app/widget/lib/alarms-table-widget.scss
index 6a71ccd..2922996 100644
--- a/ui/src/app/widget/lib/alarms-table-widget.scss
+++ b/ui/src/app/widget/lib/alarms-table-widget.scss
@@ -13,11 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-has-timewindow {
.tb-alarms-table {
md-toolbar {
min-height: 60px;
max-height: 60px;
+
&.md-table-toolbar {
.md-toolbar-tools {
max-height: 60px;
@@ -28,10 +30,10 @@
}
.tb-alarms-table {
-
md-toolbar {
min-height: 39px;
max-height: 39px;
+
&.md-table-toolbar {
.md-toolbar-tools {
max-height: 39px;
@@ -40,14 +42,15 @@
}
&.tb-data-table {
- table.md-table, table.md-table.md-row-select {
+ table.md-table,
+ table.md-table.md-row-select {
tbody {
tr {
td {
&.tb-action-cell {
+ width: 36px;
min-width: 36px;
max-width: 36px;
- width: 36px;
}
}
}
diff --git a/ui/src/app/widget/lib/entities-table-widget.scss b/ui/src/app/widget/lib/entities-table-widget.scss
index 62a1f97..85648d6 100644
--- a/ui/src/app/widget/lib/entities-table-widget.scss
+++ b/ui/src/app/widget/lib/entities-table-widget.scss
@@ -13,11 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
.tb-has-timewindow {
.tb-entities-table {
md-toolbar {
min-height: 60px;
max-height: 60px;
+
&.md-table-toolbar {
.md-toolbar-tools {
max-height: 60px;
@@ -28,10 +30,10 @@
}
.tb-entities-table {
-
md-toolbar {
min-height: 39px;
max-height: 39px;
+
&.md-table-toolbar {
.md-toolbar-tools {
max-height: 39px;
@@ -40,14 +42,15 @@
}
&.tb-data-table {
- table.md-table, table.md-table.md-row-select {
+ table.md-table,
+ table.md-table.md-row-select {
tbody {
tr {
td {
&.tb-action-cell {
+ width: 36px;
min-width: 36px;
max-width: 36px;
- width: 36px;
}
}
}
diff --git a/ui/src/app/widget/lib/extensions-table-widget.scss b/ui/src/app/widget/lib/extensions-table-widget.scss
index f5f228d..42fcf6d 100644
--- a/ui/src/app/widget/lib/extensions-table-widget.scss
+++ b/ui/src/app/widget/lib/extensions-table-widget.scss
@@ -13,16 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
tb-extension-table {
md-content {
background-color: #fff;
}
}
+
md-tabs.hide-tabs-menu {
md-tabs-wrapper {
display: none;
}
+
md-tabs-content-wrapper {
top: 0 !important;
}
-}
\ No newline at end of file
+}
ui/src/app/widget/lib/rpc/knob.scss 141(+78 -63)
diff --git a/ui/src/app/widget/lib/rpc/knob.scss b/ui/src/app/widget/lib/rpc/knob.scss
index c41db43..bf2d3e0 100644
--- a/ui/src/app/widget/lib/rpc/knob.scss
+++ b/ui/src/app/widget/lib/rpc/knob.scss
@@ -13,141 +13,156 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-$knob-img: url('./svg/knob.svg');
+$knob-img: url("./svg/knob.svg") !default;
-$bars-margin-pct: percentage(0.033);
-$background-margin-pct: percentage(0.05);
-$value-container-margin-pct: percentage(0.35);
-$error-height: percentage(0.05);
-$title-height: percentage(0.066);
-$title-container-margin-pct: percentage(0.2);
-$title-container-margin-bottom-pct: percentage(0.05);
-$minmax-height: percentage(0.04);
-$minmax-container-margin-pct: percentage(0.18);
-$minmax-container-margin-bottom-pct: percentage(0.12);
+$bars-margin-pct: percentage(.033) !default;
+$background-margin-pct: percentage(.05) !default;
+$value-container-margin-pct: percentage(.35) !default;
+$error-height: percentage(.05) !default;
+$title-height: percentage(.066) !default;
+$title-container-margin-pct: percentage(.2) !default;
+$title-container-margin-bottom-pct: percentage(.05) !default;
+$minmax-height: percentage(.04) !default;
+$minmax-container-margin-pct: percentage(.18) !default;
+$minmax-container-margin-bottom-pct: percentage(.12) !default;
-$background-color: #e6e7e8;
+$background-color: #e6e7e8 !default;
.tb-knob {
- width:100%;
- height:100%;
+ width: 100%;
+ height: 100%;
background: $background-color;
.knob {
position: relative;
+
&[draggable] {
-moz-user-select: none;
-webkit-user-select: none;
user-select: none;
}
+
#canvasBar {
- position:absolute;
- top:0;
- left:0;
- bottom: 0;
+ position: absolute;
+ top: 0;
right: 0;
+ bottom: 0;
+ left: 0;
z-index: 2;
}
+
.canvas-background {
- position:absolute;
+ position: absolute;
top: $background-margin-pct;
- left: $background-margin-pct;
- bottom: $background-margin-pct;
right: $background-margin-pct;
- border-radius: 50%;
+ bottom: $background-margin-pct;
+ left: $background-margin-pct;
+ z-index: 2;
background: #3f4346;
- z-index:2;
+ border-radius: 50%;
}
+
.value-container {
- position:absolute;
+ position: absolute;
top: $value-container-margin-pct;
- left: $value-container-margin-pct;
- bottom: $value-container-margin-pct;
right: $value-container-margin-pct;
- z-index:4;
+ bottom: $value-container-margin-pct;
+ left: $value-container-margin-pct;
+ z-index: 4;
+
.knob-value {
- color: #fff;
font-weight: 500;
+ color: #fff;
white-space: nowrap;
}
}
+
.error-container {
- position:absolute;
+ position: absolute;
top: 1%;
- left: 0;
right: 0;
- z-index:4;
+ left: 0;
+ z-index: 4;
height: $error-height;
+
.knob-error {
color: #ff3315;
white-space: nowrap;
}
}
+
.title-container {
- position:absolute;
- left: $title-container-margin-pct;
- bottom: $title-container-margin-bottom-pct;
+ position: absolute;
right: $title-container-margin-pct;
- z-index:4;
+ bottom: $title-container-margin-bottom-pct;
+ left: $title-container-margin-pct;
+ z-index: 4;
height: $title-height;
+
.knob-title {
- color: #757575;
font-weight: 500;
+ color: #757575;
white-space: nowrap;
}
}
+
.minmax-container {
- position:absolute;
- left: $minmax-container-margin-pct;
- bottom: $minmax-container-margin-bottom-pct;
+ position: absolute;
right: $minmax-container-margin-pct;
- z-index:4;
+ bottom: $minmax-container-margin-bottom-pct;
+ left: $minmax-container-margin-pct;
+ z-index: 4;
height: $minmax-height;
+
.minmax-label {
- color: #757575;
font-weight: 500;
+ color: #757575;
white-space: nowrap;
}
}
+
.top-pointer-container {
- position:absolute;
+ position: absolute;
top: $bars-margin-pct;
- left: $bars-margin-pct;
- bottom: $bars-margin-pct;
right: $bars-margin-pct;
- z-index:3;
- cursor:pointer !important;
+ bottom: $bars-margin-pct;
+ left: $bars-margin-pct;
+ z-index: 3;
+ cursor: pointer !important;
+
.top-pointer {
- content:'';
- width:5%;
- height:5%;
- background-color:#b5b5b5;
- position:absolute;
- top:50%;
- left:22%;
- margin-top:-2.5%;
+ position: absolute;
+ top: 50%;
+ left: 22%;
+ width: 5%;
+ height: 5%;
+ margin-top: -2.5%;
+ cursor: pointer !important;
+ content: "";
+ background-color: #b5b5b5;
border-radius: 50%;
- cursor:pointer !important;
box-shadow: 1px 0 2px #040404;
}
}
+
.top{
- position:absolute;
+ position: absolute;
top: $bars-margin-pct;
- left: $bars-margin-pct;
- bottom: $bars-margin-pct;
right: $bars-margin-pct;
- background:$knob-img no-repeat;
+ bottom: $bars-margin-pct;
+ left: $bars-margin-pct;
+ z-index: 2;
+ cursor: pointer !important;
+ background: $knob-img no-repeat;
background-size: contain;
- z-index:2;
- cursor:pointer !important;
}
+
#text-measure {
position: absolute;
- visibility: hidden;
- height: auto;
width: auto;
+ height: auto;
white-space: nowrap;
+ visibility: hidden;
}
}
}
ui/src/app/widget/lib/rpc/led-indicator.scss 48(+26 -22)
diff --git a/ui/src/app/widget/lib/rpc/led-indicator.scss b/ui/src/app/widget/lib/rpc/led-indicator.scss
index 9b68c42..b1da5c6 100644
--- a/ui/src/app/widget/lib/rpc/led-indicator.scss
+++ b/ui/src/app/widget/lib/rpc/led-indicator.scss
@@ -15,61 +15,65 @@
*/
@import "~compass-sass-mixins/lib/compass";
-$error-height: 14px;
+$error-height: 14px !default;
-$background-color: #e6e7e8;
+$background-color: #e6e7e8 !default;
.tb-led-indicator {
- width:100%;
- height:100%;
+ width: 100%;
+ height: 100%;
background: $background-color;
.title-container {
.led-title {
- color: #757575;
font-weight: 500;
+ color: #757575;
white-space: nowrap;
}
}
.error-container {
- position:absolute;
+ position: absolute;
top: 1%;
- left: 0;
right: 0;
- z-index:4;
+ left: 0;
+ z-index: 4;
height: $error-height;
+
.led-error {
color: #ff3315;
white-space: nowrap;
}
}
+
#text-measure {
position: absolute;
- visibility: hidden;
- height: auto;
width: auto;
+ height: auto;
white-space: nowrap;
+ visibility: hidden;
}
#led-container {
padding: 10px;
+
.led {
- cursor: pointer;
position: relative;
+ cursor: pointer;
+ background-image: -owg-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, .25));
+ background-image: -webkit-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, .25));
+ background-image: -moz-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, .25));
+ background-image: -o-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, .25));
+ background-image: radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, .25));
border-radius: 50%;
- background-image: -owg-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25));
- background-image: -webkit-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25));
- background-image: -moz-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25));
- background-image: -o-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25));
- background-image: radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25));
- transition: background-color 0.5s, box-shadow 0.5s;
+ transition: background-color .5s, box-shadow .5s;
+
&.disabled {
- background-image: -owg-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1));
- background-image: -webkit-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1));
- background-image: -moz-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1));
- background-image: -o-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1));
- background-image: radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1));
+ background-image: -owg-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, .5), rgba(0, 0, 0, .1));
+ background-image: -webkit-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, .5), rgba(0, 0, 0, .1));
+ background-image: -moz-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, .5), rgba(0, 0, 0, .1));
+ background-image: -o-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, .5), rgba(0, 0, 0, .1));
+ background-image: radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, .5), rgba(0, 0, 0, .1));
}
}
}
ui/src/app/widget/lib/rpc/round-switch.scss 214(+118 -96)
diff --git a/ui/src/app/widget/lib/rpc/round-switch.scss b/ui/src/app/widget/lib/rpc/round-switch.scss
index 9396abc..2e37f63 100644
--- a/ui/src/app/widget/lib/rpc/round-switch.scss
+++ b/ui/src/app/widget/lib/rpc/round-switch.scss
@@ -15,179 +15,201 @@
*/
@import "~compass-sass-mixins/lib/compass";
-$error-height: 14px;
+$error-height: 14px !default;
-$background-color: #e6e7e8;
+$background-color: #e6e7e8 !default;
.tb-round-switch {
- width:100%;
- height:100%;
+ width: 100%;
+ height: 100%;
background: $background-color;
.title-container {
.switch-title {
- color: #757575;
font-weight: 500;
+ color: #757575;
white-space: nowrap;
}
}
.error-container {
- position:absolute;
+ position: absolute;
top: 1%;
- left: 0;
right: 0;
- z-index:4;
+ left: 0;
+ z-index: 4;
height: $error-height;
+
.switch-error {
color: #ff3315;
white-space: nowrap;
}
}
+
#text-measure {
position: absolute;
- visibility: hidden;
- height: auto;
width: auto;
+ height: auto;
white-space: nowrap;
+ visibility: hidden;
}
#switch-container {
padding: 10px;
+
.switch {
- cursor: pointer;
position: relative;
- background:#ddd;
+ width: 260px;
+ min-width: 260px;
+ height: 260px;
+ min-height: 260px;
+ padding: 25px;
+ font-family: sans-serif;
+ font-size: 48px;
+
+ color: #424242;
+ cursor: pointer;
+ -pie-background: -pie-linear-gradient(270deg, #bbb, #ddd);
+ background: #ddd;
background: -owg-linear-gradient(270deg, #bbb, #ddd);
background: -webkit-linear-gradient(270deg, #bbb, #ddd);
background: -moz-linear-gradient(270deg, #bbb, #ddd);
background: -o-linear-gradient(270deg, #bbb, #ddd);
- -pie-background: -pie-linear-gradient(270deg, #bbb, #ddd);
background: linear-gradient(180deg, #bbb, #ddd);
- border-radius:130px;
+ border-radius: 130px;
+
@include box-sizing(border-box);
- @include box-shadow(
- 0px 0px 0px 8px rgba(0,0,0,.1)
- ,0px 0px 3px 1px rgba(0,0,0,.1)
- ,inset 0 8px 3px -8px rgba(255,255,255,.4));
- height: 260px;
- min-height: 260px;
- padding: 25px;
- width: 260px;
- min-width: 260px;
- color: #424242;
- font-family:sans-serif;
- font-size:48px;
+ @include box-shadow(
+ 0 0 0 8px rgba(0,0,0,.1)
+ ,0 0 3px 1px rgba(0,0,0,.1)
+ ,inset 0 8px 3px -8px rgba(255,255,255,.4));
input {
- display:none
+ display: none;
}
- .on,.off {
- position:absolute;
- text-align:center;
+ .on,
+ .off {
+ position: absolute;
+ width: 100%;
+ text-align: center;
+
@include text-shadow(1px 1px 4px #4a4a4a);
- width:100%;
}
.on {
- color:#444;
- top:10px;
- @include transition(all 0.1s);
- font-family:sans-serif
+ top: 10px;
+ font-family: sans-serif;
+ color: #444;
+
+ @include transition(all .1s);
}
.off {
- bottom:5px;
- @include transition(all 0.1s);
- @include transform(scaleY(0.85));
+ bottom: 5px;
+
+ @include transition(all .1s);
+
+ @include transform(scaleY(.85));
}
.but {
+ position: relative;
+ display: block;
+ width: 200px;
+ height: 178px;
+ font-size: 48px;
cursor: pointer;
- background-color:#d8d8d8;
+ background-color: #d8d8d8;
+ border-bottom-width: 0;
border-radius: 400px 400px 400px 400px / 400px 400px 300px 300px;
- border-bottom-width:0px;
+
@include box-shadow(inset 8px 6px 5px -7px #a2a2a2,
- inset -8px 6px 5px -7px #a2a2a2,
- inset 0 -3px 2px -2px rgba(200, 200, 200, 0.5),
- 0 3px 3px -2px #ffffff,
- inset 0 -230px 60px -200px rgba(255, 255, 255, 0.2),
- inset 0 220px 40px -200px rgba(0, 0, 0, 0.3));
- display:block;
- font-size:48px;
- height:178px;
- position:relative;
- @include transition(all 0.2s);
- width:200px;
+ inset -8px 6px 5px -7px #a2a2a2,
+ inset 0 -3px 2px -2px rgba(200, 200, 200, .5),
+ 0 3px 3px -2px #fff,
+ inset 0 -230px 60px -200px rgba(255, 255, 255, .2),
+ inset 0 220px 40px -200px rgba(0, 0, 0, .3));
+
+ @include transition(all .2s);
}
.back {
+ width: 210px;
+ height: 210px;
+ padding: 4px 4px;
cursor: pointer;
background-color: #888787;
- 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%);
- 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%);
- 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%);
- 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%);
- 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%);
- border-radius:105px;
- @include box-shadow(30px 30px 30px -20px rgba(58, 58, 58, 0.3),
- -30px 30px 30px -20px rgba(58, 58, 58, 0.3),
- 0 30px 30px 0px rgba(16, 16, 16, 0.3),
- inset 0 -1px 0 0 #484848);
+ 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%);
+ 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%);
+ 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%);
+ 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%);
+ 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%);
+ border-radius: 105px;
+
+ @include box-shadow(30px 30px 30px -20px rgba(58, 58, 58, .3),
+ -30px 30px 30px -20px rgba(58, 58, 58, .3),
+ 0 30px 30px 0 rgba(16, 16, 16, .3),
+ inset 0 -1px 0 0 #484848);
+
@include box-sizing(border-box);
- height:210px;
- padding:4px 4px;
- @include transition(all 0.2s);
- width:210px;
+
+ @include transition(all .2s);
}
- input:checked + .back .on,input:checked + .back .off{
+ input:checked + .back .on,
+ input:checked + .back .off{
@include text-shadow(1px 1px 4px #4a4a4a);
}
+
input:checked + .back .on{
- color:#4c4c4c;
- top:10px;
- @include transform(scaleY(0.85));
+ top: 10px;
+ color: #4c4c4c;
+
+ @include transform(scaleY(.85));
}
+
input:checked + .back .off{
- color:#444;
- bottom:5px;
+ bottom: 5px;
+ color: #444;
+
@include transform(scaleY(1));
}
+
input:checked + .back .but{
- background:#dcdcdc;
- background-image: -owg-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, 0.3), transparent);
- background-image: -webkit-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, 0.3), transparent);
- background-image: -moz-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, 0.3), transparent);
- background-image: -o-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, 0.3), transparent);
- background-image: radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, 0.3), transparent);
+ margin-top: 20px;
+ background: #dcdcdc;
+ background-image: -owg-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, .3), transparent);
+ background-image: -webkit-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, .3), transparent);
+ background-image: -moz-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, .3), transparent);
+ background-image: -o-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, .3), transparent);
+ background-image: radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, .3), transparent);
border-radius: 400px 400px 400px 400px / 300px 300px 400px 400px;
+
@include box-shadow(inset 8px -4px 5px -7px #a9a9a9,
- inset -8px -4px 5px -7px #808080,
- 0 -3px 8px -4px rgba(50, 50, 50, 0.4),
- inset 0 3px 4px -2px #9c9c9c,
- inset 0 280px 40px -200px rgba(0, 0, 0, 0.2),
- inset 0 -200px 40px -200px rgba(180, 180, 180, 0.2));
- margin-top:20px;
+ inset -8px -4px 5px -7px #808080,
+ 0 -3px 8px -4px rgba(50, 50, 50, .4),
+ inset 0 3px 4px -2px #9c9c9c,
+ inset 0 280px 40px -200px rgba(0, 0, 0, .2),
+ inset 0 -200px 40px -200px rgba(180, 180, 180, .2));
}
- input:checked + .back{
- 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%);
- 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%);
- 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%);
- 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%);
- 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%);
-
- @include box-shadow(30px 30px 30px -20px rgba(49, 49, 49, 0.1),
- -30px 30px 30px -20px rgba(111, 111, 111, 0.1),
- 0 30px 30px 0px rgba(0, 0, 0, 0.2),
- inset 0 1px 2px 0 rgba(167, 167, 167, 0.6));
- padding:2px 4px;
+ input:checked + .back{
+ padding: 2px 4px;
+
+ 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%);
+ 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%);
+ 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%);
+ 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%);
+ 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%);
+
+ @include box-shadow(30px 30px 30px -20px rgba(49, 49, 49, .1),
+ -30px 30px 30px -20px rgba(111, 111, 111, .1),
+ 0 30px 30px 0 rgba(0, 0, 0, .2),
+ inset 0 1px 2px 0 rgba(167, 167, 167, .6));
}
-
}
}
}
ui/src/app/widget/lib/rpc/switch.scss 73(+42 -31)
diff --git a/ui/src/app/widget/lib/rpc/switch.scss b/ui/src/app/widget/lib/rpc/switch.scss
index dc0b8ee..53893a0 100644
--- a/ui/src/app/widget/lib/rpc/switch.scss
+++ b/ui/src/app/widget/lib/rpc/switch.scss
@@ -13,27 +13,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-$thumb-img: url('./svg/thumb.svg');
-$thumb-checked-img: url('./svg/thumb-checked.svg');
-$thumb-bar-img: url('./svg/thumb-bar.svg');
-$thumb-bar-checked-img: url('./svg/thumb-bar-checked.svg');
+$thumb-img: url("./svg/thumb.svg") !default;
+$thumb-checked-img: url("./svg/thumb-checked.svg") !default;
+$thumb-bar-img: url("./svg/thumb-bar.svg") !default;
+$thumb-bar-checked-img: url("./svg/thumb-bar-checked.svg") !default;
-$background-color: #e6e7e8;
+$background-color: #e6e7e8 !default;
-$error-height: 14px;
+$error-height: 14px !default;
.tb-switch {
- width:100%;
- height:100%;
+ width: 100%;
+ height: 100%;
background: $background-color;
.error-container {
- position:absolute;
+ position: absolute;
top: 1%;
- left: 0;
right: 0;
- z-index:4;
+ left: 0;
+ z-index: 4;
height: $error-height;
+
.switch-error {
color: #ff3315;
white-space: nowrap;
@@ -42,62 +43,71 @@ $error-height: 14px;
.onoff-container {
height: 100%;
- color: #757575;
font-weight: 500;
+ color: #757575;
white-space: nowrap;
+
.off-label {
color: #b7b5b5;
}
+
.on-label {
color: #ff7e57;
text-shadow: #ff6e4a 1px 1px 10px, #ffd1c3 1px 1px 10px;
}
}
+
.title-container {
.switch-title {
- color: #757575;
font-weight: 500;
+ color: #757575;
white-space: nowrap;
}
}
#switch-container {
- padding-left: 10px;
- padding-right: 10px;
+ padding-right: 10px;
+ padding-left: 10px;
}
+
.switch {
position: relative;
+
md-switch {
- margin: 0;
- position:absolute;
+ position: absolute;
top: 0;
- left: 0;
- bottom: 0;
right: 0;
+ bottom: 0;
+ left: 0;
+ margin: 0;
+
.md-container {
margin: 0;
}
+
.md-bar {
+ top: 0;
left: 0;
width: 100%;
- top: 0;
height: 100%;
- border-radius: 0;
- background:$thumb-bar-img no-repeat;
+ background: $thumb-bar-img no-repeat;
background-size: contain;
+ border-radius: 0;
}
+
.md-thumb-container {
- left: 0.25%;
- width: 50%;
top: 5%;
+ left: .25%;
+ width: 50%;
height: 90%;
}
+
.md-thumb {
top: 0;
left: 0;
- height: 100%;
width: 100%;
- background:$thumb-img no-repeat;
+ height: 100%;
+ background: $thumb-img no-repeat;
background-size: contain;
border-radius: 0;
box-shadow: none;
@@ -105,22 +115,23 @@ $error-height: 14px;
&.md-checked {
.md-bar {
- background:$thumb-bar-checked-img no-repeat;
+ background: $thumb-bar-checked-img no-repeat;
background-size: contain;
}
+
.md-thumb {
- background:$thumb-checked-img no-repeat;
+ background: $thumb-checked-img no-repeat;
background-size: contain;
}
}
-
}
}
+
#text-measure {
position: absolute;
- visibility: hidden;
- height: auto;
width: auto;
+ height: auto;
white-space: nowrap;
+ visibility: hidden;
}
}
diff --git a/ui/src/app/widget/lib/timeseries-table-widget.scss b/ui/src/app/widget/lib/timeseries-table-widget.scss
index cd8f8a9..12fbf61 100644
--- a/ui/src/app/widget/lib/timeseries-table-widget.scss
+++ b/ui/src/app/widget/lib/timeseries-table-widget.scss
@@ -13,20 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
tb-timeseries-table-widget {
- table.md-table thead.md-head>tr.md-row {
- height: 40px;
- }
+ table.md-table thead.md-head > tr.md-row {
+ height: 40px;
+ }
- table.md-table tbody.md-body>tr.md-row, table.md-table tfoot.md-foot>tr.md-row {
- height: 38px;
- }
+ table.md-table tbody.md-body > tr.md-row,
+ table.md-table tfoot.md-foot > tr.md-row {
+ height: 38px;
+ }
- .md-table-pagination>* {
- height: 46px;
- }
+ .md-table-pagination > * {
+ height: 46px;
+ }
- .tb-data-table md-toolbar {
- z-index: 10;
- }
+ .tb-data-table md-toolbar {
+ z-index: 10;
+ }
}
ui/src/app/widget/widget-editor.scss 60(+32 -28)
diff --git a/ui/src/app/widget/widget-editor.scss b/ui/src/app/widget/widget-editor.scss
index 077f68c..cd374e3 100644
--- a/ui/src/app/widget/widget-editor.scss
+++ b/ui/src/app/widget/widget-editor.scss
@@ -15,13 +15,13 @@
*/
@import "~compass-sass-mixins/lib/compass";
-$edit-toolbar-height: 40px;
+$edit-toolbar-height: 40px !default;
.tb-editor {
.tb-split {
@include box-sizing(border-box);
- overflow-y: auto;
overflow-x: hidden;
+ overflow-y: auto;
}
.ace_editor {
@@ -29,7 +29,7 @@ $edit-toolbar-height: 40px;
}
.tb-content {
- border: 1px solid #C0C0C0;
+ border: 1px solid #c0c0c0;
}
.gutter {
@@ -41,21 +41,23 @@ $edit-toolbar-height: 40px;
.gutter.gutter-horizontal {
cursor: col-resize;
- background-image: url('../../../node_modules/split.js/grips/vertical.png');
+ background-image: url("../../../node_modules/split.js/grips/vertical.png");
}
.gutter.gutter-vertical {
cursor: row-resize;
- background-image: url('../../../node_modules/split.js/grips/horizontal.png');
+ background-image: url("../../../node_modules/split.js/grips/horizontal.png");
}
- .tb-split.tb-split-horizontal, .gutter.gutter-horizontal {
- height: 100%;
+ .tb-split.tb-split-horizontal,
+ .gutter.gutter-horizontal {
float: left;
+ height: 100%;
}
.tb-split.tb-split-vertical {
display: flex;
+
.tb-split.tb-content {
height: 100%;
}
@@ -64,7 +66,6 @@ $edit-toolbar-height: 40px;
.tb-split-vertical {
md-tabs {
-
md-tabs-content-wrapper {
height: calc(100% - 49px);
@@ -74,45 +75,44 @@ $edit-toolbar-height: 40px;
& > div {
height: 100%;
}
-
}
-
}
-
}
}
div.tb-editor-area-title-panel {
position: absolute;
- font-size: 0.800rem;
- font-weight: 500;
top: 5px;
right: 20px;
z-index: 5;
+ font-size: .8rem;
+ font-weight: 500;
+
label {
- color: #00acc1;
- background: rgba(220, 220, 220, 0.35);
- border-radius: 5px;
padding: 4px;
+ color: #00acc1;
text-transform: uppercase;
+ background: rgba(220, 220, 220, .35);
+ border-radius: 5px;
}
+
.md-button {
- color: #7B7B7B;
min-width: 32px;
min-height: 15px;
- line-height: 15px;
- font-size: 0.800rem;
- margin: 0;
padding: 4px;
- background: rgba(220, 220, 220, 0.35);
+ margin: 0;
+ font-size: .8rem;
+ line-height: 15px;
+ color: #7b7b7b;
+ background: rgba(220, 220, 220, .35);
}
}
.tb-resize-container {
- overflow-y: auto;
- height: 100%;
- width: 100%;
position: relative;
+ width: 100%;
+ height: 100%;
+ overflow-y: auto;
.ace_editor {
height: 100%;
@@ -127,24 +127,28 @@ md-toolbar.tb-edit-toolbar {
.md-toolbar-tools {
min-height: $edit-toolbar-height !important;
max-height: $edit-toolbar-height !important;
+
.md-button {
min-width: 65px;
min-height: 30px;
- line-height: 30px;
font-size: 12px;
+ line-height: 30px;
+
md-icon {
font-size: 20px;
}
+
span {
padding-right: 6px;
}
}
+
md-input-container {
input {
- font-size: 1.200rem;
- font-weight: 400;
- letter-spacing: 0.005em;
height: 28px;
+ font-size: 1.2rem;
+ font-weight: 400;
+ letter-spacing: .005em;
}
}
}
ui/src/scss/animations.scss 6(+5 -1)
diff --git a/ui/src/scss/animations.scss b/ui/src/scss/animations.scss
index 719b6f9..4ecfc34 100644
--- a/ui/src/scss/animations.scss
+++ b/ui/src/scss/animations.scss
@@ -18,6 +18,7 @@
@include keyframes(tbMoveFromTopFade) {
from {
opacity: 0;
+
@include transform(translate(0, -100%));
}
}
@@ -25,6 +26,7 @@
@include keyframes(tbMoveToTopFade) {
to {
opacity: 0;
+
@include transform(translate(0, -100%));
}
}
@@ -32,6 +34,7 @@
@include keyframes(tbMoveFromBottomFade) {
from {
opacity: 0;
+
@include transform(translate(0, 100%));
}
}
@@ -39,6 +42,7 @@
@include keyframes(tbMoveToBottomFade) {
to {
opacity: 0;
+
@include transform(translate(0, 150%));
}
-}
\ No newline at end of file
+}
ui/src/scss/constants.scss 20(+10 -10)
diff --git a/ui/src/scss/constants.scss b/ui/src/scss/constants.scss
index 5b6eab4..b462c66 100644
--- a/ui/src/scss/constants.scss
+++ b/ui/src/scss/constants.scss
@@ -17,18 +17,18 @@
// Colors
-$gray: #eee;
+$gray: #eee !default;
-$primary-palette-color: 'indigo';
-$default: '500';
-$hue-1: '300';
-$hue-2: '800';
-$hue-3: 'a100';
+$primary-palette-color: "indigo" !default;
+$default: "500" !default;
+$hue-1: "300" !default;
+$hue-2: "800" !default;
+$hue-3: "a100" !default;
-$primary-default: #305680; //material-color($primary-palette-color, $default);
-$primary-hue-1: material-color($primary-palette-color, $hue-1);
-$primary-hue-2: material-color($primary-palette-color, $hue-2);
-$primary-hue-3: rgb(207, 216, 220);
+$primary-default: #305680 !default; //material-color($primary-palette-color, $default);
+$primary-hue-1: material-color($primary-palette-color, $hue-1) !default;
+$primary-hue-2: material-color($primary-palette-color, $hue-2) !default;
+$primary-hue-3: rgb(207, 216, 220) !default;
// Layout
ui/src/scss/fonts.scss 8(+4 -4)
diff --git a/ui/src/scss/fonts.scss b/ui/src/scss/fonts.scss
index 95089eb..6348d35 100644
--- a/ui/src/scss/fonts.scss
+++ b/ui/src/scss/fonts.scss
@@ -14,8 +14,8 @@
* limitations under the License.
*/
@font-face {
- font-family: 'Segment7Standard';
- src: url('data:font/opentype;charset=utf-8;base64,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AAEAAAAIAAAAAAAEAAAAAAAAAAEAAAAAzD2izwAAAADPr89TAAAAAM+4fiECSQAAAkkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABpwBCAC8ALwAvAFwBpwAvAC8ALwBcAC8AXAIBAC8ALwGWAC8ALwBCAC4ALgBYAC8ALwBcAacALwAvAC8ALwAvAC8ALwAvAC8ALwAvAC8ALwGWAC8ALwAvAC8ALwAvAC8AQgAvAC4ALwAvAC8ALwAvAC8ALwAvAEIALwBCAFwBpwAvAC8ALwAvAC8ALwAvAC8BlgAvAC8ALwAvAC8ALwAvAEIALwAuAC8ALwAvAC8ALwAvAC8AAAGWAAAALwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAAAAAAAAAvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC8ALwAAAAAAAAAAAAAAAAAAAC8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') format('opentype');
- font-weight: normal;
+ font-family: "Segment7Standard";
font-style: italic;
-}
\ No newline at end of file
+ font-weight: 400;
+ src: url("data:font/opentype;charset=utf-8;base64,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AAEAAAAIAAAAAAAEAAAAAAAAAAEAAAAAzD2izwAAAADPr89TAAAAAM+4fiECSQAAAkkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABpwBCAC8ALwAvAFwBpwAvAC8ALwBcAC8AXAIBAC8ALwGWAC8ALwBCAC4ALgBYAC8ALwBcAacALwAvAC8ALwAvAC8ALwAvAC8ALwAvAC8ALwGWAC8ALwAvAC8ALwAvAC8AQgAvAC4ALwAvAC8ALwAvAC8ALwAvAEIALwBCAFwBpwAvAC8ALwAvAC8ALwAvAC8BlgAvAC8ALwAvAC8ALwAvAEIALwAuAC8ALwAvAC8ALwAvAC8AAAGWAAAALwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAAAAAAAAAvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC8ALwAAAAAAAAAAAAAAAAAAAC8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") format("opentype");
+}
ui/src/scss/main.scss 309(+181 -128)
diff --git a/ui/src/scss/main.scss b/ui/src/scss/main.scss
index 2852a7b..27c2d3b 100644
--- a/ui/src/scss/main.scss
+++ b/ui/src/scss/main.scss
@@ -14,77 +14,92 @@
* limitations under the License.
*/
@import "~compass-sass-mixins/lib/compass";
+
@import "constants";
+
@import "animations";
+
@import "mixins";
+
@import "fonts";
/***************
* TYPE DEFAULTS
***************/
-button, html, input, select, textarea {
- font-family: Roboto, 'Helvetica Neue', sans-serif;
+button,
+html,
+input,
+select,
+textarea {
+ font-family: Roboto, "Helvetica Neue", sans-serif;
}
.mdi-set {
line-height: 1;
- letter-spacing: normal;
text-transform: none;
- white-space: nowrap;
+ letter-spacing: normal;
word-wrap: normal;
+ white-space: nowrap;
direction: ltr;
- -webkit-font-feature-settings: 'liga';
+ -webkit-font-feature-settings: "liga";
}
a {
- color: #106CC8;
- text-decoration: none;
font-weight: 400;
- border-bottom: 1px solid rgba(64, 84, 178, 0.25);
- @include transition(border-bottom 0.35s);
+ color: #106cc8;
+ text-decoration: none;
+ border-bottom: 1px solid rgba(64, 84, 178, .25);
+
+ @include transition(border-bottom .35s);
}
-a:hover, a:focus {
- border-bottom: 1px solid #4054B2;
+a:hover,
+a:focus {
+ border-bottom: 1px solid #4054b2;
}
-h1, h2, h3, h4, h5, h6 {
- margin-bottom: 1rem;
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
margin-top: 1rem;
+ margin-bottom: 1rem;
}
h1 {
- font-size: 3.400rem;
+ font-size: 3.4rem;
font-weight: 400;
line-height: 4rem;
}
h2 {
- font-size: 2.400rem;
+ font-size: 2.4rem;
font-weight: 400;
line-height: 3.2rem;
}
h3 {
- font-size: 2.000rem;
+ font-size: 2rem;
font-weight: 500;
- letter-spacing: 0.005em;
+ letter-spacing: .005em;
}
h4 {
- font-size: 1.600rem;
+ font-size: 1.6rem;
font-weight: 400;
- letter-spacing: 0.010em;
line-height: 2.4rem;
+ letter-spacing: .01em;
}
p {
+ margin: .8em 0 1.6em;
font-size: 1.6rem;
font-weight: 400;
- letter-spacing: 0.010em;
line-height: 1.6em;
- margin: 0.8em 0 1.6em;
+ letter-spacing: .01em;
}
strong {
@@ -92,16 +107,16 @@ strong {
}
blockquote {
- border-left: 3px solid rgba(0, 0, 0, 0.12);
- font-style: italic;
- margin-left: 0;
padding-left: 16px;
+ margin-left: 0;
+ font-style: italic;
+ border-left: 3px solid rgba(0, 0, 0, .12);
}
fieldset {
- border: none;
padding: 0;
margin: 0;
+ border: none;
}
/*********************************
@@ -119,26 +134,28 @@ form {
}
md-bottom-sheet .md-subheader {
- font-family: Roboto, 'Helvetica Neue', sans-serif;
+ font-family: Roboto, "Helvetica Neue", sans-serif;
}
.md-chips {
- font-family: Roboto, 'Helvetica Neue', sans-serif;
+ font-family: Roboto, "Helvetica Neue", sans-serif;
}
-md-content.md-default-theme, md-content {
+md-content.md-default-theme,
+md-content {
background-color: $gray;
}
md-card {
background-color: #fff;
+
h2:first-of-type {
margin-top: 0;
}
}
.md-button:not([disabled]).md-icon-button:hover {
- background-color: rgba(158, 158, 158, 0.2);
+ background-color: rgba(158, 158, 158, .2);
}
md-toolbar:not(.md-hue-1),
@@ -147,11 +164,12 @@ md-toolbar:not(.md-hue-1),
}
md-toolbar md-input-container .md-errors-spacer {
- min-height: 0px;
+ min-height: 0;
}
md-toolbar {
- md-select.md-default-theme:not([disabled]):focus .md-select-value, md-select:not([disabled]):focus .md-select-value {
+ md-select.md-default-theme:not([disabled]):focus .md-select-value,
+ md-select:not([disabled]):focus .md-select-value {
color: #fff;
}
}
@@ -159,11 +177,10 @@ md-toolbar {
md-menu-item {
overflow: hidden;
fill: #737373;
-}
-md-menu-item {
.md-button {
display: block;
+
.tb-alt-text {
float: right;
}
@@ -190,13 +207,14 @@ md-sidenav {
overflow-y: auto;
}
-.md-radio-interactive input, button {
+.md-radio-interactive input,
+button {
pointer-events: all;
}
.md-color-picker-input-container {
md-input-container {
- margin-bottom: 0px;
+ margin-bottom: 0;
}
}
@@ -204,32 +222,36 @@ md-sidenav {
* THINGSBOARD SPECIFIC
***********************/
-$swift-ease-out-duration: 0.4s !default;
-$swift-ease-out-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1) !default;
+$swift-ease-out-duration: .4s !default;
+$swift-ease-out-timing-function: cubic-bezier(.25, .8, .25, 1) !default;
$input-label-float-offset: 6px !default;
-$input-label-float-scale: 0.75 !default;
+$input-label-float-scale: .75 !default;
label {
&.tb-title {
- pointer-events: none;
- color: #666;
+ padding-bottom: 15px;
font-size: 13px;
font-weight: 400;
- padding-bottom: 15px;
+ color: #666;
+ pointer-events: none;
+
&.no-padding {
- padding-bottom: 0px;
+ padding-bottom: 0;
}
- &.tb-required:after {
- content: ' *';
+
+ &.tb-required::after {
font-size: 13px;
+ color: rgba(0, 0, 0, .54);
vertical-align: top;
- color: rgba(0,0,0,0.54);
+ content: " *";
}
+
&.tb-error {
- color: rgb(221,44,0);
- &.tb-required:after {
- color: rgb(221,44,0);
+ color: rgb(221, 44, 0);
+
+ &.tb-required::after {
+ color: rgb(221, 44, 0);
}
}
}
@@ -246,96 +268,107 @@ label {
}
.tb-readonly-label {
- color: rgba(0,0,0,0.54);
+ color: rgba(0, 0, 0, .54);
}
.tb-disabled-label {
- color: rgba(0,0,0,0.44);
+ color: rgba(0, 0, 0, .44);
}
+/* stylelint-disable-next-line no-duplicate-selectors */
label {
&.tb-small {
- pointer-events: none;
- color: rgba(0,0,0,0.54);
font-size: 12px;
+ color: rgba(0, 0, 0, .54);
+ pointer-events: none;
}
}
div {
&.tb-small {
- color: rgba(0,0,0,0.54);
font-size: 14px;
+ color: rgba(0, 0, 0, .54);
}
}
.tb-hint {
+ padding-bottom: 15px;
font-size: 12px;
line-height: 14px;
- transition: all 0.3s cubic-bezier(0.55, 0, 0.55, 0.2);
- color: grey;
- padding-bottom: 15px;
- &.ng-hide, &.ng-enter, &.ng-leave.ng-leave-active {
+ color: #808080;
+ transition: all .3s cubic-bezier(.55, 0, .55, .2);
+
+ &.ng-hide,
+ &.ng-enter,
+ &.ng-leave.ng-leave-active {
bottom: 26px;
opacity: 0;
}
- &.ng-leave, &.ng-enter.ng-enter-active {
+
+ &.ng-leave,
+ &.ng-enter.ng-enter-active {
bottom: 7px;
opacity: 1;
}
}
.md-caption {
- &.tb-required:after {
- content: ' *';
+ &.tb-required::after {
font-size: 10px;
+ color: rgba(0, 0, 0, .54);
vertical-align: top;
- color: rgba(0,0,0,0.54);
+ content: " *";
}
}
pre.tb-highlight {
- background-color: #f7f7f7;
display: block;
- margin: 20px 0;
padding: 15px;
+ margin: 20px 0;
overflow-x: auto;
+ background-color: #f7f7f7;
+
code {
+ box-sizing: border-box;
+ display: inline-block;
padding: 0;
- color: #303030;
font-family: monospace;
- display: inline-block;
- box-sizing: border-box;
- vertical-align: bottom;
font-size: 16px;
- font-weight: bold;
+ font-weight: 700;
+ color: #303030;
+ vertical-align: bottom;
}
}
.tb-notice {
- background-color: #f7f7f7;
padding: 15px;
- border: 1px solid #ccc;
font-size: 16px;
+ background-color: #f7f7f7;
+ border: 1px solid #ccc;
}
.tb-data-table {
md-toolbar {
z-index: 0;
}
+
md-toolbar.md-table-toolbar.md-default-theme:not(.md-menu-toolbar).md-default .md-button[disabled],
md-toolbar.md-table-toolbar:not(.md-menu-toolbar).md-default .md-button[disabled] {
- color: rgba(0,0,0,0.38);
+ color: rgba(0, 0, 0, .38);
}
+
md-toolbar.md-default-theme:not(.md-menu-toolbar) .md-button[disabled] md-icon,
md-toolbar:not(.md-menu-toolbar) .md-button[disabled] md-icon {
- color: rgba(0,0,0,.28);
+ color: rgba(0, 0, 0, .28);
}
+
span.no-data-found {
position: relative;
+ display: flex;
height: calc(100% - 57px);
text-transform: uppercase;
- display: flex;
}
+
table.md-table {
&.md-row-select td.md-cell,
&.md-row-select th.md-column {
@@ -366,49 +399,55 @@ pre.tb-highlight {
td.md-cell,
th.md-column {
-
&:last-child {
padding: 0 12px 0 0;
}
-
}
}
- table.md-table, table.md-table.md-row-select {
+ table.md-table,
+ table.md-table.md-row-select {
tbody {
&.md-body {
tr {
&.md-row:not([disabled]) {
outline: none;
+
&:hover {
- background-color: rgba(221, 221, 221, 0.3) !important;
+ background-color: rgba(221, 221, 221, .3) !important;
}
+
&.md-selected {
- background-color: rgba(221, 221, 221, 0.5) !important;
+ background-color: rgba(221, 221, 221, .5) !important;
}
- &.tb-current, &.tb-current:hover{
- background-color: rgba(221, 221, 221, 0.65) !important;
+
+ &.tb-current,
+ &.tb-current:hover{
+ background-color: rgba(221, 221, 221, .65) !important;
}
}
}
}
+
tr {
td {
&.tb-action-cell {
+ width: 72px;
+ min-width: 72px;
+ max-width: 72px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
- min-width: 72px;
- max-width: 72px;
- width: 72px;
+
.md-button {
&.md-icon-button {
- margin: 0;
- padding: 6px;
width: 36px;
height: 36px;
+ padding: 6px;
+ margin: 0;
}
}
+
.tb-spacer {
padding-left: 38px;
}
@@ -420,34 +459,40 @@ pre.tb-highlight {
}
.tb-severity {
- font-weight: bold;
+ font-weight: 700;
+
&.tb-critical {
- color: red !important;
+ color: #f00 !important;
}
+
&.tb-major {
- color: orange !important;
+ color: #ffa500 !important;
}
+
&.tb-minor {
color: #ffca3d !important;
}
+
&.tb-warning {
color: #abab00 !important;
}
+
&.tb-indeterminate {
- color: green !important;
+ color: #008000 !important;
}
}
.tb-card-description {
- color: rgba(0,0,0,0.54);
font-size: 13px;
+ color: rgba(0, 0, 0, .54);
+
@include line-clamp(2, 1.1);
}
/***********************
* Flow
***********************/
-$previewSize: 100px;
+$previewSize: 100px !default;
.file-input {
display: none;
@@ -455,17 +500,19 @@ $previewSize: 100px;
.tb-flow-drop {
position: relative;
- border: dashed 2px;
height: $previewSize;
overflow: hidden;
+ border: dashed 2px;
+
label {
- width: 100%;
- height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
+ width: 100%;
+ height: 100%;
font-size: 16px;
text-align: center;
+
@media (min-width: $layout-breakpoint-sm) {
font-size: 24px;
}
@@ -474,8 +521,8 @@ $previewSize: 100px;
.tb-container {
position: relative;
- margin-top: 32px;
padding: 10px 0;
+ margin-top: 32px;
}
/***********************
@@ -483,12 +530,12 @@ $previewSize: 100px;
***********************/
.tb-prompt {
- color: rgba(0,0,0,0.38);
- text-transform: uppercase;
display: flex;
font-size: 18px;
font-weight: 400;
line-height: 18px;
+ color: rgba(0, 0, 0, .38);
+ text-transform: uppercase;
}
/***********************
@@ -500,35 +547,37 @@ $previewSize: 100px;
}
.tb-error-message {
+ padding: 10px 0 0 10px;
+ margin-top: -6px;
+ overflow: hidden;
font-size: 12px;
line-height: 14px;
- overflow: hidden;
- padding: 10px 0px 0px 10px;
- color: rgb(221,44,0);
- margin-top: -6px;
+ color: rgb(221, 44, 0);
}
.tb-error-message.ng-animate {
- @include transition(all .3s cubic-bezier(.55,0,.55,.2));
+ @include transition(all .3s cubic-bezier(.55, 0, .55, .2));
}
-.tb-error-message.ng-enter-prepare, .tb-error-message.ng-enter {
- opacity:0;
+.tb-error-message.ng-enter-prepare,
+.tb-error-message.ng-enter {
margin-top: -24px;
+ opacity: 0;
}
.tb-error-message.ng-enter.ng-enter-active {
- opacity:1;
margin-top: -6px;
+ opacity: 1;
}
.tb-error-message.ng-leave {
- opacity:1;
margin-top: -6px;
+ opacity: 1;
}
+
.tb-error-message.ng-leave.ng-leave-active {
- opacity:0;
margin-top: -24px;
+ opacity: 0;
}
/***********************
@@ -545,39 +594,41 @@ md-tabs.tb-headless {
.md-button.tb-card-button {
width: 100%;
- height: 100%;
max-width: 240px;
+ height: 100%;
+
span {
+ height: 18px;
+ min-height: 18px;
+ max-height: 18px;
padding: 0 0 20px 0;
+ margin: auto;
font-size: 18px;
font-weight: 400;
- white-space: normal;
line-height: 18px;
- max-height: 18px;
- min-height: 18px;
- height: 18px;
- margin: auto;
+ white-space: normal;
}
}
.md-button.tb-layout-button {
width: 100%;
- height: 100%;
max-width: 240px;
+ height: 100%;
+
span {
padding: 40px;
font-size: 18px;
font-weight: 400;
- white-space: normal;
line-height: 18px;
+ white-space: normal;
}
}
.md-button.tb-add-new-widget {
+ padding-right: 12px;
+ font-size: 24px;
border-style: dashed;
border-width: 2px;
- font-size: 24px;
- padding-right: 12px;
}
/***********************
@@ -585,11 +636,12 @@ md-tabs.tb-headless {
***********************/
section.tb-header-buttons {
- pointer-events: none;
position: absolute;
- right: 0px;
top: 86px;
+ right: 0;
z-index: 3;
+ pointer-events: none;
+
@media (min-width: $layout-breakpoint-sm) {
top: 86px;
}
@@ -633,6 +685,7 @@ section.tb-footer-buttons {
._md-toast-open-bottom .tb-footer-buttons {
@include transition(all .4s cubic-bezier(.25, .8, .25, 1));
+
@include transform(translate3d(0, -42px, 0));
}
@@ -641,27 +694,27 @@ section.tb-footer-buttons {
***********************/
.md-icon-button.tb-md-32 {
- vertical-align: middle;
width: 32px;
- height: 32px;
min-width: 32px;
+ height: 32px;
min-height: 32px;
- margin: 0px !important;
- padding: 0px !important;
+ padding: 0 !important;
+ margin: 0 !important;
+ vertical-align: middle;
}
.material-icons.tb-md-20 {
- font-size: 20px;
width: 20px;
- height: 20px;
min-width: 20px;
+ height: 20px;
min-height: 20px;
+ font-size: 20px;
}
.material-icons.tb-md-96 {
- font-size: 96px;
width: 96px;
height: 96px;
+ font-size: 96px;
}
/***********************
@@ -671,17 +724,17 @@ section.tb-footer-buttons {
.tb-absolute-fill {
position: absolute;
top: 0;
- left: 0;
right: 0;
bottom: 0;
+ left: 0;
}
.tb-progress-cover {
position: absolute;
top: 0;
- left: 0;
right: 0;
bottom: 0;
+ left: 0;
z-index: 6;
opacity: 1;
}
ui/src/scss/mixins.scss 26(+16 -10)
diff --git a/ui/src/scss/mixins.scss b/ui/src/scss/mixins.scss
index cb66171..a754538 100644
--- a/ui/src/scss/mixins.scss
+++ b/ui/src/scss/mixins.scss
@@ -17,43 +17,49 @@
@mixin input-placeholder {
// replaces compass/css/user-interface/input-placeholder()
+
&::-webkit-input-placeholder {
@content;
}
+
&:-moz-placeholder {
@content;
opacity: 1;
}
+
&::-moz-placeholder {
@content;
opacity: 1;
}
+
&:-ms-input-placeholder {
@content;
}
}
@mixin line-clamp($numLines: 1, $lineHeight: 1.412) {
- overflow: hidden;
position: relative;
+ max-height: ($numLines * $lineHeight * 1em);
+ padding-right: 2em;
+ margin-right: -1em;
+ overflow: hidden;
line-height: $lineHeight;
text-align: justify;
- margin-right: -1em;
- padding-right: 2em;
- max-height: ($numLines*$lineHeight)+em;
- &:before {
- content: '...';
+
+ &::before {
position: absolute;
right: 1em;
bottom: 0;
+ content: "...";
}
- &:after {
- content: '';
+
+ &::after {
position: absolute;
right: 1em;
width: 1em;
height: 1em;
- margin-top: 0.2em;
- background: white;
+ margin-top: .2em;
+ content: "";
+ background: #fff;
}
}
ui/webpack.config.dev.js 3(+3 -0)
diff --git a/ui/webpack.config.dev.js b/ui/webpack.config.dev.js
index 242ad9d..cfff811 100644
--- a/ui/webpack.config.dev.js
+++ b/ui/webpack.config.dev.js
@@ -18,6 +18,8 @@
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
+const StyleLintPlugin = require('stylelint-webpack-plugin')
+
const webpack = require('webpack');
const path = require('path');
const dirTree = require('directory-tree');
@@ -76,6 +78,7 @@ module.exports = {
title: 'ThingsBoard',
inject: 'body',
}),
+ new StyleLintPlugin(),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.NoErrorsPlugin(),
new ExtractTextPlugin('style.[contentHash].css', {