Commit a42bd5d7eeaf744aa58df15d5b943a3b7cc36d1a

Authored by Igor Kulikov
1 parent 70e81e84

UI: RuleNode configuration

Showing 101 changed files with 1151 additions and 118 deletions
@@ -41,7 +41,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; @@ -41,7 +41,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS;
41 nodeDescription = "Periodically generates messages", 41 nodeDescription = "Periodically generates messages",
42 nodeDetails = "Generates messages with configurable period. Javascript function used for message generation.", 42 nodeDetails = "Generates messages with configurable period. Javascript function used for message generation.",
43 inEnabled = false, 43 inEnabled = false,
44 - uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, 44 + uiResources = {"static/rulenode/rulenode-core-config.js"},
45 configDirective = "tbActionNodeGeneratorConfig", 45 configDirective = "tbActionNodeGeneratorConfig",
46 icon = "repeat" 46 icon = "repeat"
47 ) 47 )
@@ -32,7 +32,7 @@ import org.thingsboard.server.common.msg.TbMsg; @@ -32,7 +32,7 @@ import org.thingsboard.server.common.msg.TbMsg;
32 relationTypes = {"True", "False"}, 32 relationTypes = {"True", "False"},
33 nodeDescription = "Filter incoming messages by Message Type", 33 nodeDescription = "Filter incoming messages by Message Type",
34 nodeDetails = "If incoming MessageType is expected - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used.", 34 nodeDetails = "If incoming MessageType is expected - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used.",
35 - uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, 35 + uiResources = {"static/rulenode/rulenode-core-config.js"},
36 configDirective = "tbFilterNodeMessageTypeConfig") 36 configDirective = "tbFilterNodeMessageTypeConfig")
37 public class TbMsgTypeFilterNode implements TbNode { 37 public class TbMsgTypeFilterNode implements TbNode {
38 38
@@ -30,7 +30,7 @@ import org.thingsboard.server.common.msg.TbMsg; @@ -30,7 +30,7 @@ import org.thingsboard.server.common.msg.TbMsg;
30 relationTypes = {"True", "False"}, 30 relationTypes = {"True", "False"},
31 nodeDescription = "Filter incoming messages by message Originator Type", 31 nodeDescription = "Filter incoming messages by message Originator Type",
32 nodeDetails = "If Originator Type of incoming message is expected - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used.", 32 nodeDetails = "If Originator Type of incoming message is expected - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used.",
33 - uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, 33 + uiResources = {"static/rulenode/rulenode-core-config.js"},
34 configDirective = "tbFilterNodeOriginatorTypeConfig") 34 configDirective = "tbFilterNodeOriginatorTypeConfig")
35 public class TbOriginatorTypeFilterNode implements TbNode { 35 public class TbOriginatorTypeFilterNode implements TbNode {
36 36
@@ -32,7 +32,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType; @@ -32,7 +32,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
32 "If Latest Telemetry enrichment configured, latest telemetry added into metadata. " + 32 "If Latest Telemetry enrichment configured, latest telemetry added into metadata. " +
33 "To access those attributes in other nodes this template can be used " + 33 "To access those attributes in other nodes this template can be used " +
34 "<code>metadata.temperature</code>.", 34 "<code>metadata.temperature</code>.",
35 - uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, 35 + uiResources = {"static/rulenode/rulenode-core-config.js"},
36 configDirective = "tbEnrichmentNodeCustomerAttributesConfig") 36 configDirective = "tbEnrichmentNodeCustomerAttributesConfig")
37 public class TbGetCustomerAttributeNode extends TbEntityGetAttrNode<CustomerId> { 37 public class TbGetCustomerAttributeNode extends TbEntityGetAttrNode<CustomerId> {
38 38
@@ -34,7 +34,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType; @@ -34,7 +34,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
34 "If Latest Telemetry enrichment configured, latest telemetry added into metadata. " + 34 "If Latest Telemetry enrichment configured, latest telemetry added into metadata. " +
35 "To access those attributes in other nodes this template can be used " + 35 "To access those attributes in other nodes this template can be used " +
36 "<code>metadata.temperature</code>.", 36 "<code>metadata.temperature</code>.",
37 - uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, 37 + uiResources = {"static/rulenode/rulenode-core-config.js"},
38 configDirective = "tbEnrichmentNodeRelatedAttributesConfig") 38 configDirective = "tbEnrichmentNodeRelatedAttributesConfig")
39 39
40 public class TbGetRelatedAttributeNode extends TbEntityGetAttrNode<EntityId> { 40 public class TbGetRelatedAttributeNode extends TbEntityGetAttrNode<EntityId> {
@@ -34,7 +34,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType; @@ -34,7 +34,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
34 "If Latest Telemetry enrichment configured, latest telemetry added into metadata. " + 34 "If Latest Telemetry enrichment configured, latest telemetry added into metadata. " +
35 "To access those attributes in other nodes this template can be used " + 35 "To access those attributes in other nodes this template can be used " +
36 "<code>metadata.temperature</code>.", 36 "<code>metadata.temperature</code>.",
37 - uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, 37 + uiResources = {"static/rulenode/rulenode-core-config.js"},
38 configDirective = "tbEnrichmentNodeTenantAttributesConfig") 38 configDirective = "tbEnrichmentNodeTenantAttributesConfig")
39 public class TbGetTenantAttributeNode extends TbEntityGetAttrNode<TenantId> { 39 public class TbGetTenantAttributeNode extends TbEntityGetAttrNode<TenantId> {
40 40
@@ -47,7 +47,7 @@ import java.util.concurrent.TimeoutException; @@ -47,7 +47,7 @@ import java.util.concurrent.TimeoutException;
47 configClazz = TbMqttNodeConfiguration.class, 47 configClazz = TbMqttNodeConfiguration.class,
48 nodeDescription = "Publish messages to the MQTT broker", 48 nodeDescription = "Publish messages to the MQTT broker",
49 nodeDetails = "Will publish message payload to the MQTT broker with QoS <b>AT_LEAST_ONCE</b>.", 49 nodeDetails = "Will publish message payload to the MQTT broker with QoS <b>AT_LEAST_ONCE</b>.",
50 - uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, 50 + uiResources = {"static/rulenode/rulenode-core-config.js"},
51 configDirective = "tbActionNodeMqttConfig", 51 configDirective = "tbActionNodeMqttConfig",
52 icon = "call_split" 52 icon = "call_split"
53 ) 53 )
@@ -42,7 +42,7 @@ import java.util.Set; @@ -42,7 +42,7 @@ import java.util.Set;
42 configClazz = TbMsgAttributesNodeConfiguration.class, 42 configClazz = TbMsgAttributesNodeConfiguration.class,
43 nodeDescription = "Saves attributes data", 43 nodeDescription = "Saves attributes data",
44 nodeDetails = "Saves entity attributes based on configurable scope parameter. Expects messages with 'POST_ATTRIBUTES_REQUEST' message type", 44 nodeDetails = "Saves entity attributes based on configurable scope parameter. Expects messages with 'POST_ATTRIBUTES_REQUEST' message type",
45 - uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, 45 + uiResources = {"static/rulenode/rulenode-core-config.js"},
46 configDirective = "tbActionNodeAttributesConfig", 46 configDirective = "tbActionNodeAttributesConfig",
47 icon = "file_upload" 47 icon = "file_upload"
48 ) 48 )
@@ -43,7 +43,7 @@ import java.util.Map; @@ -43,7 +43,7 @@ import java.util.Map;
43 configClazz = TbMsgTimeseriesNodeConfiguration.class, 43 configClazz = TbMsgTimeseriesNodeConfiguration.class,
44 nodeDescription = "Saves timeseries data", 44 nodeDescription = "Saves timeseries data",
45 nodeDetails = "Saves timeseries telemetry data based on configurable TTL parameter. Expects messages with 'POST_TELEMETRY_REQUEST' message type", 45 nodeDetails = "Saves timeseries telemetry data based on configurable TTL parameter. Expects messages with 'POST_TELEMETRY_REQUEST' message type",
46 - uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, 46 + uiResources = {"static/rulenode/rulenode-core-config.js"},
47 configDirective = "tbActionNodeTimeseriesConfig", 47 configDirective = "tbActionNodeTimeseriesConfig",
48 icon = "file_upload" 48 icon = "file_upload"
49 ) 49 )
@@ -44,7 +44,7 @@ import java.util.HashSet; @@ -44,7 +44,7 @@ import java.util.HashSet;
44 nodeDetails = "Related Entity found using configured relation direction and Relation Type. " + 44 nodeDetails = "Related Entity found using configured relation direction and Relation Type. " +
45 "If multiple Related Entities are found, only first Entity is used as new Originator, other entities are discarded.<br/>" + 45 "If multiple Related Entities are found, only first Entity is used as new Originator, other entities are discarded.<br/>" +
46 "Alarm Originator found only in case original Originator is <code>Alarm</code> entity.", 46 "Alarm Originator found only in case original Originator is <code>Alarm</code> entity.",
47 - uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, 47 + uiResources = {"static/rulenode/rulenode-core-config.js"},
48 configDirective = "tbTransformationNodeChangeOriginatorConfig", 48 configDirective = "tbTransformationNodeChangeOriginatorConfig",
49 icon = "find_replace" 49 icon = "find_replace"
50 ) 50 )
@@ -33,7 +33,7 @@ import org.thingsboard.server.common.msg.TbMsg; @@ -33,7 +33,7 @@ import org.thingsboard.server.common.msg.TbMsg;
33 "Should return the following structure:<br/>" + 33 "Should return the following structure:<br/>" +
34 "<code>{ msg: <i style=\"color: #666;\">new payload</i>,<br/>&nbsp&nbsp&nbspmetadata: <i style=\"color: #666;\">new metadata</i>,<br/>&nbsp&nbsp&nbspmsgType: <i style=\"color: #666;\">new msgType</i> }</code><br/>" + 34 "<code>{ msg: <i style=\"color: #666;\">new payload</i>,<br/>&nbsp&nbsp&nbspmetadata: <i style=\"color: #666;\">new metadata</i>,<br/>&nbsp&nbsp&nbspmsgType: <i style=\"color: #666;\">new msgType</i> }</code><br/>" +
35 "All fields in resulting object are optional and will be taken from original message if not specified.", 35 "All fields in resulting object are optional and will be taken from original message if not specified.",
36 - uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, 36 + uiResources = {"static/rulenode/rulenode-core-config.js"},
37 configDirective = "tbTransformationNodeScriptConfig") 37 configDirective = "tbTransformationNodeScriptConfig")
38 public class TbTransformMsgNode extends TbAbstractTransformNode { 38 public class TbTransformMsgNode extends TbAbstractTransformNode {
39 39
1 -.tb-message-type-autocomplete .tb-not-found{display:block;line-height:1.5;height:48px}.tb-message-type-autocomplete .tb-not-found .tb-no-entries{line-height:48px}.tb-message-type-autocomplete li{height:auto!important;white-space:normal!important}.tb-generator-config tb-json-content.tb-message-body,.tb-generator-config tb-json-object-edit.tb-metadata-json{height:200px;display:block}.tb-mqtt-config .tb-credentials-panel-group .tb-panel-title{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;min-width:90px}@media (min-width:960px){.tb-mqtt-config .tb-credentials-panel-group .tb-panel-title{min-width:180px}}.tb-mqtt-config .tb-credentials-panel-group .tb-panel-prompt{font-size:14px;color:rgba(0,0,0,.87);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tb-mqtt-config .tb-credentials-panel-group.disabled .tb-panel-prompt,.tb-mqtt-config .tb-credentials-panel-group.disabled .tb-panel-title{color:rgba(0,0,0,.38)}.tb-mqtt-config .tb-credentials-panel-group md-icon.md-expansion-panel-icon{margin-right:0}.tb-mqtt-config .tb-container{width:100%}.tb-mqtt-config .dropdown-messages .tb-error-message{padding:5px 0 0}.tb-kv-map-config .header{padding-left:5px;padding-right:5px;padding-bottom:5px}.tb-kv-map-config .header .cell{padding-left:5px;padding-right:5px;color:rgba(0,0,0,.54);font-size:12px;font-weight:700;white-space:nowrap}.tb-kv-map-config .body{padding-left:5px;padding-right:5px;padding-bottom:20px;max-height:300px;overflow:auto}.tb-kv-map-config .body .row{padding-top:5px;max-height:40px}.tb-kv-map-config .body .cell{padding-left:5px;padding-right:5px}.tb-kv-map-config .body md-input-container.cell{margin:0;max-height:40px}.tb-kv-map-config .body .md-button{margin:0}  
2 -/*# sourceMappingURL=rulenode-core-config.css.map*/  
1 -!function(e){function t(i){if(n[i])return n[i].exports;var a=n[i]={exports:{},id:i,loaded:!1};return e[i].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),i=e[t[0]];return function(e,t,a){i.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(101)},function(e,t){},1,1,1,1,function(e,t){e.exports=" <section ng-form name=assignCustomerConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.customer-name-pattern</label> <input ng-required=true name=customerNamePattern ng-model=configuration.customerNamePattern> <div ng-messages=assignCustomerConfigForm.customerNamePattern.$error> <div ng-message=required translate>tb.rulenode.customer-name-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.customer-name-pattern-hint</div> </md-input-container> <md-checkbox aria-label=\"{{ 'tb.rulenode.create-group-if-not-exists' | translate }}\" ng-model=configuration.createCustomerIfNotExists>{{ 'tb.rulenode.create-customer-if-not-exists' | translate }} </md-checkbox> <md-input-container class=md-block> <label translate>tb.rulenode.customer-cache-expiration</label> <input type=number step=1 min=0 ng-required=true name=customerCacheExpiration ng-model=configuration.customerCacheExpiration> <div ng-messages=assignCustomerConfigForm.customerCacheExpiration.$error> <div translate ng-message=required>tb.rulenode.customer-cache-expiration-required</div> <div translate ng-message=min>tb.rulenode.customer-cache-expiration-range</div> </div> <div class=tb-hint translate>tb.rulenode.customer-cache-expiration-hint</div> </md-input-container> </section> "},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> <div class=tb-hint translate>tb.rulenode.entity-type-pattern-hint</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> <md-checkbox aria-label=\"{{ 'tb.rulenode.use-metadata-interval-patterns' | translate }}\" ng-model=configuration.useMessageAlarmData>{{ 'tb.rulenode.use-message-alarm-data' | translate }} </md-checkbox> <section layout=column layout-gt-sm=row ng-if=!configuration.useMessageAlarmData> <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> <div class=tb-hint translate>tb.rulenode.entity-type-pattern-hint</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> <section layout=column layout-gt-sm=row ng-if=!configuration.useMessageAlarmData> <md-checkbox aria-label=\"{{ 'tb.rulenode.propagate' | translate }}\" ng-model=configuration.propagate>{{ 'tb.rulenode.propagate' | translate }} </md-checkbox> </section> </section> "},function(e,t){e.exports=" <section ng-form name=createRelationConfigForm layout=column style=min-width:650px> <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> <md-input-container class=md-block> <tb-entity-type-select style=min-width:100px the-form=createRelationConfigForm tb-required=true ng-model=configuration.entityType> </tb-entity-type-select> </md-input-container> <md-input-container class=md-block flex ng-if=configuration.entityType style=margin-top:38px> <label translate>tb.rulenode.entity-name-pattern</label> <input ng-required=true name=entityNamePattern ng-model=configuration.entityNamePattern> <div ng-messages=createRelationConfigForm.entityNamePattern.$error> <div ng-message=required translate>tb.rulenode.entity-name-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.entity-name-pattern-hint</div> </md-input-container> <md-input-container class=md-block flex ng-if=\"configuration.entityType == 'DEVICE' || configuration.entityType == 'ASSET'\" style=margin-top:38px> <label translate>tb.rulenode.entity-type-pattern</label> <input ng-required=true name=entityTypePattern ng-model=configuration.entityTypePattern> <div ng-messages=createRelationConfigForm.entityTypePattern.$error> <div ng-message=required translate>tb.rulenode.entity-type-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.entity-type-pattern-hint</div> </md-input-container> </div> <md-input-container class=md-block flex style=margin-top:0> <label translate>tb.rulenode.relation-type-pattern</label> <input ng-required=true name=relationType ng-model=configuration.relationType> <div ng-messages=createRelationConfigForm.relationType.$error> <div ng-message=required translate>tb.rulenode.relation-type-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.relation-type-pattern-hint</div> </md-input-container> <md-checkbox flex ng-if=\"configuration.entityType == 'CUSTOMER' || configuration.entityType == 'ASSET' || configuration.entityType == 'DEVICE'\" aria-label=\"{{ 'tb.rulenode.create-entity-if-not-exists' | translate }}\" ng-model=configuration.createEntityIfNotExists>{{ 'tb.rulenode.create-entity-if-not-exists' | translate }} </md-checkbox> <div class=tb-hint ng-if=\"configuration.entityType == 'CUSTOMER' || configuration.entityType == 'ASSET' || configuration.entityType == 'DEVICE'\" translate>tb.rulenode.create-entity-if-not-exists-hint</div> <md-checkbox flex aria-label=\"{{ 'tb.rulenode.remove-current-relations' | translate }}\" ng-model=configuration.removeCurrentRelations>{{ 'tb.rulenode.remove-current-relations' | translate }} </md-checkbox> <div class=tb-hint translate>tb.rulenode.remove-current-relations-hint</div> <md-checkbox flex aria-label=\"{{ 'tb.rulenode.change-originator-to-related-entity' | translate }}\" ng-model=configuration.changeOriginatorToRelatedEntity>{{ 'tb.rulenode.change-originator-to-related-entity' | translate }} </md-checkbox> <div class=tb-hint translate>tb.rulenode.change-originator-to-related-entity-hint</div> <md-input-container class=md-block> <label translate>tb.rulenode.entity-cache-expiration</label> <input type=number step=1 min=0 ng-required=true name=entityCacheExpiration ng-model=configuration.entityCacheExpiration> <div ng-messages=createRelationConfigForm.entityCacheExpiration.$error> <div translate ng-message=required>tb.rulenode.entity-cache-expiration-required</div> <div translate ng-message=min>tb.rulenode.entity-cache-expiration-range</div> </div> <div class=tb-hint translate>tb.rulenode.entity-cache-expiration-hint</div> </md-input-container> </section> "},function(e,t){e.exports=" <section ng-form name=deleteRelationConfigForm layout=column> <md-checkbox aria-label=\"{{ 'tb.rulenode.delete-relation-to-specific-entity' | translate }}\" ng-model=configuration.deleteForSingleEntity> {{ 'tb.rulenode.delete-relation-to-specific-entity' | translate }} </md-checkbox> <div class=tb-hint translate>tb.rulenode.delete-relation-hint</div> <md-input-container class=md-block style=min-width:100px;margin-bottom:38px> <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 ng-if=configuration.deleteForSingleEntity> <md-input-container class=md-block> <tb-entity-type-select style=min-width:100px the-form=deleteRelationConfigForm tb-required=true ng-model=configuration.entityType> </tb-entity-type-select> </md-input-container> <md-input-container class=md-block flex ng-if=configuration.entityType style=margin-top:38px> <label translate>tb.rulenode.entity-name-pattern</label> <input ng-required=true name=entityNamePattern ng-model=configuration.entityNamePattern> <div ng-messages=deleteRelationConfigForm.entityNamePattern.$error> <div ng-message=required translate>tb.rulenode.entity-name-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.entity-name-pattern-hint</div> </md-input-container> </div> <md-input-container class=md-block flex> <label translate>tb.rulenode.relation-type-pattern</label> <input ng-required=true name=relationType ng-model=configuration.relationType> <div ng-messages=createRelationConfigForm.relationType.$error> <div ng-message=required translate>tb.rulenode.relation-type-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.relation-type-pattern-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.entity-cache-expiration</label> <input type=number step=1 min=0 ng-required=true name=entityCacheExpiration ng-model=configuration.entityCacheExpiration> <div ng-messages=deleteRelationConfigForm.entityCacheExpiration.$error> <div translate ng-message=required>tb.rulenode.entity-cache-expiration-required</div> <div translate ng-message=min>tb.rulenode.entity-cache-expiration-range</div> </div> <div class=tb-hint translate>tb.rulenode.entity-cache-expiration-hint</div> </md-input-container> </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=geoActionConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.latitude-key-name</label> <input ng-required=true name=latitudeKeyName ng-model=configuration.latitudeKeyName> <div ng-messages=geoActionConfigForm.latitudeKeyName.$error> <div ng-message=required translate>tb.rulenode.latitude-key-name-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.longitude-key-name</label> <input ng-required=true name=longitudeKeyName ng-model=configuration.longitudeKeyName> <div ng-messages=geoActionConfigForm.longitudeKeyName.$error> <div ng-message=required translate>tb.rulenode.longitude-key-name-required</div> </div> </md-input-container> <md-checkbox flex aria-label="{{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}" ng-model=configuration.fetchPerimeterInfoFromMessageMetadata>{{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }} </md-checkbox> <div layout=row class=tb-entity-select ng-if="configuration.fetchPerimeterInfoFromMessageMetadata === false"> <md-input-container class=md-block flex=100> <label translate>tb.rulenode.perimeter-type</label> <md-select required ng-model=configuration.perimeterType flex> <md-option ng-repeat="type in ruleNodeTypes.perimeterType" ng-value=type.value> {{ type.name | translate}} </md-option> </md-select> </md-input-container> </div> <div layout=row layout-wrap ng-if="configuration.perimeterType===ruleNodeTypes.perimeterType.CIRCLE.value && configuration.fetchPerimeterInfoFromMessageMetadata === false"> <div layout=column flex=50> <md-input-container class=md-block flex layout=column style=margin-top:44px> <label translate>tb.rulenode.circle-center-latitude</label> <input type=number min=0 step=0.1 ng-required=true name=centerLatitude ng-model=configuration.centerLatitude> <div ng-messages=geoActionConfigForm.centerLatitude.$error> <div ng-message=required translate>tb.rulenode.circle-center-latitude-required</div> </div> </md-input-container> </div> <div layout=column flex=50> <md-input-container class=md-block flex style=margin-top:44px> <label translate>tb.rulenode.circle-center-longitude</label> <input type=number min=0 step=0.1 ng-required=true name=centerLongitude ng-model=configuration.centerLongitude> <div ng-messages=geoActionConfigForm.centerLongitude.$error> <div ng-message=required translate>tb.rulenode.circle-center-longitude-required</div> </div> </md-input-container> </div> <div layout=column flex=50> <md-input-container class=md-block style=margin-top:28px> <label translate>tb.rulenode.range</label> <input type=number min=0 step=0.1 ng-required=true name=range ng-model=configuration.range> <div ng-messages=geoActionConfigForm.range.$error> <div ng-message=required translate>tb.rulenode.range-required</div> </div> </md-input-container> </div> <div layout=column flex=50> <md-input-container class=md-block style=margin-top:28px> <label translate>tb.rulenode.range-units</label> <md-select required ng-model=configuration.rangeUnit> <md-option ng-repeat="type in ruleNodeTypes.rangeUnit" ng-value=type.value> {{ type.name | translate}} </md-option> </md-select> </md-input-container> </div> </div> <div layout=row layout-wrap ng-if="configuration.perimeterType===ruleNodeTypes.perimeterType.POLYGON.value && configuration.fetchPerimeterInfoFromMessageMetadata === false"> <div layout=column flex=100> <md-input-container class=md-block style=margin-top:44px> <label translate>tb.rulenode.polygon-definition</label> <input ng-required=true name=polygonsDefinition ng-model=configuration.polygonsDefinition> <div ng-messages=geoActionConfigForm.polygonsDefinition.$error> <div ng-message=required translate>tb.rulenode.polygon-definition-required</div> </div> <div class=tb-hint style=margin-top:5px translate>tb.rulenode.polygon-definition-hint</div> </md-input-container> </div> </div> <div layout=column layout-gt-sm=row> <md-input-container flex class="md-block tb-time-value" flex> <label translate class="tb-title no-padding">tb.rulenode.min-inside-duration</label> <input required type=number step=1 min=1 max=2147483647 name=minInsideDuration ng-model=configuration.minInsideDuration> <div ng-messages=geoActionConfigForm.minInsideDuration.$error> <div translate ng-message=required>tb.rulenode.min-inside-duration-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" flex> <label translate class="tb-title no-padding">tb.rulenode.min-inside-duration-time-unit</label> <md-select required name=minInsideDurationTimeUnit aria-label="{{ \'tb.rulenode.min-inside-duration-time-unit\' | translate }}" ng-model=configuration.minInsideDurationTimeUnit> <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" flex> <label translate class="tb-title no-padding">tb.rulenode.min-outside-duration</label> <input required type=number step=1 min=1 max=2147483647 name=minOutsideDuration ng-model=configuration.minOutsideDuration> <div ng-messages=geoActionConfigForm.minOutsideDuration.$error> <div translate ng-message=required>tb.rulenode.min-outside-duration-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" flex> <label translate class="tb-title no-padding">tb.rulenode.min-outside-duration-time-unit</label> <md-select required name=minOutsideDurationTimeUnit aria-label="{{ \'tb.rulenode.min-outside-duration-time-unit\' | translate }}" ng-model=configuration.minOutsideDurationTimeUnit> <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 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=msgCountConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.interval-seconds</label> <input ng-required=true type=number step=1 name=interval ng-model=configuration.interval min=1> <div ng-messages=msgCountConfigForm.interval.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.interval-seconds-required</div> <div ng-message=min translate>tb.rulenode.min-interval-seconds-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.output-timeseries-key-prefix</label> <input ng-required=true name=telemetryPrefix ng-model=configuration.telemetryPrefix> <div ng-messages=msgCountConfigForm.telemetryPrefix.$error> <div translate ng-message=required>tb.rulenode.output-timeseries-key-prefix-required</div> </div> </md-input-container> </section> ";  
2 -},function(e,t){e.exports=' <section ng-form name=msgDelayConfigForm layout=column> <md-checkbox aria-label="{{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }}" ng-model=configuration.useMetadataPeriodInSecondsPatterns>{{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }} </md-checkbox> <div class=tb-hint translate>tb.rulenode.use-metadata-period-in-seconds-patterns-hint</div> <md-input-container class=md-block ng-if="configuration.useMetadataPeriodInSecondsPatterns === undefined || configuration.useMetadataPeriodInSecondsPatterns == false"> <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 flex ng-if="configuration.useMetadataPeriodInSecondsPatterns === true"> <label translate>tb.rulenode.period-in-seconds-pattern</label> <input ng-required=true name=periodInSecondsPattern ng-model=configuration.periodInSecondsPattern> <div ng-messages=msgDelayConfigForm.periodInSecondsPattern.$error> <div ng-message=required translate>tb.rulenode.period-in-seconds-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.period-in-seconds-pattern-hint</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=pubsubConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.gcp-project-id</label> <input ng-required=true name=projectId ng-model=configuration.projectId> <div ng-messages=pubsubConfigForm.projectId.$error> <div ng-message=required translate>tb.rulenode.gcp-project-id-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.pubsub-topic-name</label> <input ng-required=true name=topicName ng-model=configuration.topicName> <div ng-messages=pubsubConfigForm.topicName.$error> <div ng-message=required translate>tb.rulenode.pubsub-topic-name-required</div> </div> </md-input-container> <div class=tb-container ng-class=\"configuration.serviceAccountKeyFileName ? 'ng-valid' : 'ng-invalid'\"> <label class=tb-label translate>tb.rulenode.gcp-service-account-key</label> <div flow-init={singleFile:true} flow-file-added=serviceAccountFileAdded($file) class=tb-file-select-container> <div class=tb-file-clear-container> <md-button ng-click=clearServiceAccountFile() 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=serviceAccountKeySelect translate>tb.rulenode.drop-file</label> <input class=file-input flow-btn id=serviceAccountKeySelect> </div> </div> </div> <div class=dropdown-messages> <div ng-if=!configuration.serviceAccountKeyFileName class=tb-error-message translate>tb.rulenode.no-file</div> <div ng-if=configuration.serviceAccountKeyFileName>{{configuration.serviceAccountKeyFileName}}</div> </div> <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> </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> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}" ng-model=configuration.useSimpleClientHttpFactory> {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }} </md-checkbox> <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 layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.custom-table-name</label> <input ng-required=true name=tableName ng-model=configuration.tableName> <div ng-messages=saveToCustomTableConfigForm.tableName.$error> <div ng-message=required translate>tb.rulenode.custom-table-name-required</div> </div> <div class=tb-hint style=margin-top:5px translate>tb.rulenode.custom-table-hint</div> </md-input-container> <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.message-field\'" key-required-text="\'tb.rulenode.message-field-required\'" val-text="\'tb.rulenode.table-col\'" val-required-text="\'tb.rulenode.table-col-required\'"> </tb-kv-map-config> </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 ng-form name=unAssignCustomerConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.customer-name-pattern</label> <input ng-required=true name=customerNamePattern ng-model=configuration.customerNamePattern> <div ng-messages=unAssignCustomerConfigForm.customerNamePattern.$error> <div ng-message=required translate>tb.rulenode.customer-name-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.customer-name-pattern-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.customer-cache-expiration</label> <input type=number step=1 min=0 ng-required=true name=customerCacheExpiration ng-model=configuration.customerCacheExpiration> <div ng-messages=unAssignCustomerConfigForm.customerCacheExpiration.$error> <div translate ng-message=required>tb.rulenode.customer-cache-expiration-required</div> <div translate ng-message=min>tb.rulenode.customer-cache-expiration-range</div> </div> <div class=tb-hint translate>tb.rulenode.customer-cache-expiration-hint</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> <md-checkbox aria-label="{{ \'tb.rulenode.tell-failure-if-absent\' | translate }}" ng-model=configuration.tellFailureIfAbsent> {{ \'tb.rulenode.tell-failure-if-absent\' | translate }} </md-checkbox> <div class=tb-hint translate>tb.rulenode.tell-failure-if-absent-hint</div> <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-add-on-blur=true> </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-add-on-blur=true> </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-add-on-blur=true> </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-add-on-blur=true> </md-chips> </section> '},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding" class=required>tb.rulenode.entity-details</label> <md-chips readonly=disabled style=margin-bottom:28px id=entityDetailsListChips ng-required=tbRequired ng-model=configuration.detailsList placeholder={{placeholder}} secondary-placeholder={{secondaryPlaceholder}} md-autocomplete-snap md-require-match=true> <md-autocomplete md-no-cache=true id=entityDetails md-selected-item=selectedEntityDetail md-selected-item-change=selectedItemChange(item) md-search-text=entityDetailsSearchText md-items="item in entityDetailsList" md-item-text=item md-min-length=0 placeholder="{{ (!ruleNodeTypes.entityDetails || !ruleNodeTypes.entityDetails.length) ? placeholder : secondaryPlaceholder }}"> <md-item-template> <span md-highlight-text=entityDetailsSearchText md-highlight-flags=^i> {{\'tb.rulenode.entity-details-\'+item.toLowerCase() | translate}} </span> </md-item-template> <md-not-found> <span translate translate-values="{ entityDetails: entityDetailsSearchText }">tb.rulenode.no-entity-details-matching</span> </md-not-found> </md-autocomplete> <md-chip-template> <span> <strong>{{\'tb.rulenode.entity-details-\'+$chip.toLowerCase() | translate}}</strong> </span> </md-chip-template> </md-chips> <div class=tb-error-messages ng-messages=ngModelCtrl.$error ng-if="inputTouched && tbRequired" role=alert> <div translate ng-message=configuration.detailsList class=tb-error-message>tb.rulenode.entity-details-list-empty</div> </div> <md-checkbox aria-label="{{ \'tb.rulenode.add-to-metadata\' | translate }}" ng-model=configuration.addToMetadata> {{ \'tb.rulenode.add-to-metadata\' | translate }} </md-checkbox> <div class=tb-hint translate>tb.rulenode.add-to-metadata-hint</div> </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:18px;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 class=tb-hint translate>tb.rulenode.fetch-mode-hint</div> <md-input-container flex ng-if="configuration.fetchMode === \'ALL\' "> <label translate class="tb-title no-padding">tb.rulenode.order-by</label> <md-select required ng-model=configuration.orderBy> <md-option ng-repeat="type in ruleNodeTypes.samplingOrder" ng-value=type> {{ type }} </md-option> </md-select> </md-input-container> <div class=tb-hint translate flex ng-if="configuration.fetchMode === \'ALL\' ">tb.rulenode.order-by-hint</div> <md-checkbox aria-label="{{ \'tb.rulenode.use-metadata-interval-patterns\' | translate }}" ng-model=configuration.useMetadataIntervalPatterns>{{ \'tb.rulenode.use-metadata-interval-patterns\' | translate }} </md-checkbox> <div class=tb-hint translate>tb.rulenode.use-metadata-interval-patterns-hint</div> <div layout=column layout-gt-sm=row> <md-input-container flex class="md-block tb-time-value" flex ng-if="configuration.useMetadataIntervalPatterns == false"> <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" flex ng-if="configuration.useMetadataIntervalPatterns == false "> <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" flex ng-if="configuration.useMetadataIntervalPatterns == false"> <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" flex ng-if="configuration.useMetadataIntervalPatterns === false"> <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> <md-input-container class=md-block flex ng-if="configuration.useMetadataIntervalPatterns === true" style=margin-top:38px> <label translate>tb.rulenode.start-interval-pattern</label> <input ng-required=true name=startIntervalPattern ng-model=configuration.startIntervalPattern> <div ng-messages=getTelemetryConfigForm.startIntervalPattern.$error> <div ng-message=required translate>tb.rulenode.start-interval-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.start-interval-pattern-hint</div> </md-input-container> <md-input-container class=md-block flex ng-if="configuration.useMetadataIntervalPatterns === true"> <label translate>tb.rulenode.end-interval-pattern</label> <input ng-required=true name=endIntervalPattern ng-model=configuration.endIntervalPattern> <div ng-messages=getTelemetryConfigForm.endIntervalPattern.$error> <div ng-message=required translate>tb.rulenode.end-interval-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.end-interval-pattern-hint</div> </md-input-container> </section>'},function(e,t){e.exports=' <section layout=column> <md-checkbox aria-label="{{ \'tb.rulenode.tell-failure-if-absent\' | translate }}" ng-model=configuration.tellFailureIfAbsent> {{ \'tb.rulenode.tell-failure-if-absent\' | translate }} </md-checkbox> <div class=tb-hint translate>tb.rulenode.tell-failure-if-absent-hint</div> <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-add-on-blur=true> </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-add-on-blur=true> </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-add-on-blur=true> </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-add-on-blur=true> </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> ';  
3 -},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> "},31,function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding tb-required">tb.rulenode.data-keys</label> <md-chips style=padding-bottom:15px ng-required=!(configuration.metadataNames).length readonly=readonly ng-model=configuration.messageNames placeholder="{{\'tb.rulenode.data-keys\' | translate}}" md-separator-keys=separatorKeys md-add-on-blur=true> </md-chips> <div class=tb-hint translate>tb.rulenode.separator-hint</div> <label translate class="tb-title no-padding tb-required">tb.rulenode.metadata-keys</label> <md-chips style=padding-bottom:15px ng-required=!(configuration.messageNames).length readonly=readonly ng-model=configuration.metadataNames placeholder="{{\'tb.rulenode.metadata-keys\' | translate}}" md-separator-keys=separatorKeys md-add-on-blur=true> </md-chips> <div class=tb-hint translate>tb.rulenode.separator-hint</div> <md-checkbox aria-label="{{ \'tb.rulenode.check-all-keys\' | translate }}" ng-model=configuration.checkAllKeys>{{ \'tb.rulenode.check-all-keys\' | translate }} </md-checkbox> <div class=tb-hint translate>tb.rulenode.check-all-keys-hint</div> </section> '},function(e,t){e.exports=" <section ng-form name=checkRelationConfigForm> <md-checkbox aria-label=\"{{ 'tb.rulenode.check-relation-to-specific-entity' | translate }}\" ng-model=configuration.checkForSingleEntity> {{ 'tb.rulenode.check-relation-to-specific-entity' | translate }} </md-checkbox> <div class=tb-hint translate>tb.rulenode.check-relation-hint</div> <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 ng-if=configuration.checkForSingleEntity style=padding-top:20px> <tb-entity-type-select style=min-width:100px;padding-bottom:20px 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 ng-form name=geoFilterConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.latitude-key-name</label> <input ng-required=true name=latitudeKeyName ng-model=configuration.latitudeKeyName> <div ng-messages=geoFilterConfigForm.latitudeKeyName.$error> <div ng-message=required translate>tb.rulenode.latitude-key-name-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.longitude-key-name</label> <input ng-required=true name=longitudeKeyName ng-model=configuration.longitudeKeyName> <div ng-messages=geoFilterConfigForm.longitudeKeyName.$error> <div ng-message=required translate>tb.rulenode.longitude-key-name-required</div> </div> </md-input-container> <md-checkbox flex aria-label="{{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}" ng-model=configuration.fetchPerimeterInfoFromMessageMetadata>{{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }} </md-checkbox> <div layout=row class=tb-entity-select ng-if="configuration.fetchPerimeterInfoFromMessageMetadata === false"> <md-input-container class=md-block flex=100> <label translate>tb.rulenode.perimeter-type</label> <md-select required ng-model=configuration.perimeterType flex> <md-option ng-repeat="type in ruleNodeTypes.perimeterType" ng-value=type.value> {{ type.name | translate}} </md-option> </md-select> </md-input-container> </div> <div layout=row layout-wrap ng-if="configuration.perimeterType === ruleNodeTypes.perimeterType.CIRCLE.value && configuration.fetchPerimeterInfoFromMessageMetadata === false"> <div layout=column flex=50> <md-input-container class=md-block flex layout=column style=margin-top:44px> <label translate>tb.rulenode.circle-center-latitude</label> <input type=number min=0 step=0.1 ng-required=true name=centerLatitude ng-model=configuration.centerLatitude> <div ng-messages=geoFilterConfigForm.centerLatitude.$error> <div ng-message=required translate>tb.rulenode.circle-center-latitude-required</div> </div> </md-input-container> </div> <div layout=column flex=50> <md-input-container class=md-block flex style=margin-top:44px> <label translate>tb.rulenode.circle-center-longitude</label> <input type=number min=0 step=0.1 ng-required=true name=centerLongitude ng-model=configuration.centerLongitude> <div ng-messages=geoFilterConfigForm.centerLongitude.$error> <div ng-message=required translate>tb.rulenode.circle-center-longitude-required</div> </div> </md-input-container> </div> <div layout=column flex=50> <md-input-container class=md-block style=margin-top:28px> <label translate>tb.rulenode.range</label> <input type=number min=0 step=0.1 ng-required=true name=range ng-model=configuration.range> <div ng-messages=geoFilterConfigForm.range.$error> <div ng-message=required translate>tb.rulenode.range-required</div> </div> </md-input-container> </div> <div layout=column flex=50> <md-input-container class=md-block style=margin-top:28px> <label translate>tb.rulenode.range-units</label> <md-select required ng-model=configuration.rangeUnit> <md-option ng-repeat="type in ruleNodeTypes.rangeUnit" ng-value=type.value> {{ type.name | translate}} </md-option> </md-select> </md-input-container> </div> </div> <div layout=row layout-wrap ng-if="configuration.perimeterType === ruleNodeTypes.perimeterType.POLYGON.value && configuration.fetchPerimeterInfoFromMessageMetadata === false"> <div layout=column flex=100> <md-input-container class=md-block style=margin-top:44px> <label translate>tb.rulenode.polygon-definition</label> <input ng-required=true name=polygonsDefinition ng-model=configuration.polygonsDefinition> <div ng-messages=geoFilterConfigForm.polygonsDefinition.$error> <div ng-message=required translate>tb.rulenode.polygon-definition-required</div> </div> <div class=tb-hint style=margin-top:5px translate>tb.rulenode.polygon-definition-hint</div> </md-input-container> </div> </div> </section> '},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding" ng-class="{\'tb-required\': required}">tb.rulenode.message-types-filter</label> <md-chips id=message_type_chips ng-required=required readonly=readonly ng-model=messageTypes md-autocomplete-snap md-transform-chip=transformMessageTypeChip($chip) md-require-match=false> <md-autocomplete id=message_type md-no-cache=true md-selected-item=selectedMessageType md-search-text=messageTypeSearchText md-items="item in messageTypesSearch(messageTypeSearchText)" md-item-text=item.name md-min-length=0 placeholder="{{\'tb.rulenode.message-type\' | translate }}" md-menu-class=tb-message-type-autocomplete> <span md-highlight-text=messageTypeSearchText md-highlight-flags=^i>{{item}}</span> <md-not-found> <div class=tb-not-found> <div class=tb-no-entries ng-if="!messageTypeSearchText || !messageTypeSearchText.length"> <span translate>tb.rulenode.no-message-types-found</span> </div> <div ng-if="messageTypeSearchText && messageTypeSearchText.length"> <span translate translate-values=\'{ messageType: "{{messageTypeSearchText | truncate:true:6:&apos;...&apos;}}" }\'>tb.rulenode.no-message-type-matching</span> <span> <a translate ng-click="createMessageType($event, \'#message_type_chips\')">tb.rulenode.create-new-message-type</a> </span> </div> </div> </md-not-found> </md-autocomplete> <md-chip-template> <span>{{$chip.name}}</span> </md-chip-template> </md-chips> <div class=tb-error-messages ng-messages=ngModelCtrl.$error role=alert> <div translate ng-message=messageTypes class=tb-error-message>tb.rulenode.message-types-required</div> </div> </section>'},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding" class=required>tb.rulenode.originator-types-filter</label> <tb-entity-type-list flex ng-model=configuration.originatorTypes allowed-entity-types=allowedEntityTypes ignore-authority-filter=true tb-required=true> </tb-entity-type-list> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.filter</label> <tb-js-func ng-model=configuration.jsScript function-name=Filter function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-filter-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.switch</label> <tb-js-func ng-model=configuration.jsScript function-name=Switch function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-switch-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=' <section class=tb-kv-map-config layout=column> <div class=header flex layout=row> <span class=cell flex translate>{{ keyText }}</span> <span class=cell flex translate>{{ valText }}</span> <span ng-show=!disabled style=width:52px>&nbsp</span> </div> <div class=body> <div class=row ng-form name=kvForm flex layout=row layout-align="start center" ng-repeat="keyVal in kvList track by $index"> <md-input-container class="cell md-block" flex md-no-float> <input placeholder="{{ keyText | translate }}" ng-required=true name=key ng-model=keyVal.key> <div ng-messages=kvForm.key.$error> <div translate ng-message=required>{{keyRequiredText}}</div> </div> </md-input-container> <md-input-container class="cell md-block" flex md-no-float> <input placeholder="{{ valText | translate }}" ng-required=true name=value ng-model=keyVal.value> <div ng-messages=kvForm.value.$error> <div translate ng-message=required>{{valRequiredText}}</div> </div> </md-input-container> <md-button ng-show=!disabled ng-disabled=loading class="md-icon-button md-primary" ng-click=removeKeyVal($index) aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'tb.key-val.remove-entry\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.delete\' | translate }}" class=material-icons> close </md-icon> </md-button> </div> </div> <div class=tb-error-messages ng-messages=ngModelCtrl.$error role=alert> <div translate ng-message=kvMap class=tb-error-message>{{requiredText}}</div> </div> <div> <md-button ng-show=!disabled ng-disabled=loading class="md-primary md-raised" ng-click=addKeyVal() aria-label="{{ \'action.add\' | translate }}"> <md-tooltip md-direction=top> {{ \'tb.key-val.add-entry\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.add\' | translate }}" class=material-icons> add </md-icon> {{ \'action.add\' | translate }} </md-button> </div> </section> '},function(e,t){e.exports=" <section layout=column> <div layout=row> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=query.direction> <md-option ng-repeat=\"direction in types.entitySearchDirection\" ng-value=direction> {{ ('relation.search-direction.' + direction) | translate}} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.max-relation-level</label> <input name=maxRelationLevel type=number min=1 step=1 placeholder=\"{{ 'tb.rulenode.unlimited-level' | translate }}\" ng-model=query.maxLevel aria-label=\"{{ 'tb.rulenode.max-relation-level' | translate }}\"> </md-input-container> </div> <div class=md-caption style=padding-bottom:10px;color:rgba(0,0,0,.57) translate>relation.relation-filters</div> <tb-relation-filters ng-model=query.filters> </tb-relation-filters> </section> "},function(e,t){e.exports=' <section layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.originator-source</label> <md-select required ng-model=configuration.originatorSource> <md-option ng-repeat="source in ruleNodeTypes.originatorSource" ng-value=source.value> {{ source.name | translate}} </md-option> </md-select> </md-input-container> <section layout=column ng-if="configuration.originatorSource == ruleNodeTypes.originatorSource.RELATED.value"> <label translate class="tb-title tb-required">tb.rulenode.relations-query</label> <tb-relations-query-config style=padding-bottom:15px ng-model=configuration.relationsQuery> </tb-relations-query-config> </section> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.transform</label> <tb-js-func ng-model=configuration.jsScript function-name=Transform function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row style=padding-bottom:15px> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-transformer-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=" <section ng-form name=toEmailConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.from-template</label> <textarea ng-required=true name=fromTemplate ng-model=configuration.fromTemplate rows=2></textarea> <div ng-messages=toEmailConfigForm.fromTemplate.$error> <div ng-message=required translate>tb.rulenode.from-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.from-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.to-template</label> <textarea ng-required=true name=toTemplate ng-model=configuration.toTemplate rows=2></textarea> <div ng-messages=toEmailConfigForm.toTemplate.$error> <div ng-message=required translate>tb.rulenode.to-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.mail-address-list-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.cc-template</label> <textarea name=ccTemplate ng-model=configuration.ccTemplate rows=2></textarea> <div class=tb-hint translate>tb.rulenode.mail-address-list-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.bcc-template</label> <textarea name=ccTemplate ng-model=configuration.bccTemplate rows=2></textarea> <div class=tb-hint translate>tb.rulenode.mail-address-list-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.subject-template</label> <textarea ng-required=true name=subjectTemplate ng-model=configuration.subjectTemplate rows=2></textarea> <div ng-messages=toEmailConfigForm.subjectTemplate.$error> <div ng-message=required translate>tb.rulenode.subject-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.subject-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.body-template</label> <textarea ng-required=true name=bodyTemplate ng-model=configuration.bodyTemplate rows=6></textarea> <div ng-messages=toEmailConfigForm.bodyTemplate.$error> <div ng-message=required translate>tb.rulenode.body-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.body-template-hint</div> </md-input-container> </section> "},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(6),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(7),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n,i){var a=function(a,r,l,s){var d=o.default;r.html(d),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);i.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(r.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 r=n(8),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n,i){var a=function(a,r,l,s){var d=o.default;r.html(d),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);i.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(r.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 r=n(9),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(10),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(11),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n,i){var a=function(a,r,l,s){var d=o.default;r.html(d),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);i.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(r.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 r=n(12),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.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 r=n(13),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(74),r=i(a),o=n(52),l=i(o),s=n(57),d=i(s),u=n(54),c=i(u),m=n(53),g=i(m),p=n(61),f=i(p),b=n(68),v=i(b),y=n(69),h=i(y),q=n(67),$=i(q),x=n(60),k=i(x),T=n(72),C=i(T),w=n(73),M=i(w),N=n(66),_=i(N),S=n(62),E=i(S),F=n(71),P=i(F),A=n(64),V=i(A),I=n(63),j=i(I),O=n(51),D=i(O),R=n(75),K=i(R),L=n(56),U=i(L),z=n(55),B=i(z),H=n(70),G=i(H),Y=n(58),Q=i(Y),J=n(65),W=i(J);t.default=angular.module("thingsboard.ruleChain.config.action",[]).directive("tbActionNodeTimeseriesConfig",r.default).directive("tbActionNodeAttributesConfig",l.default).directive("tbActionNodeGeneratorConfig",d.default).directive("tbActionNodeCreateAlarmConfig",c.default).directive("tbActionNodeClearAlarmConfig",g.default).directive("tbActionNodeLogConfig",f.default).directive("tbActionNodeRpcReplyConfig",v.default).directive("tbActionNodeRpcRequestConfig",h.default).directive("tbActionNodeRestApiCallConfig",$.default).directive("tbActionNodeKafkaConfig",k.default).directive("tbActionNodeSnsConfig",C.default).directive("tbActionNodeSqsConfig",M.default).directive("tbActionNodeRabbitMqConfig",_.default).directive("tbActionNodeMqttConfig",E.default).directive("tbActionNodeSendEmailConfig",P.default).directive("tbActionNodeMsgDelayConfig",V.default).directive("tbActionNodeMsgCountConfig",j.default).directive("tbActionNodeAssignToCustomerConfig",D.default).directive("tbActionNodeUnAssignToCustomerConfig",K.default).directive("tbActionNodeDeleteRelationConfig",U.default).directive("tbActionNodeCreateRelationConfig",B.default).directive("tbActionNodeCustomTableConfig",G.default).directive("tbActionNodeGpsGeofencingConfig",Q.default).directive("tbActionNodePubSubConfig",W.default).name},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),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 r=n(14),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){var s=o.default;a.html(s),i.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(i.configuration)}),l.$render=function(){i.configuration=l.$viewValue},i.testScript=function(e){var a=angular.copy(i.configuration.jsScript);n.testNodeScript(e,a,"string",t.instant("tb.rulenode.to-string")+"","ToString",["msg","metadata","msgType"],i.ruleNodeId).then(function(e){i.configuration.jsScript=e,l.$setDirty()})},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}a.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(15),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){var s=o.default;a.html(s),i.$mdExpansionPanel=t,i.ruleNodeTypes=n,i.credentialsTypeChanged=function(){var e=i.configuration.credentials.type;i.configuration.credentials={},i.configuration.credentials.type=e,i.updateValidity()},i.certFileAdded=function(e,t){var n=new FileReader;n.onload=function(n){i.$apply(function(){if(n.target.result){l.$setDirty();var a=n.target.result;a&&a.length>0&&("caCert"==t&&(i.configuration.credentials.caCertFileName=e.name,i.configuration.credentials.caCert=a),"privateKey"==t&&(i.configuration.credentials.privateKeyFileName=e.name,i.configuration.credentials.privateKey=a),"Cert"==t&&(i.configuration.credentials.certFileName=e.name,i.configuration.credentials.cert=a)),i.updateValidity()}})},n.readAsText(e.file)},i.clearCertFile=function(e){l.$setDirty(),"caCert"==e&&(i.configuration.credentials.caCertFileName=null,i.configuration.credentials.caCert=null),"privateKey"==e&&(i.configuration.credentials.privateKeyFileName=null,i.configuration.credentials.privateKey=null),"Cert"==e&&(i.configuration.credentials.certFileName=null,i.configuration.credentials.cert=null),i.updateValidity()},i.updateValidity=function(){var e=!0,t=i.configuration.credentials;t.type==n.mqttCredentialTypes["cert.PEM"].value&&(t.caCert&&t.cert&&t.privateKey||(e=!1)),l.$setValidity("Certs",e)},i.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(i.configuration)}),l.$render=function(){i.configuration=l.$viewValue},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:i}}a.$inject=["$compile","$mdExpansionPanel","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a,n(2);var r=n(16),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),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 r=n(17),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),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 r=n(18),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.serviceAccountFileAdded=function(e){var t=new FileReader;t.onload=function(t){n.$apply(function(){if(t.target.result){r.$setDirty();var i=t.target.result;i&&i.length>0&&(n.configuration.serviceAccountKeyFileName=e.name,n.configuration.serviceAccountKey=i),n.updateValidity()}})},t.readAsText(e.file)},n.clearServiceAccountFile=function(){r.$setDirty(),n.configuration.serviceAccountKeyFileName=null,n.configuration.serviceAccountKey=null,n.updateValidity()},n.updateValidity=function(){var e=!0,t=n.configuration;t.serviceAccountKeyFileName&&t.serviceAccountKey||(e=!1),r.$setValidity("SAKey",e)},n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.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 r=n(19),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),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 r=n(20),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.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 r=n(21),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),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 r=n(22),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),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 r=n(23),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),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 r=n(24),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),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"  
4 -},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(25),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),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 r=n(26),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.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 r=n(27),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),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 r=n(28),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(29),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||r.$setViewValue(n.query)}),r.$render=function(){n.query=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(30),o=i(r)},function(e,t){"use strict";function n(e){var t=function(t,n,i,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 i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),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 r=n(31),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.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)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(32),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),n.entityDetailsList=[];for(var s in t.entityDetails){var d=s;n.entityDetailsList.push(d)}r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(33),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){var s=o.default;a.html(s);var d=186;i.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,d],i.ruleNodeTypes=n,i.aggPeriodTimeUnits={},i.aggPeriodTimeUnits.MINUTES=n.timeUnit.MINUTES,i.aggPeriodTimeUnits.HOURS=n.timeUnit.HOURS,i.aggPeriodTimeUnits.DAYS=n.timeUnit.DAYS,i.aggPeriodTimeUnits.MILLISECONDS=n.timeUnit.MILLISECONDS,i.aggPeriodTimeUnits.SECONDS=n.timeUnit.SECONDS,i.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(i.configuration)}),l.$render=function(){i.configuration=l.$viewValue},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{},link:i}}a.$inject=["$compile","$mdConstant","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(34),o=i(r);n(3)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(83),r=i(a),o=n(84),l=i(o),s=n(79),d=i(s),u=n(85),c=i(u),m=n(78),g=i(m),p=n(86),f=i(p),b=n(81),v=i(b),y=n(80),h=i(y);t.default=angular.module("thingsboard.ruleChain.config.enrichment",[]).directive("tbEnrichmentNodeOriginatorAttributesConfig",r.default).directive("tbEnrichmentNodeOriginatorFieldsConfig",l.default).directive("tbEnrichmentNodeDeviceAttributesConfig",d.default).directive("tbEnrichmentNodeRelatedAttributesConfig",c.default).directive("tbEnrichmentNodeCustomerAttributesConfig",g.default).directive("tbEnrichmentNodeTenantAttributesConfig",f.default).directive("tbEnrichmentNodeGetTelemetryFromDatabase",v.default).directive("tbEnrichmentNodeEntityDetailsConfig",h.default).name},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.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)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(35),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),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 r=n(36),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),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 r=n(37),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),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 r=n(38),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.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)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(39),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(40),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.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 r=n(41),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(93),r=i(a),o=n(91),l=i(o),s=n(94),d=i(s),u=n(88),c=i(u),m=n(92),g=i(m),p=n(87),f=i(p),b=n(89),v=i(b);t.default=angular.module("thingsboard.ruleChain.config.filter",[]).directive("tbFilterNodeScriptConfig",r.default).directive("tbFilterNodeMessageTypeConfig",l.default).directive("tbFilterNodeSwitchConfig",d.default).directive("tbFilterNodeCheckRelationConfig",c.default).directive("tbFilterNodeOriginatorTypeConfig",g.default).directive("tbFilterNodeCheckMessageConfig",f.default).directive("tbFilterNodeGpsGeofencingConfig",v.default).name},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){function s(){if(l.$viewValue){for(var e=[],t=0;t<i.messageTypes.length;t++)e.push(i.messageTypes[t].value);l.$viewValue.messageTypes=e,d()}}function d(){if(i.required){var e=!(!l.$viewValue.messageTypes||!l.$viewValue.messageTypes.length);l.$setValidity("messageTypes",e)}else l.$setValidity("messageTypes",!0)}var u=o.default;a.html(u),i.selectedMessageType=null,i.messageTypeSearchText=null,i.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)}i.transformMessageTypeChip=function(e){var n,i=t("filter")(c,{name:e},!0);return n=i&&i.length?angular.copy(i[0]):{name:e,value:e}},i.messageTypesSearch=function(e){var n=e?t("filter")(c,{name:e}):c;return n.map(function(e){return e.name})},i.createMessageType=function(e,t){var n=angular.element(t,a)[0].firstElementChild,i=angular.element(n),r=i.scope().$mdChipsCtrl.getChipBuffer();e.preventDefault(),e.stopPropagation(),i.scope().$mdChipsCtrl.appendChip(r.trim()),i.scope().$mdChipsCtrl.resetChipBuffer()},l.$render=function(){i.messageTypesWatch&&(i.messageTypesWatch(),i.messageTypesWatch=null);var e=l.$viewValue,t=[];if(e&&e.messageTypes)for(var a=0;a<e.messageTypes.length;a++){var r=e.messageTypes[a];n.messageType[r]?t.push(angular.copy(n.messageType[r])):t.push({name:r,value:r})}i.messageTypes=t,i.messageTypesWatch=i.$watch("messageTypes",function(e,t){angular.equals(e,t)||s()},!0)},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",readonly:"=ngReadonly"},link:i}}a.$inject=["$compile","$filter","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a,n(4);var r=n(42),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.allowedEntityTypes=[t.entityType.device,t.entityType.asset,t.entityType.entityView,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)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(43),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){var s=o.default;a.html(s),i.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(i.configuration)}),l.$render=function(){i.configuration=l.$viewValue},i.testScript=function(e){var a=angular.copy(i.configuration.jsScript);n.testNodeScript(e,a,"filter",t.instant("tb.rulenode.filter")+"","Filter",["msg","metadata","msgType"],i.ruleNodeId).then(function(e){i.configuration.jsScript=e,l.$setDirty()})},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}a.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(44),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){var s=o.default;a.html(s),i.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(i.configuration)}),l.$render=function(){i.configuration=l.$viewValue},i.testScript=function(e){var a=angular.copy(i.configuration.jsScript);n.testNodeScript(e,a,"switch",t.instant("tb.rulenode.switch")+"","Switch",["msg","metadata","msgType"],i.ruleNodeId).then(function(e){i.configuration.jsScript=e,l.$setDirty()})},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}a.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(45),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){function r(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),d()}function d(){var e=!0;t.required&&!t.kvList.length&&(e=!1),a.$setValidity("kvMap",e)}var u=o.default;n.html(u),t.ngModelCtrl=a,t.removeKeyVal=r,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),d()},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 r=n(46),o=i(r);n(5)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||r.$setViewValue(n.query)}),r.$render=function(){n.query=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(47),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(48),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(97),r=i(a),o=n(99),l=i(o),s=n(100),d=i(s);t.default=angular.module("thingsboard.ruleChain.config.transform",[]).directive("tbTransformationNodeChangeOriginatorConfig",r.default).directive("tbTransformationNodeScriptConfig",l.default).directive("tbTransformationNodeToEmailConfig",d.default).name},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){var s=o.default;a.html(s),i.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(i.configuration)}),l.$render=function(){i.configuration=l.$viewValue},i.testScript=function(e){var a=angular.copy(i.configuration.jsScript);n.testNodeScript(e,a,"update",t.instant("tb.rulenode.transformer")+"","Transform",["msg","metadata","msgType"],i.ruleNodeId).then(function(e){i.configuration.jsScript=e,l.$setDirty()})},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}a.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(49),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),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 r=n(50),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(104),r=i(a),o=n(90),l=i(o),s=n(82),d=i(s),u=n(98),c=i(u),m=n(59),g=i(m),p=n(77),f=i(p),b=n(96),v=i(b),y=n(76),h=i(y),q=n(95),$=i(q),x=n(103),k=i(x);t.default=angular.module("thingsboard.ruleChain.config",[r.default,l.default,d.default,c.default,g.default]).directive("tbNodeEmptyConfig",f.default).directive("tbRelationsQueryConfig",v.default).directive("tbDeviceRelationsQueryConfig",h.default).directive("tbKvMapConfig",$.default).config(k.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{"create-entity-if-not-exists":"Create new entity if not exists","create-entity-if-not-exists-hint":"Create a new entity set above if it does not exist.","entity-name-pattern":"Name pattern","entity-name-pattern-required":"Name pattern is required","entity-name-pattern-hint":"Name pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","entity-type-pattern-hint":"Type pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.","customer-name-pattern":"Customer name pattern","customer-name-pattern-required":"Customer name pattern is required","create-customer-if-not-exists":"Create new customer if not exists","customer-cache-expiration":"Customers cache expiration time (sec)","customer-name-pattern-hint":"Customer name pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.","customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","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","fetch-mode-hint":"If selected fetch mode 'ALL' you able to choose telemetry sampling order.","order-by":"Order by","order-by-hint":"Select to choose telemetry sampling order.","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","data-keys":"Message data","metadata-keys":"Message metadata","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","relation-type-pattern":"Relation type pattern","relation-type-pattern-hint":"Relation type pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","relation-type-pattern-required":"Relation type pattern is required","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","originator-alarm-originator":"Alarm Originator","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.","use-metadata-period-in-seconds-patterns":"Use metadata period in seconds pattern","use-metadata-period-in-seconds-patterns-hint":"If selected, rule node use period in seconds interval pattern from message metadata assuming that intervals are in the seconds.","period-in-seconds-pattern":"Period in seconds metadata pattern","period-in-seconds-pattern-required":"Period in seconds pattern is required","period-in-seconds-pattern-hint":"Period in seconds pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","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","use-simple-client-http-factory":"Use simple client HTTP factory",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","gcp-project-id":"GCP project ID","gcp-project-id-required":"GCP project ID is required","gcp-service-account-key":"GCP service account key file","gcp-service-account-key-required":"GCP service account key file is required","pubsub-topic-name":"Topic name","pubsub-topic-name-required":"Topic name is required","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","use-metadata-interval-patterns":"Use metadata interval patterns","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","check-all-keys":"Check that all selected keys are present","check-all-keys-hint":"If selected, checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-to-specific-entity":"Delete relation to specific entity","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.",  
5 -"start-interval-pattern":"Start interval pattern","end-interval-pattern":"End interval pattern","start-interval-pattern-required":"Start interval pattern is required","end-interval-pattern-required":"End interval pattern is required","start-interval-pattern-hint":"Start interval pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","end-interval-pattern-hint":"End interval pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","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","interval-seconds":"Interval in seconds","interval-seconds-required":"Interval is required.","min-interval-seconds-message":"Only 1 second minimum interval is allowed.","output-timeseries-key-prefix":"Output timeseries key prefix","output-timeseries-key-prefix-required":"Output timeseries key prefix required.","separator-hint":'You should press "enter" to complete field input.',"entity-details":"Select entity details:","entity-details-country":"Country","entity-details-state":"State","entity-details-zip":"Zip","entity-details-address":"Address","entity-details-address2":"Address2","entity-details-additional_info":"Additional Info","entity-details-phone":"Phone","entity-details-email":"Email","add-to-metadata":"Add selected details to message metadata","add-to-metadata-hint":"If selected, adds the selected details keys to the message metadata instead of message data.","entity-details-list-empty":"No entity details selected.","no-entity-details-matching":"No entity details matching were found.","custom-table-name":"Custom table name","custom-table-name-required":"Table Name is required","custom-table-hint":"You should enter the table name without prefix 'cs_tb_'.","message-field":"Message field","message-field-required":"Message field is required.","table-col":"Table column","table-col-required":"Table column is required.","latitude-key-name":"Latitude key name","longitude-key-name":"Longitude key name","latitude-key-name-required":"Latitude key name is required.","longitude-key-name-required":"Longitude key name is required.","fetch-perimeter-info-from-message-metadata":"Fetch perimeter information from message metadata","perimeter-circle":"Circle","perimeter-polygon":"Polygon","perimeter-type":"Perimeter type","circle-center-latitude":"Center latitude","circle-center-latitude-required":"Center latitude is required.","circle-center-longitude":"Center longitude","circle-center-longitude-required":"Center longitude is required.","range-unit-meter":"Meter","range-unit-kilometer":"Kilometer","range-unit-foot":"Foot","range-unit-mile":"Mile","range-unit-nautical-mile":"Nautical mile","range-units":"Range units",range:"Range","range-required":"Range is required.","polygon-definition":"Polygon definition","polygon-definition-required":"Polygon definition is required.","polygon-definition-hint":"Please, use the following format for manual definition of polygon: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].","min-inside-duration":"Minimal inside duration","min-inside-duration-value-required":"Minimal inside duration is required","min-inside-duration-time-unit":"Minimal inside duration time unit","min-outside-duration":"Minimal outside duration","min-outside-duration-value-required":"Minimal outside duration is required","min-outside-duration-time-unit":"Minimal outside duration time unit","tell-failure-if-absent":"Tell Failure","tell-failure-if-absent-hint":'If at least one selected key doesn\'t exist the outbound message will report "Failure".'},"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 i(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 r=n(102),o=i(r)},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"},ALARM_ORIGINATOR:{name:"tb.rulenode.originator-alarm-originator",value:"ALARM_ORIGINATOR"}},fetchModeType:["FIRST","LAST","ALL"],samplingOrder:["ASC","DESC"],httpRequestType:["GET","POST","PUT","DELETE"],entityDetails:{COUNTRY:{name:"tb.rulenode.entity-details-country",value:"COUNTRY"},STATE:{name:"tb.rulenode.entity-details-state",value:"STATE"},ZIP:{name:"tb.rulenode.entity-details-zip",value:"ZIP"},ADDRESS:{name:"tb.rulenode.entity-details-address",value:"ADDRESS"},ADDRESS2:{name:"tb.rulenode.entity-details-address2",value:"ADDRESS2"},PHONE:{name:"tb.rulenode.entity-details-phone",value:"PHONE"},EMAIL:{name:"tb.rulenode.entity-details-email",value:"EMAIL"},ADDITIONAL_INFO:{name:"tb.rulenode.entity-details-additional_info",value:"ADDITIONAL_INFO"}},sqsQueueType:{STANDARD:{name:"tb.rulenode.sqs-queue-standard",value:"STANDARD"},FIFO:{name:"tb.rulenode.sqs-queue-fifo",value:"FIFO"}},perimeterType:{CIRCLE:{name:"tb.rulenode.perimeter-circle",value:"CIRCLE"},POLYGON:{name:"tb.rulenode.perimeter-polygon",value:"POLYGON"}},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"}},rangeUnit:{METER:{value:"METER",name:"tb.rulenode.range-unit-meter"},KILOMETER:{value:"KILOMETER",name:"tb.rulenode.range-unit-kilometer"},FOOT:{value:"FOOT",name:"tb.rulenode.range-unit-foot"},MILE:{value:"MILE",name:"tb.rulenode.range-unit-mile"},NAUTICAL_MILE:{value:"NAUTICAL_MILE",name:"tb.rulenode.range-unit-nautical-mile"}},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}]));  
6 -//# sourceMappingURL=rulenode-core-config.js.map  
  1 +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@angular/core"),require("@angular/common"),require("@ngx-translate/core"),require("@shared/public-api"),require("@ngrx/store"),require("@angular/forms")):"function"==typeof define&&define.amd?define("rulenode-core-config",["exports","@angular/core","@angular/common","@ngx-translate/core","@shared/public-api","@ngrx/store","@angular/forms"],t):t((e=e||self)["rulenode-core-config"]={},e.ng.core,e.ng.common,e["ngx-translate"],e.shared,e["ngrx-store"],e.ng.forms)}(this,(function(e,t,r,i,a,n,s){"use strict";
  2 +/*! *****************************************************************************
  3 + Copyright (c) Microsoft Corporation. All rights reserved.
  4 + Licensed under the Apache License, Version 2.0 (the "License"); you may not use
  5 + this file except in compliance with the License. You may obtain a copy of the
  6 + License at http://www.apache.org/licenses/LICENSE-2.0
  7 +
  8 + THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  9 + KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
  10 + WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
  11 + MERCHANTABLITY OR NON-INFRINGEMENT.
  12 +
  13 + See the Apache Version 2.0 License for specific language governing permissions
  14 + and limitations under the License.
  15 + ***************************************************************************** */var o=function(e,t){return(o=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r])})(e,t)};function u(e,t){function r(){this.constructor=e}o(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)}var d=function(e){function r(t){var r=e.call(this,t)||this;return r.store=t,r}return u(r,e),r.prototype.ngOnInit=function(){e.prototype.ngOnInit.call(this)},r.prototype.onConfigurationSet=function(e){},r.decorators=[{type:t.Component,args:[{selector:"tb-node-empty-config",template:"<div></div>"}]}],r.ctorParameters=function(){return[{type:n.Store}]},r}(a.RuleNodeConfigurationComponent);var l=function(e){function r(t,r){var i=e.call(this,t)||this;return i.store=t,i.fb=r,i.attributeScopes=Object.keys(a.AttributeScope),i.telemetryTypeTranslationsMap=a.telemetryTypeTranslations,i}return u(r,e),r.prototype.ngOnInit=function(){e.prototype.ngOnInit.call(this)},r.prototype.onConfigurationSet=function(e){var t=this;this.attributesConfigForm=this.fb.group({scope:[e?e.scope:null,[s.Validators.required]]}),this.attributesConfigForm.valueChanges.subscribe((function(e){t.attributesConfigForm.valid?t.notifyConfigurationUpdated(e):t.notifyConfigurationUpdated(null)}))},r.decorators=[{type:t.Component,args:[{selector:"tb-action-node-attributes-config",template:'<section [formGroup]="attributesConfigForm" fxLayout="column">\n <mat-form-field fxFlex class="mat-block">\n <mat-label translate>attribute.attributes-scope</mat-label>\n <mat-select formControlName="scope" required>\n <mat-option *ngFor="let scope of attributeScopes" [value]="scope">\n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n</section>\n'}]}],r.ctorParameters=function(){return[{type:n.Store},{type:s.FormBuilder}]},r}(a.RuleNodeConfigurationComponent);var m=function(e){function r(t,r){var i=e.call(this,t)||this;return i.store=t,i.fb=r,i}return u(r,e),r.prototype.ngOnInit=function(){e.prototype.ngOnInit.call(this)},r.prototype.onConfigurationSet=function(e){var t=this;this.timeseriesConfigForm=this.fb.group({defaultTTL:[e?e.defaultTTL:null,[s.Validators.required,s.Validators.min(0)]]}),this.timeseriesConfigForm.valueChanges.subscribe((function(e){t.timeseriesConfigForm.valid?t.notifyConfigurationUpdated(e):t.notifyConfigurationUpdated(null)}))},r.decorators=[{type:t.Component,args:[{selector:"tb-action-node-timeseries-config",template:'<section [formGroup]="timeseriesConfigForm" fxLayout="column">\n <mat-form-field fxFlex class="mat-block">\n <mat-label translate>tb.rulenode.default-ttl</mat-label>\n <input type="number" min="0" step="1" matInput formControlName="defaultTTL" required>\n <mat-error *ngIf="timeseriesConfigForm.get(\'defaultTTL\').hasError(\'required\')">\n {{ \'tb.rulenode.default-ttl-required\' | translate }}\n </mat-error>\n <mat-error *ngIf="timeseriesConfigForm.get(\'defaultTTL\').hasError(\'min\')">\n {{ \'tb.rulenode.min-default-ttl-message\' | translate }}\n </mat-error>\n </mat-form-field>\n</section>\n'}]}],r.ctorParameters=function(){return[{type:n.Store},{type:s.FormBuilder}]},r}(a.RuleNodeConfigurationComponent);var c=function(){function e(){}return e.decorators=[{type:t.NgModule,args:[{declarations:[l,m],imports:[r.CommonModule,a.SharedModule],exports:[l,m]}]}],e}(),p=function(){function e(e){!function(e){e.setTranslation("en_US",{tb:{rulenode:{"create-entity-if-not-exists":"Create new entity if not exists","create-entity-if-not-exists-hint":"Create a new entity set above if it does not exist.","entity-name-pattern":"Name pattern","entity-name-pattern-required":"Name pattern is required","entity-name-pattern-hint":"Name pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","entity-type-pattern-hint":"Type pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.","customer-name-pattern":"Customer name pattern","customer-name-pattern-required":"Customer name pattern is required","create-customer-if-not-exists":"Create new customer if not exists","customer-cache-expiration":"Customers cache expiration time (sec)","customer-name-pattern-hint":"Customer name pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.","customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","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","fetch-mode-hint":"If selected fetch mode 'ALL' you able to choose telemetry sampling order.","order-by":"Order by","order-by-hint":"Select to choose telemetry sampling order.","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","data-keys":"Message data","metadata-keys":"Message metadata","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","relation-type-pattern":"Relation type pattern","relation-type-pattern-hint":"Relation type pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","relation-type-pattern-required":"Relation type pattern is required","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","originator-alarm-originator":"Alarm Originator","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.","use-metadata-period-in-seconds-patterns":"Use metadata period in seconds pattern","use-metadata-period-in-seconds-patterns-hint":"If selected, rule node use period in seconds interval pattern from message metadata assuming that intervals are in the seconds.","period-in-seconds-pattern":"Period in seconds metadata pattern","period-in-seconds-pattern-required":"Period in seconds pattern is required","period-in-seconds-pattern-hint":"Period in seconds pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","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","use-simple-client-http-factory":"Use simple client HTTP factory",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","gcp-project-id":"GCP project ID","gcp-project-id-required":"GCP project ID is required","gcp-service-account-key":"GCP service account key file","gcp-service-account-key-required":"GCP service account key file is required","pubsub-topic-name":"Topic name","pubsub-topic-name-required":"Topic name is required","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","use-metadata-interval-patterns":"Use metadata interval patterns","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","check-all-keys":"Check that all selected keys are present","check-all-keys-hint":"If selected, checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-to-specific-entity":"Delete relation to specific entity","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.","start-interval-pattern":"Start interval pattern","end-interval-pattern":"End interval pattern","start-interval-pattern-required":"Start interval pattern is required","end-interval-pattern-required":"End interval pattern is required","start-interval-pattern-hint":"Start interval pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","end-interval-pattern-hint":"End interval pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","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","interval-seconds":"Interval in seconds","interval-seconds-required":"Interval is required.","min-interval-seconds-message":"Only 1 second minimum interval is allowed.","output-timeseries-key-prefix":"Output timeseries key prefix","output-timeseries-key-prefix-required":"Output timeseries key prefix required.","separator-hint":'You should press "enter" to complete field input.',"entity-details":"Select entity details:","entity-details-title":"Title","entity-details-country":"Country","entity-details-state":"State","entity-details-zip":"Zip","entity-details-address":"Address","entity-details-address2":"Address2","entity-details-additional_info":"Additional Info","entity-details-phone":"Phone","entity-details-email":"Email","add-to-metadata":"Add selected details to message metadata","add-to-metadata-hint":"If selected, adds the selected details keys to the message metadata instead of message data.","entity-details-list-empty":"No entity details selected.","no-entity-details-matching":"No entity details matching were found.","custom-table-name":"Custom table name","custom-table-name-required":"Table Name is required","custom-table-hint":"You should enter the table name without prefix 'cs_tb_'.","message-field":"Message field","message-field-required":"Message field is required.","table-col":"Table column","table-col-required":"Table column is required.","latitude-key-name":"Latitude key name","longitude-key-name":"Longitude key name","latitude-key-name-required":"Latitude key name is required.","longitude-key-name-required":"Longitude key name is required.","fetch-perimeter-info-from-message-metadata":"Fetch perimeter information from message metadata","perimeter-circle":"Circle","perimeter-polygon":"Polygon","perimeter-type":"Perimeter type","circle-center-latitude":"Center latitude","circle-center-latitude-required":"Center latitude is required.","circle-center-longitude":"Center longitude","circle-center-longitude-required":"Center longitude is required.","range-unit-meter":"Meter","range-unit-kilometer":"Kilometer","range-unit-foot":"Foot","range-unit-mile":"Mile","range-unit-nautical-mile":"Nautical mile","range-units":"Range units",range:"Range","range-required":"Range is required.","polygon-definition":"Polygon definition","polygon-definition-required":"Polygon definition is required.","polygon-definition-hint":"Please, use the following format for manual definition of polygon: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].","min-inside-duration":"Minimal inside duration","min-inside-duration-value-required":"Minimal inside duration is required","min-inside-duration-time-unit":"Minimal inside duration time unit","min-outside-duration":"Minimal outside duration","min-outside-duration-value-required":"Minimal outside duration is required","min-outside-duration-time-unit":"Minimal outside duration time unit","tell-failure-if-absent":"Tell Failure","tell-failure-if-absent-hint":'If at least one selected key doesn\'t exist the outbound message will report "Failure".'},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}},!0)}(e)}return e.decorators=[{type:t.NgModule,args:[{declarations:[d],imports:[r.CommonModule,a.SharedModule],exports:[c,d]}]}],e.ctorParameters=function(){return[{type:i.TranslateService}]},e}();e.RuleNodeCoreConfigModule=p,e.default=p,e.ɵa=d,e.ɵb=c,e.ɵc=l,e.ɵd=m,Object.defineProperty(e,"__esModule",{value:!0})}));
  16 +//# sourceMappingURL=rulenode-core-config.umd.min.js.map
@@ -78,7 +78,8 @@ @@ -78,7 +78,8 @@
78 "node_modules/ace-builds/src-min/snippets/css.js", 78 "node_modules/ace-builds/src-min/snippets/css.js",
79 "node_modules/ace-builds/src-min/snippets/json.js", 79 "node_modules/ace-builds/src-min/snippets/json.js",
80 "node_modules/ace-builds/src-min/snippets/java.js", 80 "node_modules/ace-builds/src-min/snippets/java.js",
81 - "node_modules/ace-builds/src-min/snippets/javascript.js" 81 + "node_modules/ace-builds/src-min/snippets/javascript.js",
  82 + "node_modules/systemjs/dist/system.js"
82 ], 83 ],
83 "es5BrowserSupport": true, 84 "es5BrowserSupport": true,
84 "customWebpackConfig": { 85 "customWebpackConfig": {
@@ -116,7 +117,7 @@ @@ -116,7 +117,7 @@
116 "builder": "@angular-builders/custom-webpack:dev-server", 117 "builder": "@angular-builders/custom-webpack:dev-server",
117 "options": { 118 "options": {
118 "browserTarget": "thingsboard:build", 119 "browserTarget": "thingsboard:build",
119 - "proxyConfig": "proxy.conf.json" 120 + "proxyConfig": "proxy.conf.js"
120 }, 121 },
121 "configurations": { 122 "configurations": {
122 "production": { 123 "production": {
@@ -12624,6 +12624,11 @@ @@ -12624,6 +12624,11 @@
12624 "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", 12624 "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==",
12625 "dev": true 12625 "dev": true
12626 }, 12626 },
  12627 + "systemjs": {
  12628 + "version": "0.21.5",
  12629 + "resolved": "https://registry.npmjs.org/systemjs/-/systemjs-0.21.5.tgz",
  12630 + "integrity": "sha512-GWzZhN/x7Fsae2CYkz2GF7OgOS+YDgKulcgd5L1kTogZHMKDrPx5T8zI8I0y5RoU9Dx78Z7j1XMfuFa1thD84A=="
  12631 + },
12627 "tapable": { 12632 "tapable": {
12628 "version": "1.1.3", 12633 "version": "1.1.3",
12629 "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", 12634 "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
@@ -74,6 +74,7 @@ @@ -74,6 +74,7 @@
74 "schema-inspector": "^1.6.8", 74 "schema-inspector": "^1.6.8",
75 "screenfull": "^5.0.0", 75 "screenfull": "^5.0.0",
76 "split.js": "^1.5.11", 76 "split.js": "^1.5.11",
  77 + "systemjs": "0.21.5",
77 "tinycolor2": "^1.4.1", 78 "tinycolor2": "^1.4.1",
78 "tooltipster": "^4.2.7", 79 "tooltipster": "^4.2.7",
79 "tslib": "^1.10.0", 80 "tslib": "^1.10.0",
  1 +/*
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +const ruleNodeUiforwardHost = 'localhost';
  18 +const ruleNodeUiforwardPort = 8080;
  19 +
  20 +const PROXY_CONFIG = {
  21 + '/api': {
  22 + 'target': 'http://localhost:8080',
  23 + 'secure': false
  24 + },
  25 + '/static/rulenode': {
  26 + 'target': `http://${ruleNodeUiforwardHost}:${ruleNodeUiforwardPort}`,
  27 + 'secure': false
  28 + },
  29 + '/api/ws': {
  30 + 'target': 'ws://localhost:8080',
  31 + 'ws': true
  32 + }
  33 +}
  34 +
  35 +module.exports = PROXY_CONFIG;
  1 +///
  2 +/// Copyright © 2016-2019 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +export * from './alias-controller';
  18 +export * from './data-aggregator';
  19 +export * from './datasource.service';
  20 +export * from './datasource-subcription';
  21 +export * from './widget-api.models';
  22 +export * from './widget-subscription';
@@ -25,6 +25,7 @@ import {WINDOW} from '@core/services/window.service'; @@ -25,6 +25,7 @@ import {WINDOW} from '@core/services/window.service';
25 import { ActivationEnd, NavigationEnd, Router } from '@angular/router'; 25 import { ActivationEnd, NavigationEnd, Router } from '@angular/router';
26 import { filter, map, publishReplay, refCount } from 'rxjs/operators'; 26 import { filter, map, publishReplay, refCount } from 'rxjs/operators';
27 27
  28 +// @dynamic
28 @Injectable({ 29 @Injectable({
29 providedIn: 'root' 30 providedIn: 'root'
30 }) 31 })
  1 +///
  2 +/// Copyright © 2016-2019 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +export * from './admin.service';
  18 +export * from './alarm.service';
  19 +export * from './asset.service';
  20 +export * from './attribute.service';
  21 +export * from './audit-log.service';
  22 +export * from './component-descriptor.service';
  23 +export * from './customer.service';
  24 +export * from './dashboard.service';
  25 +export * from './device.service';
  26 +export * from './entity.service';
  27 +export * from './entity-relation.service';
  28 +export * from './entity-view.service';
  29 +export * from './event.service';
  30 +export * from './http-utils';
  31 +export * from './rule-chain.service';
  32 +export * from './tenant.service';
  33 +export * from './user.service';
  34 +export * from './widget.service';
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { Injectable } from '@angular/core'; 17 +import { ComponentFactory, Injectable } from '@angular/core';
18 import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; 18 import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils';
19 import { forkJoin, Observable, of } from 'rxjs/index'; 19 import { forkJoin, Observable, of } from 'rxjs/index';
20 import { HttpClient } from '@angular/common/http'; 20 import { HttpClient } from '@angular/common/http';
@@ -28,12 +28,16 @@ import { @@ -28,12 +28,16 @@ import {
28 ruleNodeTypeComponentTypes, unknownNodeComponent 28 ruleNodeTypeComponentTypes, unknownNodeComponent
29 } from '@shared/models/rule-chain.models'; 29 } from '@shared/models/rule-chain.models';
30 import { ComponentDescriptorService } from './component-descriptor.service'; 30 import { ComponentDescriptorService } from './component-descriptor.service';
31 -import { LinkLabel, RuleNodeComponentDescriptor } from '@app/shared/models/rule-node.models'; 31 +import {
  32 + IRuleNodeConfigurationComponent,
  33 + LinkLabel,
  34 + RuleNodeComponentDescriptor
  35 +} from '@app/shared/models/rule-node.models';
32 import { ResourcesService } from '../services/resources.service'; 36 import { ResourcesService } from '../services/resources.service';
33 import { catchError, map, mergeMap } from 'rxjs/operators'; 37 import { catchError, map, mergeMap } from 'rxjs/operators';
34 import { TranslateService } from '@ngx-translate/core'; 38 import { TranslateService } from '@ngx-translate/core';
35 import { EntityType } from '@shared/models/entity-type.models'; 39 import { EntityType } from '@shared/models/entity-type.models';
36 -import { deepClone } from '@core/utils'; 40 +import { deepClone, snakeCase } from '@core/utils';
37 41
38 @Injectable({ 42 @Injectable({
39 providedIn: 'root' 43 providedIn: 'root'
@@ -41,6 +45,7 @@ import { deepClone } from '@core/utils'; @@ -41,6 +45,7 @@ import { deepClone } from '@core/utils';
41 export class RuleChainService { 45 export class RuleChainService {
42 46
43 private ruleNodeComponents: Array<RuleNodeComponentDescriptor>; 47 private ruleNodeComponents: Array<RuleNodeComponentDescriptor>;
  48 + private ruleNodeConfigFactories: {[directive: string]: ComponentFactory<IRuleNodeConfigurationComponent>} = {};
44 49
45 constructor( 50 constructor(
46 private http: HttpClient, 51 private http: HttpClient,
@@ -105,13 +110,14 @@ export class RuleChainService { @@ -105,13 +110,14 @@ export class RuleChainService {
105 ); 110 );
106 } 111 }
107 112
108 - public getRuleNodeComponents(config?: RequestConfig): Observable<Array<RuleNodeComponentDescriptor>> { 113 + public getRuleNodeComponents(ruleNodeConfigResourcesModulesMap: {[key: string]: any}, config?: RequestConfig):
  114 + Observable<Array<RuleNodeComponentDescriptor>> {
109 if (this.ruleNodeComponents) { 115 if (this.ruleNodeComponents) {
110 return of(this.ruleNodeComponents); 116 return of(this.ruleNodeComponents);
111 } else { 117 } else {
112 return this.loadRuleNodeComponents(config).pipe( 118 return this.loadRuleNodeComponents(config).pipe(
113 mergeMap((components) => { 119 mergeMap((components) => {
114 - return this.resolveRuleNodeComponentsUiResources(components).pipe( 120 + return this.resolveRuleNodeComponentsUiResources(components, ruleNodeConfigResourcesModulesMap).pipe(
115 map((ruleNodeComponents) => { 121 map((ruleNodeComponents) => {
116 this.ruleNodeComponents = ruleNodeComponents; 122 this.ruleNodeComponents = ruleNodeComponents;
117 this.ruleNodeComponents.push(ruleChainNodeComponent); 123 this.ruleNodeComponents.push(ruleChainNodeComponent);
@@ -132,6 +138,10 @@ export class RuleChainService { @@ -132,6 +138,10 @@ export class RuleChainService {
132 } 138 }
133 } 139 }
134 140
  141 + public getRuleNodeConfigFactory(directive: string): ComponentFactory<IRuleNodeConfigurationComponent> {
  142 + return this.ruleNodeConfigFactories[directive];
  143 + }
  144 +
135 public getRuleNodeComponentByClazz(clazz: string): RuleNodeComponentDescriptor { 145 public getRuleNodeComponentByClazz(clazz: string): RuleNodeComponentDescriptor {
136 const found = this.ruleNodeComponents.filter((component) => component.clazz === clazz); 146 const found = this.ruleNodeComponents.filter((component) => component.clazz === clazz);
137 if (found && found.length) { 147 if (found && found.length) {
@@ -192,11 +202,12 @@ export class RuleChainService { @@ -192,11 +202,12 @@ export class RuleChainService {
192 ); 202 );
193 } 203 }
194 204
195 - private resolveRuleNodeComponentsUiResources(components: Array<RuleNodeComponentDescriptor>): 205 + private resolveRuleNodeComponentsUiResources(components: Array<RuleNodeComponentDescriptor>,
  206 + ruleNodeConfigResourcesModulesMap: {[key: string]: any}):
196 Observable<Array<RuleNodeComponentDescriptor>> { 207 Observable<Array<RuleNodeComponentDescriptor>> {
197 const tasks: Observable<RuleNodeComponentDescriptor>[] = []; 208 const tasks: Observable<RuleNodeComponentDescriptor>[] = [];
198 components.forEach((component) => { 209 components.forEach((component) => {
199 - tasks.push(this.resolveRuleNodeComponentUiResources(component)); 210 + tasks.push(this.resolveRuleNodeComponentUiResources(component, ruleNodeConfigResourcesModulesMap));
200 }); 211 });
201 return forkJoin(tasks).pipe( 212 return forkJoin(tasks).pipe(
202 catchError((err) => { 213 catchError((err) => {
@@ -205,13 +216,39 @@ export class RuleChainService { @@ -205,13 +216,39 @@ export class RuleChainService {
205 ); 216 );
206 } 217 }
207 218
208 - private resolveRuleNodeComponentUiResources(component: RuleNodeComponentDescriptor): Observable<RuleNodeComponentDescriptor> {  
209 - const uiResources = component.configurationDescriptor.nodeDefinition.uiResources; 219 + private resolveRuleNodeComponentUiResources(component: RuleNodeComponentDescriptor,
  220 + ruleNodeConfigResourcesModulesMap: {[key: string]: any}):
  221 + Observable<RuleNodeComponentDescriptor> {
  222 + const nodeDefinition = component.configurationDescriptor.nodeDefinition;
  223 + const uiResources = nodeDefinition.uiResources;
210 if (uiResources && uiResources.length) { 224 if (uiResources && uiResources.length) {
  225 + const commonResources = uiResources.filter((resource) => !resource.endsWith('.js'));
  226 + const moduleResource = uiResources.find((resource) => resource.endsWith('.js'));
211 const tasks: Observable<any>[] = []; 227 const tasks: Observable<any>[] = [];
212 - uiResources.forEach((uiResource) => {  
213 - tasks.push(this.resourcesService.loadResource(uiResource));  
214 - }); 228 + if (commonResources && commonResources.length) {
  229 + commonResources.forEach((resource) => {
  230 + tasks.push(this.resourcesService.loadResource(resource));
  231 + });
  232 + }
  233 + if (moduleResource) {
  234 + tasks.push(this.resourcesService.loadModule(moduleResource, ruleNodeConfigResourcesModulesMap).pipe(
  235 + map((res) => {
  236 + if (nodeDefinition.configDirective && nodeDefinition.configDirective.length) {
  237 + const selector = snakeCase(nodeDefinition.configDirective, '-');
  238 + const componentFactory = res.componentFactories.find((factory) =>
  239 + factory.selector === selector);
  240 + if (componentFactory) {
  241 + this.ruleNodeConfigFactories[nodeDefinition.configDirective] = componentFactory;
  242 + } else {
  243 + component.configurationDescriptor.nodeDefinition.uiResourceLoadError =
  244 + this.translate.instant('rulenode.directive-is-not-loaded',
  245 + {directiveName: nodeDefinition.configDirective});
  246 + }
  247 + }
  248 + return of(component);
  249 + })
  250 + ));
  251 + }
215 return forkJoin(tasks).pipe( 252 return forkJoin(tasks).pipe(
216 map((res) => { 253 map((res) => {
217 return component; 254 return component;
@@ -231,7 +268,7 @@ export class RuleChainService { @@ -231,7 +268,7 @@ export class RuleChainService {
231 map(ruleChain => ruleChain), 268 map(ruleChain => ruleChain),
232 catchError((err) => { 269 catchError((err) => {
233 const ruleChain = { 270 const ruleChain = {
234 - id: { 271 + id: {
235 entityType: EntityType.RULE_CHAIN, 272 entityType: EntityType.RULE_CHAIN,
236 id: ruleChainId 273 id: ruleChainId
237 } 274 }
@@ -18,6 +18,7 @@ import { Injectable } from '@angular/core'; @@ -18,6 +18,7 @@ import { Injectable } from '@angular/core';
18 18
19 const APP_PREFIX = 'TB-'; 19 const APP_PREFIX = 'TB-';
20 20
  21 +// @dynamic
21 @Injectable( 22 @Injectable(
22 { 23 {
23 providedIn: 'root' 24 providedIn: 'root'
  1 +///
  2 +/// Copyright © 2016-2019 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +export * from './api/public-api';
  18 +export * from './http/public-api';
  19 +export * from './local-storage/local-storage.service';
  20 +export * from './services/public-api';
  21 +export * from './ws/telemetry-websocket.service';
  22 +export * from './core.state';
  23 +export * from './core.module';
@@ -24,6 +24,7 @@ export interface ConfirmDialogData { @@ -24,6 +24,7 @@ export interface ConfirmDialogData {
24 ok: string; 24 ok: string;
25 } 25 }
26 26
  27 +// @dynamic
27 @Component({ 28 @Component({
28 selector: 'tb-confirm-dialog', 29 selector: 'tb-confirm-dialog',
29 templateUrl: './confirm-dialog.component.html', 30 templateUrl: './confirm-dialog.component.html',
  1 +///
  2 +/// Copyright © 2016-2019 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +export * from './script/node-script-test.service';
  18 +export * from './broadcast.models';
  19 +export * from './broadcast.service';
  20 +export * from './dashboard-utils.service';
  21 +export * from './dialog.service';
  22 +export * from './dynamic-component-factory.service';
  23 +export * from './item-buffer.service';
  24 +export * from './menu.models';
  25 +export * from './menu.service';
  26 +export * from './notification.service';
  27 +export * from './raf.service';
  28 +export * from './resources.service';
  29 +export * from './time.service';
  30 +export * from './title.service';
  31 +export * from './utils.service';
  32 +export * from './window.service';
@@ -20,6 +20,7 @@ import { WINDOW } from '@core/services/window.service'; @@ -20,6 +20,7 @@ import { WINDOW } from '@core/services/window.service';
20 20
21 export type CancelAnimationFrame = () => void; 21 export type CancelAnimationFrame = () => void;
22 22
  23 +// @dynamic
23 @Injectable({ 24 @Injectable({
24 providedIn: 'root' 25 providedIn: 'root'
25 }) 26 })
@@ -14,20 +14,25 @@ @@ -14,20 +14,25 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { Injectable, Inject } from '@angular/core'; 17 +import { Injectable, Inject, ModuleWithComponentFactories, Compiler, Injector } from '@angular/core';
18 import { DOCUMENT } from '@angular/common'; 18 import { DOCUMENT } from '@angular/common';
19 import { ReplaySubject, Observable, throwError } from 'rxjs'; 19 import { ReplaySubject, Observable, throwError } from 'rxjs';
20 20
  21 +declare const SystemJS;
  22 +
21 @Injectable({ 23 @Injectable({
22 providedIn: 'root' 24 providedIn: 'root'
23 }) 25 })
24 export class ResourcesService { 26 export class ResourcesService {
25 27
26 private loadedResources: { [url: string]: ReplaySubject<any> } = {}; 28 private loadedResources: { [url: string]: ReplaySubject<any> } = {};
  29 + private loadedModules: { [url: string]: ReplaySubject<ModuleWithComponentFactories<any>> } = {};
27 30
28 private anchor = this.document.getElementsByTagName('head')[0] || this.document.getElementsByTagName('body')[0]; 31 private anchor = this.document.getElementsByTagName('head')[0] || this.document.getElementsByTagName('body')[0];
29 32
30 - constructor(@Inject(DOCUMENT) private readonly document: any) {} 33 + constructor(@Inject(DOCUMENT) private readonly document: any,
  34 + private compiler: Compiler,
  35 + private injector: Injector) {}
31 36
32 public loadResource(url: string): Observable<any> { 37 public loadResource(url: string): Observable<any> {
33 if (this.loadedResources[url]) { 38 if (this.loadedResources[url]) {
@@ -47,6 +52,49 @@ export class ResourcesService { @@ -47,6 +52,49 @@ export class ResourcesService {
47 return this.loadResourceByType(fileType, url); 52 return this.loadResourceByType(fileType, url);
48 } 53 }
49 54
  55 + public loadModule(url: string, modulesMap: {[key: string]: any}): Observable<ModuleWithComponentFactories<any>> {
  56 + if (this.loadedModules[url]) {
  57 + return this.loadedModules[url].asObservable();
  58 + }
  59 + const subject = new ReplaySubject<ModuleWithComponentFactories<any>>();
  60 + this.loadedModules[url] = subject;
  61 + if (modulesMap) {
  62 + for (const moduleId of Object.keys(modulesMap)) {
  63 + SystemJS.set(moduleId, modulesMap[moduleId]);
  64 + }
  65 + }
  66 + SystemJS.import(url).then(
  67 + (module) => {
  68 + if (module.default) {
  69 + this.compiler.compileModuleAndAllComponentsAsync(module.default).then(
  70 + (compiled) => {
  71 + try {
  72 + compiled.ngModuleFactory.create(this.injector);
  73 + this.loadedModules[url].next(compiled);
  74 + this.loadedModules[url].complete();
  75 + } catch (e) {
  76 + this.loadedModules[url].error(new Error(`Unable to init module from url: ${url}`));
  77 + delete this.loadedModules[url];
  78 + }
  79 + },
  80 + (e) => {
  81 + this.loadedModules[url].error(new Error(`Unable to compile module from url: ${url}`));
  82 + delete this.loadedModules[url];
  83 + }
  84 + );
  85 + } else {
  86 + this.loadedModules[url].error(new Error(`Module '${url}' doesn't have default export!`));
  87 + delete this.loadedModules[url];
  88 + }
  89 + },
  90 + (e) => {
  91 + this.loadedModules[url].error(new Error(`Unable to load module from url: ${url}`));
  92 + delete this.loadedModules[url];
  93 + }
  94 + );
  95 + return subject.asObservable();
  96 + }
  97 +
50 private loadResourceByType(type: 'css' | 'js', url: string): Observable<any> { 98 private loadResourceByType(type: 'css' | 'js', url: string): Observable<any> {
51 const subject = new ReplaySubject(); 99 const subject = new ReplaySubject();
52 this.loadedResources[url] = subject; 100 this.loadedResources[url] = subject;
  1 +///
  2 +/// Copyright © 2016-2019 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import { Injectable } from '@angular/core';
  18 +import { Observable, of } from 'rxjs';
  19 +
  20 +@Injectable({
  21 + providedIn: 'root'
  22 +})
  23 +export class NodeScriptTestService {
  24 +
  25 + testNodeScript(script: string, scriptType: any, functionTitle: string,
  26 + functionName: string, argNames: string[], ruleNodeId: string): Observable<string> {
  27 + console.log(`testNodeScript TODO: ${script}`);
  28 + return of(script);
  29 + }
  30 +
  31 +}
@@ -14,6 +14,9 @@ @@ -14,6 +14,9 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
  17 +// tslint:disable-next-line:no-reference
  18 +/// <reference path="../../../../src/typings/rawloader.typings.d.ts" />
  19 +
17 import { Inject, Injectable, NgZone } from '@angular/core'; 20 import { Inject, Injectable, NgZone } from '@angular/core';
18 import { WINDOW } from '@core/services/window.service'; 21 import { WINDOW } from '@core/services/window.service';
19 import { ExceptionData } from '@app/shared/models/error.models'; 22 import { ExceptionData } from '@app/shared/models/error.models';
@@ -67,6 +70,7 @@ const commonMaterialIcons: Array<string> = [ 'more_horiz', 'more_vert', 'open_in @@ -67,6 +70,7 @@ const commonMaterialIcons: Array<string> = [ 'more_horiz', 'more_vert', 'open_in
67 'settings', 'notifications', 'notifications_active', 'info', 'info_outline', 'warning', 'list', 'file_download', 'import_export', 70 'settings', 'notifications', 'notifications_active', 'info', 'info_outline', 'warning', 'list', 'file_download', 'import_export',
68 'share', 'add', 'edit', 'done' ]; 71 'share', 'add', 'edit', 'done' ];
69 72
  73 +// @dynamic
70 @Injectable({ 74 @Injectable({
71 providedIn: 'root' 75 providedIn: 'root'
72 }) 76 })
@@ -387,3 +387,12 @@ export function guid(): string { @@ -387,3 +387,12 @@ export function guid(): string {
387 return s4() + s4() + '-' + s4() + '-' + s4() + '-' + 387 return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
388 s4() + '-' + s4() + s4() + s4(); 388 s4() + '-' + s4() + s4() + s4();
389 } 389 }
  390 +
  391 +const SNAKE_CASE_REGEXP = /[A-Z]/g;
  392 +
  393 +export function snakeCase(name: string, separator: string): string {
  394 + separator = separator || '_';
  395 + return name.replace(SNAKE_CASE_REGEXP, (letter, pos) => {
  396 + return (pos ? separator : '') + letter.toLowerCase();
  397 + });
  398 +}
@@ -40,6 +40,7 @@ const RECONNECT_INTERVAL = 2000; @@ -40,6 +40,7 @@ const RECONNECT_INTERVAL = 2000;
40 const WS_IDLE_TIMEOUT = 90000; 40 const WS_IDLE_TIMEOUT = 90000;
41 const MAX_PUBLISH_COMMANDS = 10; 41 const MAX_PUBLISH_COMMANDS = 10;
42 42
  43 +// @dynamic
43 @Injectable({ 44 @Injectable({
44 providedIn: 'root' 45 providedIn: 'root'
45 }) 46 })
@@ -30,6 +30,7 @@ import { @@ -30,6 +30,7 @@ import {
30 AliasesEntitySelectPanelData 30 AliasesEntitySelectPanelData
31 } from './aliases-entity-select-panel.component'; 31 } from './aliases-entity-select-panel.component';
32 32
  33 +// @dynamic
33 @Component({ 34 @Component({
34 selector: 'tb-aliases-entity-select', 35 selector: 'tb-aliases-entity-select',
35 templateUrl: './aliases-entity-select.component.html', 36 templateUrl: './aliases-entity-select.component.html',
@@ -73,7 +73,7 @@ export class EntityAliasDialogComponent extends DialogComponent<EntityAliasDialo @@ -73,7 +73,7 @@ export class EntityAliasDialogComponent extends DialogComponent<EntityAliasDialo
73 public dialogRef: MatDialogRef<EntityAliasDialogComponent, EntityAlias>, 73 public dialogRef: MatDialogRef<EntityAliasDialogComponent, EntityAlias>,
74 private fb: FormBuilder, 74 private fb: FormBuilder,
75 private utils: UtilsService, 75 private utils: UtilsService,
76 - private translate: TranslateService, 76 + public translate: TranslateService,
77 private entityService: EntityService) { 77 private entityService: EntityService) {
78 super(store, router, dialogRef); 78 super(store, router, dialogRef);
79 this.isAdd = data.isAdd; 79 this.isAdd = data.isAdd;
@@ -92,7 +92,7 @@ export class EntityAliasSelectComponent implements ControlValueAccessor, OnInit, @@ -92,7 +92,7 @@ export class EntityAliasSelectComponent implements ControlValueAccessor, OnInit,
92 92
93 filteredEntityAliases: Observable<Array<EntityAlias>>; 93 filteredEntityAliases: Observable<Array<EntityAlias>>;
94 94
95 - private searchText = ''; 95 + searchText = '';
96 96
97 private dirty = false; 97 private dirty = false;
98 98
@@ -42,7 +42,7 @@ @@ -42,7 +42,7 @@
42 <fieldset [disabled]="isLoading$ | async"> 42 <fieldset [disabled]="isLoading$ | async">
43 <div fxFlex fxLayout="row" fxLayoutAlign="start center" 43 <div fxFlex fxLayout="row" fxLayoutAlign="start center"
44 formArrayName="entityAliases" 44 formArrayName="entityAliases"
45 - *ngFor="let entityAliasControl of entityAliasesFormGroup.get('entityAliases').controls; let $index = index"> 45 + *ngFor="let entityAliasControl of entityAliasesFormArray().controls; let $index = index">
46 <span fxFlex="5">{{$index + 1}}.</span> 46 <span fxFlex="5">{{$index + 1}}.</span>
47 <div class="mat-elevation-z4 tb-alias" fxFlex="95" fxLayout="row" fxLayoutAlign="start center"> 47 <div class="mat-elevation-z4 tb-alias" fxFlex="95" fxLayout="row" fxLayoutAlign="start center">
48 <mat-form-field floatLabel="always" hideRequiredMarker class="mat-block" fxFlex="150px"> 48 <mat-form-field floatLabel="always" hideRequiredMarker class="mat-block" fxFlex="150px">
@@ -154,6 +154,11 @@ export class EntityAliasesDialogComponent extends DialogComponent<EntityAliasesD @@ -154,6 +154,11 @@ export class EntityAliasesDialogComponent extends DialogComponent<EntityAliasesD
154 return aliasFormControl; 154 return aliasFormControl;
155 } 155 }
156 156
  157 +
  158 + entityAliasesFormArray(): FormArray {
  159 + return this.entityAliasesFormGroup.get('entityAliases') as FormArray;
  160 + }
  161 +
157 ngOnInit(): void { 162 ngOnInit(): void {
158 } 163 }
159 164
@@ -112,7 +112,7 @@ @@ -112,7 +112,7 @@
112 [selectFirstBundle]="false" 112 [selectFirstBundle]="false"
113 [selectBundleAlias]="selectedWidgetsBundleAlias" 113 [selectBundleAlias]="selectedWidgetsBundleAlias"
114 [(ngModel)]="widgetsBundle" 114 [(ngModel)]="widgetsBundle"
115 - (ngModelChange)="onWidgetsBundleChanged($event)"> 115 + (ngModelChange)="onWidgetsBundleChanged()">
116 </tb-widgets-bundle-select> 116 </tb-widgets-bundle-select>
117 </div> 117 </div>
118 <button mat-button mat-raised-button [fxShow]="widgetsList.length > 0" 118 <button mat-button mat-raised-button [fxShow]="widgetsList.length > 0"
@@ -195,7 +195,7 @@ @@ -195,7 +195,7 @@
195 <ngx-hm-carousel fxFlex *ngIf="mode === 'widget' && widgetsList.length > 0" 195 <ngx-hm-carousel fxFlex *ngIf="mode === 'widget' && widgetsList.length > 0"
196 #carousel 196 #carousel
197 [(ngModel)]="widgetsCarouselIndex" 197 [(ngModel)]="widgetsCarouselIndex"
198 - (ngModelChange)="onWidgetsCarouselIndexChanged($event)" 198 + (ngModelChange)="onWidgetsCarouselIndexChanged()"
199 [data]="widgetsList" 199 [data]="widgetsList"
200 [infinite]="false" 200 [infinite]="false"
201 class="carousel c-accent"> 201 class="carousel c-accent">
@@ -54,6 +54,7 @@ import { ImportDialogCsvComponent, ImportDialogCsvData } from './import-dialog-c @@ -54,6 +54,7 @@ import { ImportDialogCsvComponent, ImportDialogCsvData } from './import-dialog-c
54 import { ImportEntityData, ImportEntitiesResultInfo } from '@shared/models/entity.models'; 54 import { ImportEntityData, ImportEntitiesResultInfo } from '@shared/models/entity.models';
55 import { RequestConfig } from '@core/http/http-utils'; 55 import { RequestConfig } from '@core/http/http-utils';
56 56
  57 +// @dynamic
57 @Injectable() 58 @Injectable()
58 export class ImportExportService { 59 export class ImportExportService {
59 60
  1 +///
  2 +/// Copyright © 2016-2019 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +export * from './home-components.module';
@@ -16,17 +16,17 @@ @@ -16,17 +16,17 @@
16 16
17 --> 17 -->
18 <div class="tb-relation-filters" [formGroup]="relationFiltersFormGroup"> 18 <div class="tb-relation-filters" [formGroup]="relationFiltersFormGroup">
19 - <div class="header" [fxShow]="relationFiltersFormGroup.get('relationFilters').length"> 19 + <div class="header" [fxShow]="relationFiltersFormArray().length">
20 <div fxLayout="row" fxLayoutAlign="start center"> 20 <div fxLayout="row" fxLayoutAlign="start center">
21 <span class="cell" style="width: 200px; min-width: 200px;" translate>relation.type</span> 21 <span class="cell" style="width: 200px; min-width: 200px;" translate>relation.type</span>
22 <span class="cell" fxFlex translate>entity.entity-types</span> 22 <span class="cell" fxFlex translate>entity.entity-types</span>
23 <span class="cell" style="width: 40px; min-width: 40px;">&nbsp;</span> 23 <span class="cell" style="width: 40px; min-width: 40px;">&nbsp;</span>
24 </div> 24 </div>
25 </div> 25 </div>
26 - <div class="body" [fxShow]="relationFiltersFormGroup.get('relationFilters').length"> 26 + <div class="body" [fxShow]="relationFiltersFormArray().length">
27 <div class="row" fxFlex fxLayout="row" 27 <div class="row" fxFlex fxLayout="row"
28 fxLayoutAlign="start center" formArrayName="relationFilters" 28 fxLayoutAlign="start center" formArrayName="relationFilters"
29 - *ngFor="let relationFilterControl of relationFiltersFormGroup.get('relationFilters').controls; let $index = index"> 29 + *ngFor="let relationFilterControl of relationFiltersFormArray().controls; let $index = index">
30 <div class="mat-elevation-z1" fxFlex fxLayout="row" fxLayoutAlign="start center"> 30 <div class="mat-elevation-z1" fxFlex fxLayout="row" fxLayoutAlign="start center">
31 <tb-relation-type-autocomplete 31 <tb-relation-type-autocomplete
32 class="cell" style="width: 200px; min-width: 200px;" 32 class="cell" style="width: 200px; min-width: 200px;"
@@ -48,7 +48,7 @@ @@ -48,7 +48,7 @@
48 </div> 48 </div>
49 </div> 49 </div>
50 </div> 50 </div>
51 - <div class="any-filter" [fxShow]="!relationFiltersFormGroup.get('relationFilters').length"> 51 + <div class="any-filter" [fxShow]="!relationFiltersFormArray().length">
52 <span fxLayoutAlign="center center" 52 <span fxLayoutAlign="center center"
53 class="tb-prompt" translate>relation.any-relation</span> 53 class="tb-prompt" translate>relation.any-relation</span>
54 </div> 54 </div>
@@ -69,6 +69,10 @@ export class RelationFiltersComponent extends PageComponent implements ControlVa @@ -69,6 +69,10 @@ export class RelationFiltersComponent extends PageComponent implements ControlVa
69 this.fb.array([])); 69 this.fb.array([]));
70 } 70 }
71 71
  72 + relationFiltersFormArray(): FormArray {
  73 + return this.relationFiltersFormGroup.get('relationFilters') as FormArray;
  74 + }
  75 +
72 registerOnChange(fn: any): void { 76 registerOnChange(fn: any): void {
73 this.propagateChange = fn; 77 this.propagateChange = fn;
74 } 78 }
@@ -14,6 +14,9 @@ @@ -14,6 +14,9 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
  17 +// tslint:disable-next-line:no-reference
  18 +/// <reference path="../../../../../../../src/typings/split.js.typings.d.ts" />
  19 +
17 import { 20 import {
18 AfterViewInit, 21 AfterViewInit,
19 ChangeDetectionStrategy, 22 ChangeDetectionStrategy,
@@ -78,7 +78,7 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con @@ -78,7 +78,7 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con
78 78
79 displayAdvanced = false; 79 displayAdvanced = false;
80 80
81 - private modelValue: DataKey; 81 + modelValue: DataKey;
82 82
83 private propagateChange = null; 83 private propagateChange = null;
84 84
@@ -140,7 +140,7 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, AfterVie @@ -140,7 +140,7 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, AfterVie
140 placeholder: string; 140 placeholder: string;
141 requiredText: string; 141 requiredText: string;
142 142
143 - private searchText = ''; 143 + searchText = '';
144 private latestSearchTextResult: Array<DataKey> = null; 144 private latestSearchTextResult: Array<DataKey> = null;
145 145
146 private dirty = false; 146 private dirty = false;
@@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
17 --> 17 -->
18 <button cdkOverlayOrigin #legendConfigPanelOrigin="cdkOverlayOrigin" [disabled]="disabled" 18 <button cdkOverlayOrigin #legendConfigPanelOrigin="cdkOverlayOrigin" [disabled]="disabled"
19 type="button" 19 type="button"
20 - mat-button mat-raised-button color="primary" (click)="openEditMode($event)"> 20 + mat-button mat-raised-button color="primary" (click)="openEditMode()">
21 <mat-icon class="material-icons">toc</mat-icon> 21 <mat-icon class="material-icons">toc</mat-icon>
22 <span translate>legend.settings</span> 22 <span translate>legend.settings</span>
23 </button> 23 </button>
@@ -59,6 +59,7 @@ import { @@ -59,6 +59,7 @@ import {
59 LegendConfigPanelData 59 LegendConfigPanelData
60 } from '@home/components/widget/legend-config-panel.component'; 60 } from '@home/components/widget/legend-config-panel.component';
61 61
  62 +// @dynamic
62 @Component({ 63 @Component({
63 selector: 'tb-legend-config', 64 selector: 'tb-legend-config',
64 templateUrl: './legend-config.component.html', 65 templateUrl: './legend-config.component.html',
@@ -14,6 +14,9 @@ @@ -14,6 +14,9 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
  17 +// tslint:disable-next-line:no-reference
  18 +/// <reference path="../../../../../../../src/typings/jquery.flot.typings.d.ts" />
  19 +
17 import { JsonSettingsSchema, DataKey, DatasourceData } from '@shared/models/widget.models'; 20 import { JsonSettingsSchema, DataKey, DatasourceData } from '@shared/models/widget.models';
18 21
19 export declare type ChartType = 'line' | 'pie' | 'bar' | 'state' | 'graph'; 22 export declare type ChartType = 'line' | 'pie' | 'bar' | 'state' | 'graph';
@@ -395,8 +398,8 @@ export function flotSettingsSchema(chartType: ChartType): JsonSettingsSchema { @@ -395,8 +398,8 @@ export function flotSettingsSchema(chartType: ChartType): JsonSettingsSchema {
395 return schema; 398 return schema;
396 } 399 }
397 400
398 -export function flotPieSettingsSchema(): JsonSettingsSchema {  
399 - return { 401 +export const flotPieSettingsSchema: JsonSettingsSchema =
  402 + {
400 schema: { 403 schema: {
401 type: 'object', 404 type: 'object',
402 title: 'Settings', 405 title: 'Settings',
@@ -477,8 +480,7 @@ export function flotPieSettingsSchema(): JsonSettingsSchema { @@ -477,8 +480,7 @@ export function flotPieSettingsSchema(): JsonSettingsSchema {
477 }, 480 },
478 'fontSize' 481 'fontSize'
479 ] 482 ]
480 - };  
481 -} 483 +};
482 484
483 export function flotDatakeySettingsSchema(defaultShowLines: boolean): JsonSettingsSchema { 485 export function flotDatakeySettingsSchema(defaultShowLines: boolean): JsonSettingsSchema {
484 return { 486 return {
@@ -36,12 +36,17 @@ import { @@ -36,12 +36,17 @@ import {
36 TbFlotTicksFormatterFunction, 36 TbFlotTicksFormatterFunction,
37 TooltipValueFormatFunction 37 TooltipValueFormatFunction
38 } from './flot-widget.models'; 38 } from './flot-widget.models';
39 -import * as moment from 'moment';  
40 -import * as tinycolor from 'tinycolor2'; 39 +import * as moment_ from 'moment';
  40 +import * as tinycolor_ from 'tinycolor2';
41 import { AggregationType } from '@shared/models/time/time.models'; 41 import { AggregationType } from '@shared/models/time/time.models';
42 import { CancelAnimationFrame } from '@core/services/raf.service'; 42 import { CancelAnimationFrame } from '@core/services/raf.service';
43 import Timeout = NodeJS.Timeout; 43 import Timeout = NodeJS.Timeout;
44 44
  45 +const tinycolor = tinycolor_;
  46 +const moment = moment_;
  47 +
  48 +const flotPieSettingsSchemaValue = flotPieSettingsSchema;
  49 +
45 export class TbFlot { 50 export class TbFlot {
46 51
47 private settings: TbFlotSettings; 52 private settings: TbFlotSettings;
@@ -89,11 +94,11 @@ export class TbFlot { @@ -89,11 +94,11 @@ export class TbFlot {
89 private pieAnimationLastTime: number; 94 private pieAnimationLastTime: number;
90 private pieAnimationCaf: CancelAnimationFrame; 95 private pieAnimationCaf: CancelAnimationFrame;
91 96
92 - static get pieSettingsSchema(): JsonSettingsSchema {  
93 - return flotPieSettingsSchema(); 97 + static pieSettingsSchema(): JsonSettingsSchema {
  98 + return flotPieSettingsSchemaValue;
94 } 99 }
95 100
96 - static get pieDatakeySettingsSchema(): JsonSettingsSchema { 101 + static pieDatakeySettingsSchema(): JsonSettingsSchema {
97 return {}; 102 return {};
98 } 103 }
99 104
@@ -38,14 +38,17 @@ import { SharedModule } from '@shared/shared.module'; @@ -38,14 +38,17 @@ import { SharedModule } from '@shared/shared.module';
38 import { WidgetComponentsModule } from '@home/components/widget/widget-components.module'; 38 import { WidgetComponentsModule } from '@home/components/widget/widget-components.module';
39 import { WINDOW } from '@core/services/window.service'; 39 import { WINDOW } from '@core/services/window.service';
40 40
41 -import * as tinycolor from 'tinycolor2'; 41 +import * as tinycolor_ from 'tinycolor2';
42 import { TbFlot } from './lib/flot-widget'; 42 import { TbFlot } from './lib/flot-widget';
43 import { NULL_UUID } from '@shared/models/id/has-uuid'; 43 import { NULL_UUID } from '@shared/models/id/has-uuid';
44 import { WidgetTypeId } from '@app/shared/models/id/widget-type-id'; 44 import { WidgetTypeId } from '@app/shared/models/id/widget-type-id';
45 import { TenantId } from '@app/shared/models/id/tenant-id'; 45 import { TenantId } from '@app/shared/models/id/tenant-id';
46 46
  47 +const tinycolor = tinycolor_;
  48 +
47 // declare var jQuery: any; 49 // declare var jQuery: any;
48 50
  51 +// @dynamic
49 @Injectable() 52 @Injectable()
50 export class WidgetComponentService { 53 export class WidgetComponentService {
51 54
@@ -71,7 +71,7 @@ @@ -71,7 +71,7 @@
71 class="tb-hint">{{ 'widget-config.maximum-datasources' | translate:{count: modelValue?.typeParameters.maxDatasources} }}</div> 71 class="tb-hint">{{ 'widget-config.maximum-datasources' | translate:{count: modelValue?.typeParameters.maxDatasources} }}</div>
72 </mat-panel-title> 72 </mat-panel-title>
73 </mat-expansion-panel-header> 73 </mat-expansion-panel-header>
74 - <div *ngIf="dataSettings.get('datasources').length === 0; else datasourcesTemplate"> 74 + <div *ngIf="datasourcesFormArray().length === 0; else datasourcesTemplate">
75 <span translate fxLayoutAlign="center center" 75 <span translate fxLayoutAlign="center center"
76 class="tb-prompt">datasource.add-datasource-prompt</span> 76 class="tb-prompt">datasource.add-datasource-prompt</span>
77 </div> 77 </div>
@@ -89,7 +89,7 @@ @@ -89,7 +89,7 @@
89 <div style="overflow: auto; padding-bottom: 15px;"> 89 <div style="overflow: auto; padding-bottom: 15px;">
90 <div fxFlex fxLayout="row" fxLayoutAlign="start center" 90 <div fxFlex fxLayout="row" fxLayoutAlign="start center"
91 formArrayName="datasources" 91 formArrayName="datasources"
92 - *ngFor="let datasourceControl of dataSettings.get('datasources').controls; let $index = index;"> 92 + *ngFor="let datasourceControl of datasourcesFormArray().controls; let $index = index;">
93 <span fxFlex="5">{{$index + 1}}.</span> 93 <span fxFlex="5">{{$index + 1}}.</span>
94 <div [formGroupName]="$index" class="mat-elevation-z4" fxFlex 94 <div [formGroupName]="$index" class="mat-elevation-z4" fxFlex
95 fxLayout="row" 95 fxLayout="row"
@@ -109,7 +109,7 @@ @@ -109,7 +109,7 @@
109 </mat-form-field> 109 </mat-form-field>
110 <section class="tb-datasource" [ngSwitch]="datasourceControl.get('type').value"> 110 <section class="tb-datasource" [ngSwitch]="datasourceControl.get('type').value">
111 <ng-template [ngSwitchCase]="datasourceType.function"> 111 <ng-template [ngSwitchCase]="datasourceType.function">
112 - <mat-form-field floatLabel="always" [fxShow]="widgetType !== widgetTypes.alarm" 112 + <mat-form-field floatLabel="always"
113 class="tb-datasource-name" style="min-width: 200px;"> 113 class="tb-datasource-name" style="min-width: 200px;">
114 <mat-label></mat-label> 114 <mat-label></mat-label>
115 <input matInput 115 <input matInput
@@ -156,7 +156,7 @@ @@ -156,7 +156,7 @@
156 type="button" 156 type="button"
157 mat-button mat-raised-button color="primary" 157 mat-button mat-raised-button color="primary"
158 [fxShow]="modelValue?.typeParameters && 158 [fxShow]="modelValue?.typeParameters &&
159 - (modelValue?.typeParameters.maxDatasources == -1 || dataSettings.get('datasources').controls.length < modelValue?.typeParameters.maxDatasources)" 159 + (modelValue?.typeParameters.maxDatasources == -1 || datasourcesFormArray().controls.length < modelValue?.typeParameters.maxDatasources)"
160 (click)="addDatasource()" 160 (click)="addDatasource()"
161 matTooltip="{{ 'widget-config.add-datasource' | translate }}" 161 matTooltip="{{ 'widget-config.add-datasource' | translate }}"
162 matTooltipPosition="above"> 162 matTooltipPosition="above">
@@ -128,7 +128,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont @@ -128,7 +128,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont
128 128
129 selectedTab: number; 129 selectedTab: number;
130 130
131 - private modelValue: WidgetConfigComponentData; 131 + modelValue: WidgetConfigComponentData;
132 132
133 private propagateChange = null; 133 private propagateChange = null;
134 134
@@ -307,6 +307,10 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont @@ -307,6 +307,10 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont
307 this.fb.control(null, [])); 307 this.fb.control(null, []));
308 } 308 }
309 309
  310 + datasourcesFormArray(): FormArray {
  311 + return this.dataSettings.get('datasources') as FormArray;
  312 + }
  313 +
310 registerOnChange(fn: any): void { 314 registerOnChange(fn: any): void {
311 this.propagateChange = fn; 315 this.propagateChange = fn;
312 } 316 }
@@ -86,6 +86,7 @@ import { @@ -86,6 +86,7 @@ import {
86 import { ImportExportService } from '@home/components/import-export/import-export.service'; 86 import { ImportExportService } from '@home/components/import-export/import-export.service';
87 import { AuthState } from '@app/core/auth/auth.models'; 87 import { AuthState } from '@app/core/auth/auth.models';
88 88
  89 +// @dynamic
89 @Component({ 90 @Component({
90 selector: 'tb-dashboard-page', 91 selector: 'tb-dashboard-page',
91 templateUrl: './dashboard-page.component.html', 92 templateUrl: './dashboard-page.component.html',
@@ -90,6 +90,7 @@ const routes: Routes = [ @@ -90,6 +90,7 @@ const routes: Routes = [
90 } 90 }
91 ]; 91 ];
92 92
  93 +// @dynamic
93 @NgModule({ 94 @NgModule({
94 imports: [RouterModule.forChild(routes)], 95 imports: [RouterModule.forChild(routes)],
95 exports: [RouterModule], 96 exports: [RouterModule],
@@ -47,7 +47,7 @@ import { map } from 'rxjs/operators'; @@ -47,7 +47,7 @@ import { map } from 'rxjs/operators';
47 }) 47 })
48 export class EntityStateControllerComponent extends StateControllerComponent implements OnInit, OnDestroy { 48 export class EntityStateControllerComponent extends StateControllerComponent implements OnInit, OnDestroy {
49 49
50 - private selectedStateIndex = -1; 50 + selectedStateIndex = -1;
51 51
52 constructor(protected router: Router, 52 constructor(protected router: Router,
53 protected route: ActivatedRoute, 53 protected route: ActivatedRoute,
  1 +///
  2 +/// Copyright © 2016-2019 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +export * from './home-pages.module';
  1 +<!--
  2 +
  3 + Copyright © 2016-2019 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<form #ruleNodeForm="ngForm" (ngSubmit)="add()" style="min-width: 650px;">
  19 + <mat-toolbar fxLayout="row" color="primary">
  20 + <h2 translate>rulenode.add</h2>
  21 + <span fxFlex></span>
  22 + <div [tb-help]="helpLinkIdForRuleNodeType()"></div>
  23 + <button mat-button mat-icon-button
  24 + (click)="cancel()"
  25 + type="button">
  26 + <mat-icon class="material-icons">close</mat-icon>
  27 + </button>
  28 + </mat-toolbar>
  29 + <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
  30 + </mat-progress-bar>
  31 + <div mat-dialog-content>
  32 + <fieldset [disabled]="isLoading$ | async">
  33 + <tb-rule-node #tbRuleNode
  34 + [ruleNode]="ruleNode"
  35 + [ruleChainId]="ruleChainId"
  36 + [isEdit]="true"
  37 + [isReadOnly]="false">
  38 + </tb-rule-node>
  39 + </fieldset>
  40 + </div>
  41 + <div mat-dialog-actions fxLayout="row">
  42 + <span fxFlex></span>
  43 + <button mat-button mat-raised-button color="primary"
  44 + type="submit"
  45 + [disabled]="(isLoading$ | async) || tbRuleNode.ruleNodeForm.invalid || !tbRuleNode.ruleNodeForm.dirty">
  46 + {{ 'action.add' | translate }}
  47 + </button>
  48 + <button mat-button color="primary"
  49 + style="margin-right: 20px;"
  50 + type="button"
  51 + [disabled]="(isLoading$ | async)"
  52 + (click)="cancel()" cdkFocusInitial>
  53 + {{ 'action.cancel' | translate }}
  54 + </button>
  55 + </div>
  56 +</form>
@@ -45,7 +45,7 @@ @@ -45,7 +45,7 @@
45 <mat-option *ngFor="let label of filteredLabels | async" [value]="label"> 45 <mat-option *ngFor="let label of filteredLabels | async" [value]="label">
46 <span [innerHTML]="label.name | highlight:searchText"></span> 46 <span [innerHTML]="label.name | highlight:searchText"></span>
47 </mat-option> 47 </mat-option>
48 - <mat-option *ngIf="!(filteredLabels | async)?.length" [value]="null" class="tb-not-found"> 48 + <mat-option *ngIf="(filteredLabels | async)?.length === 0" [value]="null" class="tb-not-found">
49 <div class="tb-not-found-content" (click)="$event.stopPropagation()"> 49 <div class="tb-not-found-content" (click)="$event.stopPropagation()">
50 <div *ngIf="!textIsNotEmpty(searchText); else searchNotEmpty"> 50 <div *ngIf="!textIsNotEmpty(searchText); else searchNotEmpty">
51 <span translate>rulenode.no-link-labels-found</span> 51 <span translate>rulenode.no-link-labels-found</span>
@@ -94,9 +94,9 @@ export class LinkLabelsComponent implements ControlValueAccessor, OnInit, OnChan @@ -94,9 +94,9 @@ export class LinkLabelsComponent implements ControlValueAccessor, OnInit, OnChan
94 94
95 filteredLabels: Observable<Array<LinkLabel>>; 95 filteredLabels: Observable<Array<LinkLabel>>;
96 96
97 - private labels: Array<LinkLabel> = []; 97 + labels: Array<LinkLabel> = [];
98 98
99 - private searchText = ''; 99 + searchText = '';
100 100
101 private propagateChange = (v: any) => { }; 101 private propagateChange = (v: any) => { };
102 102
@@ -190,7 +190,7 @@ export class LinkLabelsComponent implements ControlValueAccessor, OnInit, OnChan @@ -190,7 +190,7 @@ export class LinkLabelsComponent implements ControlValueAccessor, OnInit, OnChan
190 } 190 }
191 191
192 add(event: MatChipInputEvent): void { 192 add(event: MatChipInputEvent): void {
193 - if (!this.matAutocomplete.isOpen) { 193 + if (!this.matAutocomplete.isOpen || this.allowCustom) {
194 this.transformLinkLabel(event.value); 194 this.transformLinkLabel(event.value);
195 } 195 }
196 } 196 }
@@ -250,8 +250,8 @@ export class LinkLabelsComponent implements ControlValueAccessor, OnInit, OnChan @@ -250,8 +250,8 @@ export class LinkLabelsComponent implements ControlValueAccessor, OnInit, OnChan
250 if (this.required) { 250 if (this.required) {
251 this.chipList.errorState = false; 251 this.chipList.errorState = false;
252 } 252 }
  253 + this.updateModel();
253 } 254 }
254 - this.updateModel();  
255 } 255 }
256 256
257 clear(value: string = '') { 257 clear(value: string = '') {
  1 +<!--
  2 +
  3 + Copyright © 2016-2019 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<div [formGroup]="ruleNodeConfigFormGroup">
  19 + <ng-container #definedConfigContent></ng-container>
  20 + <div class="tb-rulenode-directive-error" *ngIf="definedDirectiveError">{{definedDirectiveError}}</div>
  21 + <tb-json-object-edit *ngIf="!useDefinedDirective()" #jsonObjectEditComponent
  22 + class="tb-rule-node-configuration-json"
  23 + formControlName="configuration"
  24 + [label]="'rulenode.configuration' | translate"
  25 + [required]="required"
  26 + [fillHeight]="true">
  27 + </tb-json-object-edit>
  28 +</div>
  1 +/**
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +:host {
  17 + tb-json-object-edit.tb-rule-node-configuration-json {
  18 + display: block;
  19 + height: 300px;
  20 + }
  21 +
  22 + .tb-rulenode-directive-error {
  23 + font-size: 13px;
  24 + font-weight: 400;
  25 + color: rgb(221, 44, 0);
  26 + }
  27 +}
  1 +///
  2 +/// Copyright © 2016-2019 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import {
  18 + AfterViewInit,
  19 + Component, ElementRef,
  20 + EventEmitter, forwardRef,
  21 + Input,
  22 + OnChanges,
  23 + OnInit,
  24 + Output,
  25 + SimpleChanges,
  26 + ViewChild,
  27 + Compiler,
  28 + Injector, ComponentRef, OnDestroy
  29 +} from '@angular/core';
  30 +import { PageComponent } from '@shared/components/page.component';
  31 +import { Store } from '@ngrx/store';
  32 +import { AppState } from '@core/core.state';
  33 +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, NgForm, Validators } from '@angular/forms';
  34 +import { FcRuleNode, FcRuleEdge } from './rulechain-page.models';
  35 +import { RuleNodeType, LinkLabel, RuleNodeDefinition, RuleNodeConfiguration, IRuleNodeConfigurationComponent } from '@shared/models/rule-node.models';
  36 +import { EntityType } from '@shared/models/entity-type.models';
  37 +import { Observable, of, Subscription } from 'rxjs';
  38 +import { RuleChainService } from '@core/http/rule-chain.service';
  39 +import { coerceBooleanProperty } from '@angular/cdk/coercion';
  40 +import { deepClone } from '@core/utils';
  41 +import { EntityAlias } from '@shared/models/alias.models';
  42 +import { TruncatePipe } from '@shared/pipe/truncate.pipe';
  43 +import { MatChipList, MatAutocomplete, MatChipInputEvent, MatAutocompleteSelectedEvent } from '@angular/material';
  44 +import { TranslateService } from '@ngx-translate/core';
  45 +import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes';
  46 +import { catchError, map, mergeMap, share } from 'rxjs/operators';
  47 +import { DynamicWidgetComponent } from '@home/components/widget/dynamic-widget.component';
  48 +import { SharedModule } from '@shared/shared.module';
  49 +import { WidgetComponentsModule } from '@home/components/widget/widget-components.module';
  50 +import { DynamicComponentFactoryService } from '@core/services/dynamic-component-factory.service';
  51 +import { ViewContainerRef } from '@angular/core';
  52 +import { JsonObjectEditComponent } from '@shared/components/json-object-edit.component';
  53 +
  54 +@Component({
  55 + selector: 'tb-rule-node-config',
  56 + templateUrl: './rule-node-config.component.html',
  57 + styleUrls: ['./rule-node-config.component.scss'],
  58 + providers: [{
  59 + provide: NG_VALUE_ACCESSOR,
  60 + useExisting: forwardRef(() => RuleNodeConfigComponent),
  61 + multi: true
  62 + }]
  63 +})
  64 +export class RuleNodeConfigComponent implements ControlValueAccessor, OnInit, OnDestroy, AfterViewInit {
  65 +
  66 + @ViewChild('definedConfigContent', {read: ViewContainerRef, static: true}) definedConfigContainer: ViewContainerRef;
  67 +
  68 + @ViewChild('jsonObjectEditComponent', {static: false}) jsonObjectEditComponent: JsonObjectEditComponent;
  69 +
  70 + private requiredValue: boolean;
  71 + get required(): boolean {
  72 + return this.requiredValue;
  73 + }
  74 + @Input()
  75 + set required(value: boolean) {
  76 + this.requiredValue = coerceBooleanProperty(value);
  77 + }
  78 +
  79 + @Input()
  80 + disabled: boolean;
  81 +
  82 + @Input()
  83 + ruleNodeId: string;
  84 +
  85 + nodeDefinitionValue: RuleNodeDefinition;
  86 +
  87 + @Input()
  88 + set nodeDefinition(nodeDefinition: RuleNodeDefinition) {
  89 + if (this.nodeDefinitionValue !== nodeDefinition) {
  90 + this.nodeDefinitionValue = nodeDefinition;
  91 + if (this.nodeDefinitionValue) {
  92 + this.validateDefinedDirective();
  93 + }
  94 + }
  95 + }
  96 +
  97 + get nodeDefinition(): RuleNodeDefinition {
  98 + return this.nodeDefinitionValue;
  99 + }
  100 +
  101 + definedDirectiveError: string;
  102 +
  103 + ruleNodeConfigFormGroup: FormGroup;
  104 +
  105 + changeSubscription: Subscription;
  106 +
  107 + private definedConfigComponentRef: ComponentRef<IRuleNodeConfigurationComponent>;
  108 + private definedConfigComponent: IRuleNodeConfigurationComponent;
  109 +
  110 + private configuration: RuleNodeConfiguration;
  111 +
  112 + private propagateChange = (v: any) => { };
  113 +
  114 + constructor(private translate: TranslateService,
  115 + private ruleChainService: RuleChainService,
  116 + private fb: FormBuilder) {
  117 + this.ruleNodeConfigFormGroup = this.fb.group({
  118 + configuration: [null, Validators.required]
  119 + });
  120 + }
  121 +
  122 + registerOnChange(fn: any): void {
  123 + this.propagateChange = fn;
  124 + }
  125 +
  126 + registerOnTouched(fn: any): void {
  127 + }
  128 +
  129 + ngOnInit(): void {
  130 + }
  131 +
  132 + ngOnDestroy(): void {
  133 + if (this.definedConfigComponentRef) {
  134 + this.definedConfigComponentRef.destroy();
  135 + }
  136 + }
  137 +
  138 + ngAfterViewInit(): void {
  139 + }
  140 +
  141 + setDisabledState(isDisabled: boolean): void {
  142 + this.disabled = isDisabled;
  143 + if (this.disabled) {
  144 + this.ruleNodeConfigFormGroup.disable({emitEvent: false});
  145 + } else {
  146 + this.ruleNodeConfigFormGroup.enable({emitEvent: false});
  147 + }
  148 + }
  149 +
  150 + writeValue(value: RuleNodeConfiguration): void {
  151 + this.configuration = value;
  152 + if (this.changeSubscription) {
  153 + this.changeSubscription.unsubscribe();
  154 + this.changeSubscription = null;
  155 + }
  156 + if (this.definedConfigComponent) {
  157 + this.definedConfigComponent.configuration = this.configuration;
  158 + this.changeSubscription = this.definedConfigComponent.configurationChanged.subscribe((configuration) => {
  159 + this.updateModel(configuration);
  160 + });
  161 + } else {
  162 + this.ruleNodeConfigFormGroup.get('configuration').patchValue(value, {emitEvent: false});
  163 + this.changeSubscription = this.ruleNodeConfigFormGroup.get('configuration').valueChanges.subscribe(
  164 + (configuration: RuleNodeConfiguration) => {
  165 + this.updateModel(configuration);
  166 + }
  167 + );
  168 + }
  169 + }
  170 +
  171 + useDefinedDirective(): boolean {
  172 + return this.nodeDefinition &&
  173 + (this.nodeDefinition.configDirective &&
  174 + this.nodeDefinition.configDirective.length) && !this.definedDirectiveError;
  175 + }
  176 +
  177 + private updateModel(configuration: RuleNodeConfiguration) {
  178 + if (this.definedConfigComponent || this.ruleNodeConfigFormGroup.valid) {
  179 + this.propagateChange(configuration);
  180 + } else {
  181 + this.propagateChange(this.required ? null : configuration);
  182 + }
  183 + }
  184 +
  185 + private validateDefinedDirective() {
  186 + if (this.definedConfigComponentRef) {
  187 + this.definedConfigComponentRef.destroy();
  188 + this.definedConfigComponentRef = null;
  189 + }
  190 + if (this.nodeDefinition.uiResourceLoadError && this.nodeDefinition.uiResourceLoadError.length) {
  191 + this.definedDirectiveError = this.nodeDefinition.uiResourceLoadError;
  192 + } else if (this.nodeDefinition.configDirective && this.nodeDefinition.configDirective.length) {
  193 + if (this.changeSubscription) {
  194 + this.changeSubscription.unsubscribe();
  195 + this.changeSubscription = null;
  196 + }
  197 + this.definedConfigContainer.clear();
  198 + const factory = this.ruleChainService.getRuleNodeConfigFactory(this.nodeDefinition.configDirective);
  199 + this.definedConfigComponentRef = this.definedConfigContainer.createComponent(factory);
  200 + this.definedConfigComponent = this.definedConfigComponentRef.instance;
  201 + this.definedConfigComponent.ruleNodeId = this.ruleNodeId;
  202 + this.definedConfigComponent.configuration = this.configuration;
  203 + this.changeSubscription = this.definedConfigComponent.configurationChanged.subscribe((configuration) => {
  204 + this.updateModel(configuration);
  205 + });
  206 + }
  207 + }
  208 +}
@@ -31,7 +31,11 @@ @@ -31,7 +31,11 @@
31 {{ 'rulenode.debug-mode' | translate }} 31 {{ 'rulenode.debug-mode' | translate }}
32 </mat-checkbox> 32 </mat-checkbox>
33 </section> 33 </section>
34 - TODO: tb-rule-node-config 34 + <tb-rule-node-config
  35 + formControlName="configuration"
  36 + [ruleNodeId]="ruleNode.ruleNodeId?.id"
  37 + [nodeDefinition]="ruleNode.component.configurationDescriptor.nodeDefinition">
  38 + </tb-rule-node-config>
35 <div formGroupName="additionalInfo" fxLayout="column"> 39 <div formGroupName="additionalInfo" fxLayout="column">
36 <mat-form-field class="mat-block"> 40 <mat-form-field class="mat-block">
37 <mat-label translate>rulenode.description</mat-label> 41 <mat-label translate>rulenode.description</mat-label>
@@ -70,6 +70,7 @@ export class RuleNodeDetailsComponent extends PageComponent implements OnInit, O @@ -70,6 +70,7 @@ export class RuleNodeDetailsComponent extends PageComponent implements OnInit, O
70 this.ruleNodeFormGroup = this.fb.group({ 70 this.ruleNodeFormGroup = this.fb.group({
71 name: [this.ruleNode.name, [Validators.required]], 71 name: [this.ruleNode.name, [Validators.required]],
72 debugMode: [this.ruleNode.debugMode, []], 72 debugMode: [this.ruleNode.debugMode, []],
  73 + configuration: [this.ruleNode.configuration, [Validators.required]],
73 additionalInfo: this.fb.group( 74 additionalInfo: this.fb.group(
74 { 75 {
75 description: [this.ruleNode.additionalInfo ? this.ruleNode.additionalInfo.description : ''], 76 description: [this.ruleNode.additionalInfo ? this.ruleNode.additionalInfo.description : ''],
@@ -151,7 +151,6 @@ export class RuleChainPageComponent extends PageComponent @@ -151,7 +151,6 @@ export class RuleChainPageComponent extends PageComponent
151 return source.type === FlowchartConstants.rightConnectorType && destination.type === FlowchartConstants.leftConnectorType; 151 return source.type === FlowchartConstants.rightConnectorType && destination.type === FlowchartConstants.leftConnectorType;
152 }, 152 },
153 createEdge: (event, edge: FcRuleEdge) => { 153 createEdge: (event, edge: FcRuleEdge) => {
154 - console.log('TODO');  
155 const sourceNode = this.ruleChainCanvas.modelService.nodes.getNodeByConnectorId(edge.source) as FcRuleNode; 154 const sourceNode = this.ruleChainCanvas.modelService.nodes.getNodeByConnectorId(edge.source) as FcRuleNode;
156 if (sourceNode.component.type === RuleNodeType.INPUT) { 155 if (sourceNode.component.type === RuleNodeType.INPUT) {
157 const destNode = this.ruleChainCanvas.modelService.nodes.getNodeByConnectorId(edge.destination) as FcRuleNode; 156 const destNode = this.ruleChainCanvas.modelService.nodes.getNodeByConnectorId(edge.destination) as FcRuleNode;
@@ -185,9 +184,8 @@ export class RuleChainPageComponent extends PageComponent @@ -185,9 +184,8 @@ export class RuleChainPageComponent extends PageComponent
185 } 184 }
186 } 185 }
187 }, 186 },
188 - dropNode: (event, node) => {  
189 - console.log('TODO dropNode');  
190 - console.log(node); 187 + dropNode: (event, node: FcRuleNode) => {
  188 + this.addRuleNode(node);
191 } 189 }
192 }; 190 };
193 191
@@ -269,7 +267,7 @@ export class RuleChainPageComponent extends PageComponent @@ -269,7 +267,7 @@ export class RuleChainPageComponent extends PageComponent
269 this.createRuleChainModel(); 267 this.createRuleChainModel();
270 } 268 }
271 269
272 - private updateRuleChainLibrary() { 270 + updateRuleChainLibrary() {
273 const search = this.ruleNodeTypeSearch.toUpperCase(); 271 const search = this.ruleNodeTypeSearch.toUpperCase();
274 const res = this.ruleNodeComponents.filter( 272 const res = this.ruleNodeComponents.filter(
275 (ruleNodeComponent) => ruleNodeComponent.name.toUpperCase().includes(search)); 273 (ruleNodeComponent) => ruleNodeComponent.name.toUpperCase().includes(search));
@@ -734,6 +732,46 @@ export class RuleChainPageComponent extends PageComponent @@ -734,6 +732,46 @@ export class RuleChainPageComponent extends PageComponent
734 this.createRuleChainModel(); 732 this.createRuleChainModel();
735 } 733 }
736 734
  735 + addRuleNode(ruleNode: FcRuleNode) {
  736 + ruleNode.configuration = deepClone(ruleNode.component.configurationDescriptor.nodeDefinition.defaultConfiguration);
  737 + const ruleChainId = this.ruleChain.id ? this.ruleChain.id.id : null;
  738 + this.dialog.open<AddRuleNodeDialogComponent, AddRuleNodeDialogData,
  739 + FcRuleNode>(AddRuleNodeDialogComponent, {
  740 + disableClose: true,
  741 + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
  742 + data: {
  743 + ruleNode,
  744 + ruleChainId
  745 + }
  746 + }).afterClosed().subscribe(
  747 + (addedRuleNode) => {
  748 + if (addedRuleNode) {
  749 + addedRuleNode.id = 'rule-chain-node-' + this.nextNodeID++;
  750 + addedRuleNode.connectors = [];
  751 + if (addedRuleNode.component.configurationDescriptor.nodeDefinition.inEnabled) {
  752 + addedRuleNode.connectors.push(
  753 + {
  754 + id: (this.nextConnectorID++) + '',
  755 + type: FlowchartConstants.leftConnectorType
  756 + }
  757 + );
  758 + }
  759 + if (addedRuleNode.component.configurationDescriptor.nodeDefinition.outEnabled) {
  760 + addedRuleNode.connectors.push(
  761 + {
  762 + id: (this.nextConnectorID++) + '',
  763 + type: FlowchartConstants.rightConnectorType
  764 + }
  765 + );
  766 + }
  767 + this.ruleChainModel.nodes.push(addedRuleNode);
  768 + this.onModelChanged();
  769 + this.updateRuleNodesHighlight();
  770 + }
  771 + }
  772 + );
  773 + }
  774 +
737 addRuleNodeLink(link: FcRuleEdge, labels: {[label: string]: LinkLabel}, allowCustomLabels: boolean): Observable<FcRuleEdge> { 775 addRuleNodeLink(link: FcRuleEdge, labels: {[label: string]: LinkLabel}, allowCustomLabels: boolean): Observable<FcRuleEdge> {
738 return this.dialog.open<AddRuleNodeLinkDialogComponent, AddRuleNodeLinkDialogData, 776 return this.dialog.open<AddRuleNodeLinkDialogComponent, AddRuleNodeLinkDialogData,
739 FcRuleEdge>(AddRuleNodeLinkDialogComponent, { 777 FcRuleEdge>(AddRuleNodeLinkDialogComponent, {
@@ -885,3 +923,56 @@ export class AddRuleNodeLinkDialogComponent extends DialogComponent<AddRuleNodeL @@ -885,3 +923,56 @@ export class AddRuleNodeLinkDialogComponent extends DialogComponent<AddRuleNodeL
885 this.dialogRef.close(this.link); 923 this.dialogRef.close(this.link);
886 } 924 }
887 } 925 }
  926 +
  927 +export interface AddRuleNodeDialogData {
  928 + ruleNode: FcRuleNode;
  929 + ruleChainId: string;
  930 +}
  931 +
  932 +@Component({
  933 + selector: 'tb-add-rule-node-dialog',
  934 + templateUrl: './add-rule-node-dialog.component.html',
  935 + providers: [{provide: ErrorStateMatcher, useExisting: AddRuleNodeDialogComponent}],
  936 + styleUrls: []
  937 +})
  938 +export class AddRuleNodeDialogComponent extends DialogComponent<AddRuleNodeDialogComponent, FcRuleNode>
  939 + implements OnInit, ErrorStateMatcher {
  940 +
  941 + ruleNode: FcRuleNode;
  942 + ruleChainId: string;
  943 +
  944 + submitted = false;
  945 +
  946 + constructor(protected store: Store<AppState>,
  947 + protected router: Router,
  948 + @Inject(MAT_DIALOG_DATA) public data: AddRuleNodeDialogData,
  949 + @SkipSelf() private errorStateMatcher: ErrorStateMatcher,
  950 + public dialogRef: MatDialogRef<AddRuleNodeDialogComponent, FcRuleNode>) {
  951 + super(store, router, dialogRef);
  952 +
  953 + this.ruleNode = this.data.ruleNode;
  954 + this.ruleChainId = this.data.ruleChainId;
  955 + }
  956 +
  957 + ngOnInit(): void {
  958 + }
  959 +
  960 + isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
  961 + const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
  962 + const customErrorState = !!(control && control.invalid && this.submitted);
  963 + return originalErrorState || customErrorState;
  964 + }
  965 +
  966 + helpLinkIdForRuleNodeType(): string {
  967 + return getRuleNodeHelpLink(this.ruleNode.component);
  968 + }
  969 +
  970 + cancel(): void {
  971 + this.dialogRef.close(null);
  972 + }
  973 +
  974 + add(): void {
  975 + this.submitted = true;
  976 + this.dialogRef.close(this.ruleNode);
  977 + }
  978 +}
@@ -39,6 +39,29 @@ import { RuleChainPageComponent } from '@home/pages/rulechain/rulechain-page.com @@ -39,6 +39,29 @@ import { RuleChainPageComponent } from '@home/pages/rulechain/rulechain-page.com
39 import { RuleNodeComponentDescriptor } from '@shared/models/rule-node.models'; 39 import { RuleNodeComponentDescriptor } from '@shared/models/rule-node.models';
40 import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; 40 import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard';
41 41
  42 +import * as AngularCommon from '@angular/common';
  43 +import * as AngularCore from '@angular/core';
  44 +import * as AngularForms from '@angular/forms';
  45 +import * as AngularCdkCoercion from '@angular/cdk/coercion';
  46 +import * as NgrxStore from '@ngrx/store';
  47 +import * as TranslateCore from '@ngx-translate/core';
  48 +import * as TbCore from '@core/public-api';
  49 +import * as TbShared from '@shared/public-api';
  50 +
  51 +declare const SystemJS;
  52 +
  53 +const ruleNodeConfigResourcesModulesMap = {
  54 + '@angular/core': SystemJS.newModule(AngularCore),
  55 + '@angular/common': SystemJS.newModule(AngularCommon),
  56 + '@angular/forms': SystemJS.newModule(AngularForms),
  57 + '@ngrx/store': SystemJS.newModule(NgrxStore),
  58 + '@ngx-translate/core': SystemJS.newModule(TranslateCore),
  59 + '@core/public-api': SystemJS.newModule(TbCore),
  60 + '@shared/public-api': SystemJS.newModule(TbShared)
  61 +};
  62 +
  63 +const t = SystemJS.newModule(AngularCore);
  64 +
42 65
43 @Injectable() 66 @Injectable()
44 export class RuleChainResolver implements Resolve<RuleChain> { 67 export class RuleChainResolver implements Resolve<RuleChain> {
@@ -71,7 +94,7 @@ export class RuleNodeComponentsResolver implements Resolve<Array<RuleNodeCompone @@ -71,7 +94,7 @@ export class RuleNodeComponentsResolver implements Resolve<Array<RuleNodeCompone
71 } 94 }
72 95
73 resolve(route: ActivatedRouteSnapshot): Observable<Array<RuleNodeComponentDescriptor>> { 96 resolve(route: ActivatedRouteSnapshot): Observable<Array<RuleNodeComponentDescriptor>> {
74 - return this.ruleChainService.getRuleNodeComponents(); 97 + return this.ruleChainService.getRuleNodeComponents(ruleNodeConfigResourcesModulesMap);
75 } 98 }
76 } 99 }
77 100
@@ -150,6 +173,7 @@ const routes: Routes = [ @@ -150,6 +173,7 @@ const routes: Routes = [
150 } 173 }
151 ]; 174 ];
152 175
  176 +// @dynamic
153 @NgModule({ 177 @NgModule({
154 imports: [RouterModule.forChild(routes)], 178 imports: [RouterModule.forChild(routes)],
155 exports: [RouterModule], 179 exports: [RouterModule],
@@ -21,19 +21,21 @@ import {RuleChainComponent} from '@modules/home/pages/rulechain/rulechain.compon @@ -21,19 +21,21 @@ import {RuleChainComponent} from '@modules/home/pages/rulechain/rulechain.compon
21 import {RuleChainRoutingModule} from '@modules/home/pages/rulechain/rulechain-routing.module'; 21 import {RuleChainRoutingModule} from '@modules/home/pages/rulechain/rulechain-routing.module';
22 import {HomeComponentsModule} from '@modules/home/components/home-components.module'; 22 import {HomeComponentsModule} from '@modules/home/components/home-components.module';
23 import { RuleChainTabsComponent } from '@home/pages/rulechain/rulechain-tabs.component'; 23 import { RuleChainTabsComponent } from '@home/pages/rulechain/rulechain-tabs.component';
24 -import { RuleChainPageComponent, AddRuleNodeLinkDialogComponent } from './rulechain-page.component'; 24 +import { RuleChainPageComponent, AddRuleNodeLinkDialogComponent, AddRuleNodeDialogComponent } from './rulechain-page.component';
25 import { RuleNodeComponent } from '@home/pages/rulechain/rulenode.component'; 25 import { RuleNodeComponent } from '@home/pages/rulechain/rulenode.component';
26 import { FC_NODE_COMPONENT_CONFIG } from 'ngx-flowchart/dist/ngx-flowchart'; 26 import { FC_NODE_COMPONENT_CONFIG } from 'ngx-flowchart/dist/ngx-flowchart';
27 import { RuleNodeDetailsComponent } from './rule-node-details.component'; 27 import { RuleNodeDetailsComponent } from './rule-node-details.component';
28 import { RuleNodeLinkComponent } from './rule-node-link.component'; 28 import { RuleNodeLinkComponent } from './rule-node-link.component';
29 import { LinkLabelsComponent } from '@home/pages/rulechain/link-labels.conponent'; 29 import { LinkLabelsComponent } from '@home/pages/rulechain/link-labels.conponent';
  30 +import { RuleNodeConfigComponent } from './rule-node-config.component';
30 31
31 @NgModule({ 32 @NgModule({
32 entryComponents: [ 33 entryComponents: [
33 RuleChainComponent, 34 RuleChainComponent,
34 RuleChainTabsComponent, 35 RuleChainTabsComponent,
35 RuleNodeComponent, 36 RuleNodeComponent,
36 - AddRuleNodeLinkDialogComponent 37 + AddRuleNodeLinkDialogComponent,
  38 + AddRuleNodeDialogComponent
37 ], 39 ],
38 declarations: [ 40 declarations: [
39 RuleChainComponent, 41 RuleChainComponent,
@@ -41,9 +43,11 @@ import { LinkLabelsComponent } from '@home/pages/rulechain/link-labels.conponent @@ -41,9 +43,11 @@ import { LinkLabelsComponent } from '@home/pages/rulechain/link-labels.conponent
41 RuleChainPageComponent, 43 RuleChainPageComponent,
42 RuleNodeComponent, 44 RuleNodeComponent,
43 RuleNodeDetailsComponent, 45 RuleNodeDetailsComponent,
  46 + RuleNodeConfigComponent,
44 LinkLabelsComponent, 47 LinkLabelsComponent,
45 RuleNodeLinkComponent, 48 RuleNodeLinkComponent,
46 - AddRuleNodeLinkDialogComponent 49 + AddRuleNodeLinkDialogComponent,
  50 + AddRuleNodeDialogComponent
47 ], 51 ],
48 providers: [ 52 providers: [
49 { 53 {
@@ -34,8 +34,8 @@ @@ -34,8 +34,8 @@
34 <mat-form-field class="mat-block"> 34 <mat-form-field class="mat-block">
35 <mat-label translate>user.activation-method</mat-label> 35 <mat-label translate>user.activation-method</mat-label>
36 <mat-select matInput [ngModelOptions]="{standalone: true}" [(ngModel)]="activationMethod"> 36 <mat-select matInput [ngModelOptions]="{standalone: true}" [(ngModel)]="activationMethod">
37 - <mat-option *ngFor="let activationMethod of (activationMethods | enumToArray)" [value]="activationMethods[activationMethod]">  
38 - {{ activationMethodTranslations.get(activationMethods[activationMethod]) | translate }} 37 + <mat-option *ngFor="let activationMethod of activationMethods" [value]="activationMethod">
  38 + {{ activationMethodTranslations.get(activationMethod) | translate }}
39 </mat-option> 39 </mat-option>
40 </mat-select> 40 </mat-select>
41 </mat-form-field> 41 </mat-form-field>
@@ -49,7 +49,7 @@ export class AddUserDialogComponent extends DialogComponent<AddUserDialogCompone @@ -49,7 +49,7 @@ export class AddUserDialogComponent extends DialogComponent<AddUserDialogCompone
49 detailsForm: NgForm; 49 detailsForm: NgForm;
50 user: User; 50 user: User;
51 51
52 - activationMethods = ActivationMethod; 52 + activationMethods = Object.keys(ActivationMethod);
53 53
54 activationMethodTranslations = activationMethodTranslations; 54 activationMethodTranslations = activationMethodTranslations;
55 55
@@ -48,6 +48,7 @@ import { @@ -48,6 +48,7 @@ import {
48 } from '@home/pages/widget/save-widget-type-as-dialog.component'; 48 } from '@home/pages/widget/save-widget-type-as-dialog.component';
49 import { Subscription } from 'rxjs'; 49 import { Subscription } from 'rxjs';
50 50
  51 +// @dynamic
51 @Component({ 52 @Component({
52 selector: 'tb-widget-editor', 53 selector: 'tb-widget-editor',
53 templateUrl: './widget-editor.component.html', 54 templateUrl: './widget-editor.component.html',
@@ -201,6 +201,7 @@ export const routes: Routes = [ @@ -201,6 +201,7 @@ export const routes: Routes = [
201 } 201 }
202 ]; 202 ];
203 203
  204 +// @dynamic
204 @NgModule({ 205 @NgModule({
205 imports: [RouterModule.forChild(routes)], 206 imports: [RouterModule.forChild(routes)],
206 exports: [RouterModule], 207 exports: [RouterModule],
  1 +///
  2 +/// Copyright © 2016-2019 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +export * from './home.module';
@@ -81,7 +81,7 @@ export class DashboardAutocompleteComponent implements ControlValueAccessor, OnI @@ -81,7 +81,7 @@ export class DashboardAutocompleteComponent implements ControlValueAccessor, OnI
81 81
82 filteredDashboards: Observable<Array<DashboardInfo>>; 82 filteredDashboards: Observable<Array<DashboardInfo>>;
83 83
84 - private searchText = ''; 84 + searchText = '';
85 85
86 private propagateChange = (v: any) => { }; 86 private propagateChange = (v: any) => { };
87 87
@@ -40,6 +40,7 @@ import { @@ -40,6 +40,7 @@ import {
40 } from './dashboard-select-panel.component'; 40 } from './dashboard-select-panel.component';
41 import { NULL_UUID } from '@shared/models/id/has-uuid'; 41 import { NULL_UUID } from '@shared/models/id/has-uuid';
42 42
  43 +// @dynamic
43 @Component({ 44 @Component({
44 selector: 'tb-dashboard-select', 45 selector: 'tb-dashboard-select',
45 templateUrl: './dashboard-select.component.html', 46 templateUrl: './dashboard-select.component.html',
@@ -94,7 +94,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit @@ -94,7 +94,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit
94 94
95 filteredEntities: Observable<Array<BaseData<EntityId>>>; 95 filteredEntities: Observable<Array<BaseData<EntityId>>>;
96 96
97 - private searchText = ''; 97 + searchText = '';
98 98
99 private dirty = false; 99 private dirty = false;
100 100
@@ -92,7 +92,7 @@ export class EntityKeysListComponent implements ControlValueAccessor, OnInit, Af @@ -92,7 +92,7 @@ export class EntityKeysListComponent implements ControlValueAccessor, OnInit, Af
92 92
93 separatorKeysCodes: number[] = [ENTER, COMMA, SEMICOLON]; 93 separatorKeysCodes: number[] = [ENTER, COMMA, SEMICOLON];
94 94
95 - private searchText = ''; 95 + searchText = '';
96 96
97 private dirty = false; 97 private dirty = false;
98 98
@@ -90,7 +90,7 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, AfterV @@ -90,7 +90,7 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, AfterV
90 entities: Array<BaseData<EntityId>> = []; 90 entities: Array<BaseData<EntityId>> = [];
91 filteredEntities: Observable<Array<BaseData<EntityId>>>; 91 filteredEntities: Observable<Array<BaseData<EntityId>>>;
92 92
93 - private searchText = ''; 93 + searchText = '';
94 94
95 private dirty = false; 95 private dirty = false;
96 96
@@ -79,7 +79,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor, @@ -79,7 +79,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor,
79 79
80 private broadcastSubscription: Subscription; 80 private broadcastSubscription: Subscription;
81 81
82 - private searchText = ''; 82 + searchText = '';
83 83
84 private dirty = false; 84 private dirty = false;
85 85
@@ -106,7 +106,7 @@ export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit, @@ -106,7 +106,7 @@ export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit,
106 106
107 separatorKeysCodes: number[] = [ENTER, COMMA, SEMICOLON]; 107 separatorKeysCodes: number[] = [ENTER, COMMA, SEMICOLON];
108 108
109 - private searchText = ''; 109 + searchText = '';
110 110
111 private dirty = false; 111 private dirty = false;
112 112
@@ -102,7 +102,7 @@ export class EntityTypeListComponent implements ControlValueAccessor, OnInit, Af @@ -102,7 +102,7 @@ export class EntityTypeListComponent implements ControlValueAccessor, OnInit, Af
102 placeholder: string; 102 placeholder: string;
103 secondaryPlaceholder: string; 103 secondaryPlaceholder: string;
104 104
105 - private searchText = ''; 105 + searchText = '';
106 106
107 private dirty = false; 107 private dirty = false;
108 108
@@ -63,6 +63,7 @@ export class FabActionsDirective implements OnInit { @@ -63,6 +63,7 @@ export class FabActionsDirective implements OnInit {
63 63
64 } 64 }
65 65
  66 +// @dynamic
66 @Component({ 67 @Component({
67 selector: 'mat-fab-toolbar', 68 selector: 'mat-fab-toolbar',
68 templateUrl: './fab-toolbar.component.html', 69 templateUrl: './fab-toolbar.component.html',
@@ -35,7 +35,7 @@ import { deepClone, isString } from '@app/core/utils'; @@ -35,7 +35,7 @@ import { deepClone, isString } from '@app/core/utils';
35 import { TranslateService } from '@ngx-translate/core'; 35 import { TranslateService } from '@ngx-translate/core';
36 import { JsonFormProps } from './react/json-form.models'; 36 import { JsonFormProps } from './react/json-form.models';
37 import inspector from 'schema-inspector'; 37 import inspector from 'schema-inspector';
38 -import * as tinycolor from 'tinycolor2'; 38 +import * as tinycolor_ from 'tinycolor2';
39 import { DialogService } from '@app/core/services/dialog.service'; 39 import { DialogService } from '@app/core/services/dialog.service';
40 import * as React from 'react'; 40 import * as React from 'react';
41 import * as ReactDOM from 'react-dom'; 41 import * as ReactDOM from 'react-dom';
@@ -43,6 +43,8 @@ import ReactSchemaForm from './react/json-form-react'; @@ -43,6 +43,8 @@ import ReactSchemaForm from './react/json-form-react';
43 import JsonFormUtils from './react/json-form-utils'; 43 import JsonFormUtils from './react/json-form-utils';
44 import { JsonFormComponentData } from './json-form-component.models'; 44 import { JsonFormComponentData } from './json-form-component.models';
45 45
  46 +const tinycolor = tinycolor_;
  47 +
46 @Component({ 48 @Component({
47 selector: 'tb-json-form', 49 selector: 'tb-json-form',
48 templateUrl: './json-form.component.html', 50 templateUrl: './json-form.component.html',
@@ -20,7 +20,6 @@ import reactCSS from 'reactcss'; @@ -20,7 +20,6 @@ import reactCSS from 'reactcss';
20 import ReactAce from 'react-ace'; 20 import ReactAce from 'react-ace';
21 import Button from '@material-ui/core/Button'; 21 import Button from '@material-ui/core/Button';
22 import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; 22 import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models';
23 -import * as tinycolor from 'tinycolor2';  
24 import { IEditorProps } from 'react-ace/src/types'; 23 import { IEditorProps } from 'react-ace/src/types';
25 24
26 interface ThingsboardAceEditorProps extends JsonFormFieldProps { 25 interface ThingsboardAceEditorProps extends JsonFormFieldProps {
@@ -17,13 +17,15 @@ import * as React from 'react'; @@ -17,13 +17,15 @@ import * as React from 'react';
17 import * as ReactDOM from 'react-dom'; 17 import * as ReactDOM from 'react-dom';
18 import ThingsboardBaseComponent from './json-form-base-component'; 18 import ThingsboardBaseComponent from './json-form-base-component';
19 import reactCSS from 'reactcss'; 19 import reactCSS from 'reactcss';
20 -import * as tinycolor from 'tinycolor2'; 20 +import * as tinycolor_ from 'tinycolor2';
21 import TextField from '@material-ui/core/TextField'; 21 import TextField from '@material-ui/core/TextField';
22 import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; 22 import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models';
23 import IconButton from '@material-ui/core/IconButton'; 23 import IconButton from '@material-ui/core/IconButton';
24 import ClearIcon from '@material-ui/icons/Clear'; 24 import ClearIcon from '@material-ui/icons/Clear';
25 import Tooltip from '@material-ui/core/Tooltip'; 25 import Tooltip from '@material-ui/core/Tooltip';
26 26
  27 +const tinycolor = tinycolor_;
  28 +
27 interface ThingsboardColorState extends JsonFormFieldState { 29 interface ThingsboardColorState extends JsonFormFieldState {
28 color: tinycolor.ColorFormats.RGBA | null; 30 color: tinycolor.ColorFormats.RGBA | null;
29 focused: boolean; 31 focused: boolean;
@@ -35,7 +35,8 @@ import ThingsboardFieldSet from './json-form-fieldset'; @@ -35,7 +35,8 @@ import ThingsboardFieldSet from './json-form-fieldset';
35 import { JsonFormProps, GroupInfo, JsonFormData, onChangeFn, OnColorClickFn } from './json-form.models'; 35 import { JsonFormProps, GroupInfo, JsonFormData, onChangeFn, OnColorClickFn } from './json-form.models';
36 36
37 import _ from 'lodash'; 37 import _ from 'lodash';
38 -import * as tinycolor from 'tinycolor2'; 38 +import * as tinycolor_ from 'tinycolor2';
  39 +const tinycolor = tinycolor_;
39 40
40 class ThingsboardSchemaForm extends React.Component<JsonFormProps, any> { 41 class ThingsboardSchemaForm extends React.Component<JsonFormProps, any> {
41 42
@@ -18,7 +18,9 @@ import { isUndefined, isDefined, isString } from '@app/core/utils'; @@ -18,7 +18,9 @@ import { isUndefined, isDefined, isString } from '@app/core/utils';
18 import * as equal from 'deep-equal'; 18 import * as equal from 'deep-equal';
19 import ObjectPath from 'objectpath'; 19 import ObjectPath from 'objectpath';
20 import * as React from 'react'; 20 import * as React from 'react';
21 -import * as tinycolor from 'tinycolor2'; 21 +import * as tinycolor_ from 'tinycolor2';
  22 +
  23 +const tinycolor = tinycolor_;
22 24
23 export interface SchemaValidationResult { 25 export interface SchemaValidationResult {
24 valid: boolean; 26 valid: boolean;
@@ -49,7 +49,7 @@ export class MatChipDraggableDirective implements AfterViewInit { @@ -49,7 +49,7 @@ export class MatChipDraggableDirective implements AfterViewInit {
49 private elementRef: ElementRef<HTMLElement>) { 49 private elementRef: ElementRef<HTMLElement>) {
50 } 50 }
51 51
52 - @HostListener('document:mouseup', ['$event']) 52 + @HostListener('document:mouseup')
53 onDocumentMouseUp() { 53 onDocumentMouseUp() {
54 this.draggableChips.forEach((draggableChip) => { 54 this.draggableChips.forEach((draggableChip) => {
55 draggableChip.preventDrag = false; 55 draggableChip.preventDrag = false;
  1 +///
  2 +/// Copyright © 2016-2019 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +export * from './page.component';
@@ -69,7 +69,7 @@ export class RelationTypeAutocompleteComponent implements ControlValueAccessor, @@ -69,7 +69,7 @@ export class RelationTypeAutocompleteComponent implements ControlValueAccessor,
69 69
70 filteredRelationTypes: Observable<Array<string>>; 70 filteredRelationTypes: Observable<Array<string>>;
71 71
72 - private searchText = ''; 72 + searchText = '';
73 73
74 private dirty = false; 74 private dirty = false;
75 75
@@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
17 --> 17 -->
18 <button *ngIf="asButton" cdkOverlayOrigin #timewindowPanelOrigin="cdkOverlayOrigin" [disabled]="disabled" 18 <button *ngIf="asButton" cdkOverlayOrigin #timewindowPanelOrigin="cdkOverlayOrigin" [disabled]="disabled"
19 type="button" 19 type="button"
20 - mat-raised-button color="primary" (click)="openEditMode($event)"> 20 + mat-raised-button color="primary" (click)="openEditMode()">
21 <mat-icon class="material-icons">query_builder</mat-icon> 21 <mat-icon class="material-icons">query_builder</mat-icon>
22 <span>{{innerValue?.displayValue}}</span> 22 <span>{{innerValue?.displayValue}}</span>
23 </button> 23 </button>
@@ -25,20 +25,20 @@ @@ -25,20 +25,20 @@
25 class="tb-timewindow" fxLayout="row" fxLayoutAlign="start center"> 25 class="tb-timewindow" fxLayout="row" fxLayoutAlign="start center">
26 <button *ngIf="direction === 'left'" [disabled]="disabled" mat-button mat-icon-button class="tb-mat-32" 26 <button *ngIf="direction === 'left'" [disabled]="disabled" mat-button mat-icon-button class="tb-mat-32"
27 type="button" 27 type="button"
28 - (click)="openEditMode($event)" 28 + (click)="openEditMode()"
29 matTooltip="{{ 'timewindow.edit' | translate }}" 29 matTooltip="{{ 'timewindow.edit' | translate }}"
30 [matTooltipPosition]="tooltipPosition"> 30 [matTooltipPosition]="tooltipPosition">
31 <mat-icon class="material-icons">query_builder</mat-icon> 31 <mat-icon class="material-icons">query_builder</mat-icon>
32 </button> 32 </button>
33 <span [fxHide]="hideLabel()" 33 <span [fxHide]="hideLabel()"
34 - (click)="openEditMode($event)" 34 + (click)="openEditMode()"
35 matTooltip="{{ 'timewindow.edit' | translate }}" 35 matTooltip="{{ 'timewindow.edit' | translate }}"
36 [matTooltipPosition]="tooltipPosition"> 36 [matTooltipPosition]="tooltipPosition">
37 {{innerValue?.displayValue}} 37 {{innerValue?.displayValue}}
38 </span> 38 </span>
39 <button *ngIf="direction === 'right'" [disabled]="disabled" mat-button mat-icon-button class="tb-mat-32" 39 <button *ngIf="direction === 'right'" [disabled]="disabled" mat-button mat-icon-button class="tb-mat-32"
40 type="button" 40 type="button"
41 - (click)="openEditMode($event)" 41 + (click)="openEditMode()"
42 matTooltip="{{ 'timewindow.edit' | translate }}" 42 matTooltip="{{ 'timewindow.edit' | translate }}"
43 [matTooltipPosition]="tooltipPosition"> 43 [matTooltipPosition]="tooltipPosition">
44 <mat-icon class="material-icons">query_builder</mat-icon> 44 <mat-icon class="material-icons">query_builder</mat-icon>
@@ -54,6 +54,7 @@ import { TimeService } from '@core/services/time.service'; @@ -54,6 +54,7 @@ import { TimeService } from '@core/services/time.service';
54 import { TooltipPosition } from '@angular/material/typings/tooltip'; 54 import { TooltipPosition } from '@angular/material/typings/tooltip';
55 import { deepClone } from '@core/utils'; 55 import { deepClone } from '@core/utils';
56 56
  57 +// @dynamic
57 @Component({ 58 @Component({
58 selector: 'tb-timewindow', 59 selector: 'tb-timewindow',
59 templateUrl: './timewindow.component.html', 60 templateUrl: './timewindow.component.html',
@@ -59,6 +59,7 @@ export const HelpLinks = { @@ -59,6 +59,7 @@ export const HelpLinks = {
59 securitySettings: helpBaseUrl + '/docs/user-guide/ui/security-settings', 59 securitySettings: helpBaseUrl + '/docs/user-guide/ui/security-settings',
60 ruleEngine: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/overview/', 60 ruleEngine: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/overview/',
61 ruleNodeCheckRelation: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/filter-nodes/#check-relation-filter-node', 61 ruleNodeCheckRelation: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/filter-nodes/#check-relation-filter-node',
  62 + ruleNodeCheckExistenceFields: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/filter-nodes/#check-existence-fields-node',
62 ruleNodeJsFilter: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/filter-nodes/#script-filter-node', 63 ruleNodeJsFilter: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/filter-nodes/#script-filter-node',
63 ruleNodeJsSwitch: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/filter-nodes/#switch-node', 64 ruleNodeJsSwitch: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/filter-nodes/#switch-node',
64 ruleNodeMessageTypeFilter: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/filter-nodes/#message-type-filter-node', 65 ruleNodeMessageTypeFilter: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/filter-nodes/#message-type-filter-node',
  1 +///
  2 +/// Copyright © 2016-2019 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +export * from './alarm-id';
  18 +export * from './asset-id';
  19 +export * from './audit-log-id';
  20 +export * from './customer-id';
  21 +export * from './dashboard-id';
  22 +export * from './device-credentials-id';
  23 +export * from './device-id';
  24 +export * from './entity-id';
  25 +export * from './entity-view-id';
  26 +export * from './event-id';
  27 +export * from './has-uuid';
  28 +export * from './rule-chain-id';
  29 +export * from './rule-node-id';
  30 +export * from './tenant-id';
  31 +export * from './user-id';
  32 +export * from './widget-type-id';
  33 +export * from './widgets-bundle-id';
@@ -14,7 +14,8 @@ @@ -14,7 +14,8 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import * as tinycolor from 'tinycolor2'; 17 +import * as tinycolor_ from 'tinycolor2';
  18 +const tinycolor = tinycolor_;
18 19
19 export interface MaterialColorItem { 20 export interface MaterialColorItem {
20 value: string; 21 value: string;
  1 +///
  2 +/// Copyright © 2016-2019 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +export * from './page-data';
  18 +export * from './page-link';
  19 +export * from './sort-order';
  1 +///
  2 +/// Copyright © 2016-2019 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +export * from './id/public-api';
  18 +export * from './page/public-api';
  19 +export * from './telemetry/telemetry.models';
  20 +export * from './time/time.models';
  21 +export * from './alarm.models';
  22 +export * from './alias.models';
  23 +export * from './audit-log.models';
  24 +export * from './authority.enum';
  25 +export * from './base-data';
  26 +export * from './component-descriptor.models';
  27 +export * from './constants';
  28 +export * from './contact-based.model';
  29 +export * from './customer.model';
  30 +export * from './dashboard.models';
  31 +export * from './device.models';
  32 +export * from './entity.models';
  33 +export * from './entity-type.models';
  34 +export * from './entity-view.models';
  35 +export * from './error.models';
  36 +export * from './event.models';
  37 +export * from './login.models';
  38 +export * from './material.models';
  39 +export * from './relation.models';
  40 +export * from './rule-chain.models';
  41 +export * from './rule-node.models';
  42 +export * from './settings.models';
  43 +export * from './tenant.model';
  44 +export * from './user.model';
  45 +export * from './widget.models';
  46 +export * from './widgets-bundle.model';
  47 +export * from './window-message.model';
@@ -22,6 +22,12 @@ import {RuleChainId} from '@shared/models/id/rule-chain-id'; @@ -22,6 +22,12 @@ import {RuleChainId} from '@shared/models/id/rule-chain-id';
22 import {RuleNodeId} from '@shared/models/id/rule-node-id'; 22 import {RuleNodeId} from '@shared/models/id/rule-node-id';
23 import { ComponentDescriptor, ComponentType } from '@shared/models/component-descriptor.models'; 23 import { ComponentDescriptor, ComponentType } from '@shared/models/component-descriptor.models';
24 import { EntityType, EntityTypeResource } from '@shared/models/entity-type.models'; 24 import { EntityType, EntityTypeResource } from '@shared/models/entity-type.models';
  25 +import { Observable } from 'rxjs';
  26 +import { PageComponent } from '@shared/components/page.component';
  27 +import { ComponentFactory, EventEmitter, Inject, OnDestroy, OnInit } from '@angular/core';
  28 +import { RafService } from '@core/services/raf.service';
  29 +import { Store } from '@ngrx/store';
  30 +import { AppState } from '@core/core.state';
25 31
26 export enum MsgDataType { 32 export enum MsgDataType {
27 JSON = 'JSON', 33 JSON = 'JSON',
@@ -48,23 +54,57 @@ export interface LinkLabel { @@ -48,23 +54,57 @@ export interface LinkLabel {
48 value: string; 54 value: string;
49 } 55 }
50 56
  57 +export interface RuleNodeDefinition {
  58 + description: string;
  59 + details: string;
  60 + inEnabled: boolean;
  61 + outEnabled: boolean;
  62 + relationTypes: string[];
  63 + customRelations: boolean;
  64 + defaultConfiguration: RuleNodeConfiguration;
  65 + icon?: string;
  66 + iconUrl?: string;
  67 + docUrl?: string;
  68 + uiResources?: string[];
  69 + uiResourceLoadError?: string;
  70 + configDirective?: string;
  71 +}
  72 +
51 export interface RuleNodeConfigurationDescriptor { 73 export interface RuleNodeConfigurationDescriptor {
52 - nodeDefinition: {  
53 - description: string;  
54 - details: string;  
55 - inEnabled: boolean;  
56 - outEnabled: boolean;  
57 - relationTypes: string[];  
58 - customRelations: boolean;  
59 - defaultConfiguration: any;  
60 - icon?: string;  
61 - iconUrl?: string;  
62 - docUrl?: string;  
63 - uiResources?: string[];  
64 - uiResourceLoadError?: string;  
65 - }; 74 + nodeDefinition: RuleNodeDefinition;
  75 +}
  76 +
  77 +export interface IRuleNodeConfigurationComponent {
  78 + ruleNodeId: string;
  79 + configuration: RuleNodeConfiguration;
  80 + configurationChanged: Observable<RuleNodeConfiguration>;
  81 + [key: string]: any;
66 } 82 }
67 83
  84 +export abstract class RuleNodeConfigurationComponent extends PageComponent implements
  85 + IRuleNodeConfigurationComponent, OnInit {
  86 +
  87 + ruleNodeId: string;
  88 + configuration: RuleNodeConfiguration;
  89 + configurationChangedEmiter = new EventEmitter<RuleNodeConfiguration>();
  90 + configurationChanged = this.configurationChangedEmiter.asObservable();
  91 +
  92 + protected constructor(@Inject(Store) protected store: Store<AppState>) {
  93 + super(store);
  94 + }
  95 +
  96 + ngOnInit() {
  97 + this.onConfigurationSet(this.configuration);
  98 + }
  99 +
  100 + protected abstract onConfigurationSet(configuration: RuleNodeConfiguration);
  101 +
  102 + protected notifyConfigurationUpdated(configuration: RuleNodeConfiguration) {
  103 + this.configurationChangedEmiter.emit(configuration);
  104 + }
  105 +}
  106 +
  107 +
68 export enum RuleNodeType { 108 export enum RuleNodeType {
69 FILTER = 'FILTER', 109 FILTER = 'FILTER',
70 ENRICHMENT = 'ENRICHMENT', 110 ENRICHMENT = 'ENRICHMENT',
@@ -187,6 +227,7 @@ export interface RuleNodeComponentDescriptor extends ComponentDescriptor { @@ -187,6 +227,7 @@ export interface RuleNodeComponentDescriptor extends ComponentDescriptor {
187 227
188 const ruleNodeClazzHelpLinkMap = { 228 const ruleNodeClazzHelpLinkMap = {
189 'org.thingsboard.rule.engine.filter.TbCheckRelationNode': 'ruleNodeCheckRelation', 229 'org.thingsboard.rule.engine.filter.TbCheckRelationNode': 'ruleNodeCheckRelation',
  230 + 'org.thingsboard.rule.engine.filter.TbCheckMessageNode': 'ruleNodeCheckExistenceFields',
190 'org.thingsboard.rule.engine.filter.TbJsFilterNode': 'ruleNodeJsFilter', 231 'org.thingsboard.rule.engine.filter.TbJsFilterNode': 'ruleNodeJsFilter',
191 'org.thingsboard.rule.engine.filter.TbJsSwitchNode': 'ruleNodeJsSwitch', 232 'org.thingsboard.rule.engine.filter.TbJsSwitchNode': 'ruleNodeJsSwitch',
192 'org.thingsboard.rule.engine.filter.TbMsgTypeFilterNode': 'ruleNodeMessageTypeFilter', 233 'org.thingsboard.rule.engine.filter.TbMsgTypeFilterNode': 'ruleNodeMessageTypeFilter',
@@ -31,8 +31,8 @@ export interface User extends BaseData<UserId> { @@ -31,8 +31,8 @@ export interface User extends BaseData<UserId> {
31 } 31 }
32 32
33 export enum ActivationMethod { 33 export enum ActivationMethod {
34 - DISPLAY_ACTIVATION_LINK,  
35 - SEND_ACTIVATION_MAIL 34 + DISPLAY_ACTIVATION_LINK = 'DISPLAY_ACTIVATION_LINK',
  35 + SEND_ACTIVATION_MAIL = 'SEND_ACTIVATION_MAIL'
36 } 36 }
37 37
38 export const activationMethodTranslations = new Map<ActivationMethod, string>( 38 export const activationMethodTranslations = new Map<ActivationMethod, string>(
@@ -20,7 +20,7 @@ import { Pipe, PipeTransform } from '@angular/core'; @@ -20,7 +20,7 @@ import { Pipe, PipeTransform } from '@angular/core';
20 name: 'enumToArray' 20 name: 'enumToArray'
21 }) 21 })
22 export class EnumToArrayPipe implements PipeTransform { 22 export class EnumToArrayPipe implements PipeTransform {
23 - transform(data: object) { 23 + transform(data: object): string[] {
24 const keys = Object.keys(data); 24 const keys = Object.keys(data);
25 return keys.slice(keys.length / 2); 25 return keys.slice(keys.length / 2);
26 } 26 }
@@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
17 import { Inject, Pipe, PipeTransform } from '@angular/core'; 17 import { Inject, Pipe, PipeTransform } from '@angular/core';
18 import { WINDOW } from '@core/services/window.service'; 18 import { WINDOW } from '@core/services/window.service';
19 19
  20 +// @dynamic
20 @Pipe({ 21 @Pipe({
21 name: 'keyboardShortcut' 22 name: 'keyboardShortcut'
22 }) 23 })
  1 +///
  2 +/// Copyright © 2016-2019 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +export * from './enum-to-array.pipe';
  18 +export * from './highlight.pipe';
  19 +export * from './keyboard-shortcut.pipe';
  20 +export * from './milliseconds-to-time-string.pipe';
  21 +export * from './nospace.pipe';
  22 +export * from './truncate.pipe';
  1 +///
  2 +/// Copyright © 2016-2019 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +export * from './components/public-api';
  18 +export * from './models/public-api';
  19 +export * from './pipe/public-api';
  20 +export * from './shared.module';