diff --git a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js
index 48530a9d28..e821549e09 100644
--- a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js
+++ b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js
@@ -13,4 +13,4 @@
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */var g=function(e,t){return(g=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 y(e,t){function r(){this.constructor=e}g(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)}function b(e,t,r,n){var a,o=arguments.length,i=o<3?t:null===n?n=Object.getOwnPropertyDescriptor(t,r):n;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)i=Reflect.decorate(e,t,r,n);else for(var l=e.length-1;l>=0;l--)(a=e[l])&&(i=(o<3?a(i):o>3?a(t,r,i):a(t,r))||i);return o>3&&i&&Object.defineProperty(t,r,i),i}function h(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)}function C(e){var t="function"==typeof Symbol&&e[Symbol.iterator],r=0;return t?t.call(e):{next:function(){return e&&r>=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}}}var v,F=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n}return y(r,e),r.prototype.configForm=function(){return this.emptyConfigForm},r.prototype.onConfigurationSet=function(e){this.emptyConfigForm=this.fb.group({})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-node-empty-config",template:"
"}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),T=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n.attributeScopes=Object.keys(a.AttributeScope),n.telemetryTypeTranslationsMap=a.telemetryTypeTranslations,n}return y(r,e),r.prototype.configForm=function(){return this.attributesConfigForm},r.prototype.onConfigurationSet=function(e){this.attributesConfigForm=this.fb.group({scope:[e?e.scope:null,[i.Validators.required]]})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-action-node-attributes-config",template:'\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),x=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n}return y(r,e),r.prototype.configForm=function(){return this.timeseriesConfigForm},r.prototype.onConfigurationSet=function(e){this.timeseriesConfigForm=this.fb.group({defaultTTL:[e?e.defaultTTL:null,[i.Validators.required,i.Validators.min(0)]]})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-action-node-timeseries-config",template:'\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),q=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n}return y(r,e),r.prototype.configForm=function(){return this.rpcRequestConfigForm},r.prototype.onConfigurationSet=function(e){this.rpcRequestConfigForm=this.fb.group({timeoutInSeconds:[e?e.timeoutInSeconds:null,[i.Validators.required,i.Validators.min(0)]]})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-action-node-rpc-request-config",template:'\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),S=function(e){function r(t,r,n,a){var o=e.call(this,t)||this;return o.store=t,o.fb=r,o.nodeScriptTestService=n,o.translate=a,o}return y(r,e),r.prototype.configForm=function(){return this.logConfigForm},r.prototype.onConfigurationSet=function(e){this.logConfigForm=this.fb.group({jsScript:[e?e.jsScript:null,[i.Validators.required]]})},r.prototype.testScript=function(){var e=this,t=this.logConfigForm.get("jsScript").value;this.nodeScriptTestService.testNodeScript(t,"string",this.translate.instant("tb.rulenode.to-string"),"ToString",["msg","metadata","msgType"],this.ruleNodeId).subscribe((function(t){t&&e.logConfigForm.get("jsScript").setValue(t)}))},r.prototype.onValidate=function(){this.jsFuncComponent.validateOnSubmit()},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder},{type:l.NodeScriptTestService},{type:n.TranslateService}]},b([t.ViewChild("jsFuncComponent",{static:!0}),h("design:type",a.JsFuncComponent)],r.prototype,"jsFuncComponent",void 0),r=b([t.Component({selector:"tb-action-node-log-config",template:'\n \n \n \n \n \n
\n\n'}),h("design:paramtypes",[o.Store,i.FormBuilder,l.NodeScriptTestService,n.TranslateService])],r)}(a.RuleNodeConfigurationComponent),I=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n}return y(r,e),r.prototype.configForm=function(){return this.assignCustomerConfigForm},r.prototype.onConfigurationSet=function(e){this.assignCustomerConfigForm=this.fb.group({customerNamePattern:[e?e.customerNamePattern:null,[i.Validators.required]],createCustomerIfNotExists:[!!e&&e.createCustomerIfNotExists,[]],customerCacheExpiration:[e?e.customerCacheExpiration:null,[i.Validators.required,i.Validators.min(0)]]})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-action-node-assign-to-customer-config",template:'\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),k=function(e){function r(t,r,n,a){var o=e.call(this,t)||this;return o.store=t,o.fb=r,o.nodeScriptTestService=n,o.translate=a,o}return y(r,e),r.prototype.configForm=function(){return this.clearAlarmConfigForm},r.prototype.onConfigurationSet=function(e){this.clearAlarmConfigForm=this.fb.group({alarmDetailsBuildJs:[e?e.alarmDetailsBuildJs:null,[i.Validators.required]],alarmType:[e?e.alarmType:null,[i.Validators.required]]})},r.prototype.testScript=function(){var e=this,t=this.clearAlarmConfigForm.get("alarmDetailsBuildJs").value;this.nodeScriptTestService.testNodeScript(t,"json",this.translate.instant("tb.rulenode.details"),"Details",["msg","metadata","msgType"],this.ruleNodeId).subscribe((function(t){t&&e.clearAlarmConfigForm.get("alarmDetailsBuildJs").setValue(t)}))},r.prototype.onValidate=function(){this.jsFuncComponent.validateOnSubmit()},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder},{type:l.NodeScriptTestService},{type:n.TranslateService}]},b([t.ViewChild("jsFuncComponent",{static:!0}),h("design:type",a.JsFuncComponent)],r.prototype,"jsFuncComponent",void 0),r=b([t.Component({selector:"tb-action-node-clear-alarm-config",template:'\n'}),h("design:paramtypes",[o.Store,i.FormBuilder,l.NodeScriptTestService,n.TranslateService])],r)}(a.RuleNodeConfigurationComponent),N=function(e){function r(t,r,n,o){var i=e.call(this,t)||this;return i.store=t,i.fb=r,i.nodeScriptTestService=n,i.translate=o,i.alarmSeverities=Object.keys(a.AlarmSeverity),i.alarmSeverityTranslationMap=a.alarmSeverityTranslations,i.separatorKeysCodes=[s.ENTER,s.COMMA,s.SEMICOLON],i}return y(r,e),r.prototype.configForm=function(){return this.createAlarmConfigForm},r.prototype.onConfigurationSet=function(e){this.createAlarmConfigForm=this.fb.group({alarmDetailsBuildJs:[e?e.alarmDetailsBuildJs:null,[i.Validators.required]],useMessageAlarmData:[!!e&&e.useMessageAlarmData,[]],alarmType:[e?e.alarmType:null,[]],severity:[e?e.severity:null,[]],propagate:[!!e&&e.propagate,[]],relationTypes:[e?e.relationTypes:null,[]]})},r.prototype.validatorTriggers=function(){return["useMessageAlarmData"]},r.prototype.updateValidators=function(e){this.createAlarmConfigForm.get("useMessageAlarmData").value?(this.createAlarmConfigForm.get("alarmType").setValidators([]),this.createAlarmConfigForm.get("severity").setValidators([])):(this.createAlarmConfigForm.get("alarmType").setValidators([i.Validators.required]),this.createAlarmConfigForm.get("severity").setValidators([i.Validators.required])),this.createAlarmConfigForm.get("alarmType").updateValueAndValidity({emitEvent:e}),this.createAlarmConfigForm.get("severity").updateValueAndValidity({emitEvent:e})},r.prototype.testScript=function(){var e=this,t=this.createAlarmConfigForm.get("alarmDetailsBuildJs").value;this.nodeScriptTestService.testNodeScript(t,"json",this.translate.instant("tb.rulenode.details"),"Details",["msg","metadata","msgType"],this.ruleNodeId).subscribe((function(t){t&&e.createAlarmConfigForm.get("alarmDetailsBuildJs").setValue(t)}))},r.prototype.removeKey=function(e,t){var r=this.createAlarmConfigForm.get(t).value,n=r.indexOf(e);n>=0&&(r.splice(n,1),this.createAlarmConfigForm.get(t).setValue(r,{emitEvent:!0}))},r.prototype.addKey=function(e,t){var r=e.input,n=e.value;if((n||"").trim()){n=n.trim();var a=this.createAlarmConfigForm.get(t).value;a&&-1!==a.indexOf(n)||(a||(a=[]),a.push(n),this.createAlarmConfigForm.get(t).setValue(a,{emitEvent:!0}))}r&&(r.value="")},r.prototype.onValidate=function(){this.jsFuncComponent.validateOnSubmit()},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder},{type:l.NodeScriptTestService},{type:n.TranslateService}]},b([t.ViewChild("jsFuncComponent",{static:!0}),h("design:type",a.JsFuncComponent)],r.prototype,"jsFuncComponent",void 0),r=b([t.Component({selector:"tb-action-node-create-alarm-config",template:'\n \n \n \n \n \n
\n \n {{ \'tb.rulenode.use-message-alarm-data\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.propagate\' | translate }}\n \n \n \n\n'}),h("design:paramtypes",[o.Store,i.FormBuilder,l.NodeScriptTestService,n.TranslateService])],r)}(a.RuleNodeConfigurationComponent),V=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n.directionTypes=Object.keys(a.EntitySearchDirection),n.directionTypeTranslations=a.entitySearchDirectionTranslations,n.entityType=a.EntityType,n}return y(r,e),r.prototype.configForm=function(){return this.createRelationConfigForm},r.prototype.onConfigurationSet=function(e){this.createRelationConfigForm=this.fb.group({direction:[e?e.direction:null,[i.Validators.required]],entityType:[e?e.entityType:null,[i.Validators.required]],entityNamePattern:[e?e.entityNamePattern:null,[]],entityTypePattern:[e?e.entityTypePattern:null,[]],relationType:[e?e.relationType:null,[i.Validators.required]],createEntityIfNotExists:[!!e&&e.createEntityIfNotExists,[]],removeCurrentRelations:[!!e&&e.removeCurrentRelations,[]],changeOriginatorToRelatedEntity:[!!e&&e.changeOriginatorToRelatedEntity,[]],entityCacheExpiration:[e?e.entityCacheExpiration:null,[i.Validators.required,i.Validators.min(0)]]})},r.prototype.validatorTriggers=function(){return["entityType"]},r.prototype.updateValidators=function(e){var t=this.createRelationConfigForm.get("entityType").value;t?this.createRelationConfigForm.get("entityNamePattern").setValidators([i.Validators.required]):this.createRelationConfigForm.get("entityNamePattern").setValidators([]),!t||t!==a.EntityType.DEVICE&&t!==a.EntityType.ASSET?this.createRelationConfigForm.get("entityTypePattern").setValidators([]):this.createRelationConfigForm.get("entityTypePattern").setValidators([i.Validators.required]),this.createRelationConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e}),this.createRelationConfigForm.get("entityTypePattern").updateValueAndValidity({emitEvent:e})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-action-node-create-relation-config",template:'\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),E=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n}return y(r,e),r.prototype.configForm=function(){return this.msgDelayConfigForm},r.prototype.onConfigurationSet=function(e){this.msgDelayConfigForm=this.fb.group({useMetadataPeriodInSecondsPatterns:[!!e&&e.useMetadataPeriodInSecondsPatterns,[]],periodInSeconds:[e?e.periodInSeconds:null,[]],periodInSecondsPattern:[e?e.periodInSecondsPattern:null,[]],maxPendingMsgs:[e?e.maxPendingMsgs:null,[i.Validators.required,i.Validators.min(1),i.Validators.max(1e5)]]})},r.prototype.validatorTriggers=function(){return["useMetadataPeriodInSecondsPatterns"]},r.prototype.updateValidators=function(e){this.msgDelayConfigForm.get("useMetadataPeriodInSecondsPatterns").value?(this.msgDelayConfigForm.get("periodInSecondsPattern").setValidators([i.Validators.required]),this.msgDelayConfigForm.get("periodInSeconds").setValidators([])):(this.msgDelayConfigForm.get("periodInSecondsPattern").setValidators([]),this.msgDelayConfigForm.get("periodInSeconds").setValidators([i.Validators.required,i.Validators.min(0)])),this.msgDelayConfigForm.get("periodInSecondsPattern").updateValueAndValidity({emitEvent:e}),this.msgDelayConfigForm.get("periodInSeconds").updateValueAndValidity({emitEvent:e})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-action-node-msg-delay-config",template:'\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),A=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n.directionTypes=Object.keys(a.EntitySearchDirection),n.directionTypeTranslations=a.entitySearchDirectionTranslations,n.entityType=a.EntityType,n}return y(r,e),r.prototype.configForm=function(){return this.deleteRelationConfigForm},r.prototype.onConfigurationSet=function(e){this.deleteRelationConfigForm=this.fb.group({deleteForSingleEntity:[!!e&&e.deleteForSingleEntity,[]],direction:[e?e.direction:null,[i.Validators.required]],entityType:[e?e.entityType:null,[]],entityNamePattern:[e?e.entityNamePattern:null,[]],relationType:[e?e.relationType:null,[i.Validators.required]],entityCacheExpiration:[e?e.entityCacheExpiration:null,[i.Validators.required,i.Validators.min(0)]]})},r.prototype.validatorTriggers=function(){return["deleteForSingleEntity","entityType"]},r.prototype.updateValidators=function(e){var t=this.deleteRelationConfigForm.get("deleteForSingleEntity").value,r=this.deleteRelationConfigForm.get("entityType").value;t?this.deleteRelationConfigForm.get("entityType").setValidators([i.Validators.required]):this.deleteRelationConfigForm.get("entityType").setValidators([]),t&&r?this.deleteRelationConfigForm.get("entityNamePattern").setValidators([i.Validators.required]):this.deleteRelationConfigForm.get("entityNamePattern").setValidators([]),this.deleteRelationConfigForm.get("entityType").updateValueAndValidity({emitEvent:!1}),this.deleteRelationConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-action-node-delete-relation-config",template:'\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),L=function(e){function r(t,r,n,a){var o=e.call(this,t)||this;return o.store=t,o.fb=r,o.nodeScriptTestService=n,o.translate=a,o}return y(r,e),r.prototype.configForm=function(){return this.generatorConfigForm},r.prototype.onConfigurationSet=function(e){this.generatorConfigForm=this.fb.group({msgCount:[e?e.msgCount:null,[i.Validators.required,i.Validators.min(0)]],periodInSeconds:[e?e.periodInSeconds:null,[i.Validators.required,i.Validators.min(1)]],originator:[e?e.originator:null,[]],jsScript:[e?e.jsScript:null,[i.Validators.required]]})},r.prototype.prepareInputConfig=function(e){return e&&(e.originatorId&&e.originatorType?e.originator={id:e.originatorId,entityType:e.originatorType}:e.originator=null,delete e.originatorId,delete e.originatorType),e},r.prototype.prepareOutputConfig=function(e){return e.originator?(e.originatorId=e.originator.id,e.originatorType=e.originator.entityType):(e.originatorId=null,e.originatorType=null),delete e.originator,e},r.prototype.testScript=function(){var e=this,t=this.generatorConfigForm.get("jsScript").value;this.nodeScriptTestService.testNodeScript(t,"generate",this.translate.instant("tb.rulenode.generator"),"Generate",["prevMsg","prevMetadata","prevMsgType"],this.ruleNodeId).subscribe((function(t){t&&e.generatorConfigForm.get("jsScript").setValue(t)}))},r.prototype.onValidate=function(){this.jsFuncComponent.validateOnSubmit()},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder},{type:l.NodeScriptTestService},{type:n.TranslateService}]},b([t.ViewChild("jsFuncComponent",{static:!0}),h("design:type",a.JsFuncComponent)],r.prototype,"jsFuncComponent",void 0),r=b([t.Component({selector:"tb-action-node-generator-config",template:'\n'}),h("design:paramtypes",[o.Store,i.FormBuilder,l.NodeScriptTestService,n.TranslateService])],r)}(a.RuleNodeConfigurationComponent);!function(e){e.CUSTOMER="CUSTOMER",e.TENANT="TENANT",e.RELATED="RELATED",e.ALARM_ORIGINATOR="ALARM_ORIGINATOR"}(v||(v={}));var M,P=new Map([[v.CUSTOMER,"tb.rulenode.originator-customer"],[v.TENANT,"tb.rulenode.originator-tenant"],[v.RELATED,"tb.rulenode.originator-related"],[v.ALARM_ORIGINATOR,"tb.rulenode.originator-alarm-originator"]]);!function(e){e.CIRCLE="CIRCLE",e.POLYGON="POLYGON"}(M||(M={}));var R,w=new Map([[M.CIRCLE,"tb.rulenode.perimeter-circle"],[M.POLYGON,"tb.rulenode.perimeter-polygon"]]);!function(e){e.MILLISECONDS="MILLISECONDS",e.SECONDS="SECONDS",e.MINUTES="MINUTES",e.HOURS="HOURS",e.DAYS="DAYS"}(R||(R={}));var D,O=new Map([[R.MILLISECONDS,"tb.rulenode.time-unit-milliseconds"],[R.SECONDS,"tb.rulenode.time-unit-seconds"],[R.MINUTES,"tb.rulenode.time-unit-minutes"],[R.HOURS,"tb.rulenode.time-unit-hours"],[R.DAYS,"tb.rulenode.time-unit-days"]]);!function(e){e.METER="METER",e.KILOMETER="KILOMETER",e.FOOT="FOOT",e.MILE="MILE",e.NAUTICAL_MILE="NAUTICAL_MILE"}(D||(D={}));var K,B=new Map([[D.METER,"tb.rulenode.range-unit-meter"],[D.KILOMETER,"tb.rulenode.range-unit-kilometer"],[D.FOOT,"tb.rulenode.range-unit-foot"],[D.MILE,"tb.rulenode.range-unit-mile"],[D.NAUTICAL_MILE,"tb.rulenode.range-unit-nautical-mile"]]);!function(e){e.TITLE="TITLE",e.COUNTRY="COUNTRY",e.STATE="STATE",e.ZIP="ZIP",e.ADDRESS="ADDRESS",e.ADDRESS2="ADDRESS2",e.PHONE="PHONE",e.EMAIL="EMAIL",e.ADDITIONAL_INFO="ADDITIONAL_INFO"}(K||(K={}));var j,U,G,H=new Map([[K.TITLE,"tb.rulenode.entity-details-title"],[K.COUNTRY,"tb.rulenode.entity-details-country"],[K.STATE,"tb.rulenode.entity-details-state"],[K.ZIP,"tb.rulenode.entity-details-zip"],[K.ADDRESS,"tb.rulenode.entity-details-address"],[K.ADDRESS2,"tb.rulenode.entity-details-address2"],[K.PHONE,"tb.rulenode.entity-details-phone"],[K.EMAIL,"tb.rulenode.entity-details-email"],[K.ADDITIONAL_INFO,"tb.rulenode.entity-details-additional_info"]]);!function(e){e.FIRST="FIRST",e.LAST="LAST",e.ALL="ALL"}(j||(j={})),function(e){e.ASC="ASC",e.DESC="DESC"}(U||(U={})),function(e){e.STANDARD="STANDARD",e.FIFO="FIFO"}(G||(G={}));var Q,z=new Map([[G.STANDARD,"tb.rulenode.sqs-queue-standard"],[G.FIFO,"tb.rulenode.sqs-queue-fifo"]]),$=["anonymous","basic","cert.PEM"],_=new Map([["anonymous","tb.rulenode.credentials-anonymous"],["basic","tb.rulenode.credentials-basic"],["cert.PEM","tb.rulenode.credentials-pem"]]);!function(e){e.GET="GET",e.POST="POST",e.PUT="PUT",e.DELETE="DELETE"}(Q||(Q={}));var W=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n.perimeterType=M,n.perimeterTypes=Object.keys(M),n.perimeterTypeTranslationMap=w,n.rangeUnits=Object.keys(D),n.rangeUnitTranslationMap=B,n.timeUnits=Object.keys(R),n.timeUnitsTranslationMap=O,n}return y(r,e),r.prototype.configForm=function(){return this.geoActionConfigForm},r.prototype.onConfigurationSet=function(e){this.geoActionConfigForm=this.fb.group({latitudeKeyName:[e?e.latitudeKeyName:null,[i.Validators.required]],longitudeKeyName:[e?e.longitudeKeyName:null,[i.Validators.required]],fetchPerimeterInfoFromMessageMetadata:[!!e&&e.fetchPerimeterInfoFromMessageMetadata,[]],perimeterType:[e?e.perimeterType:null,[]],centerLatitude:[e?e.centerLatitude:null,[]],centerLongitude:[e?e.centerLatitude:null,[]],range:[e?e.range:null,[]],rangeUnit:[e?e.rangeUnit:null,[]],polygonsDefinition:[e?e.polygonsDefinition:null,[]],minInsideDuration:[e?e.minInsideDuration:null,[i.Validators.required,i.Validators.min(1),i.Validators.max(2147483647)]],minInsideDurationTimeUnit:[e?e.minInsideDurationTimeUnit:null,[i.Validators.required]],minOutsideDuration:[e?e.minOutsideDuration:null,[i.Validators.required,i.Validators.min(1),i.Validators.max(2147483647)]],minOutsideDurationTimeUnit:[e?e.minOutsideDurationTimeUnit:null,[i.Validators.required]]})},r.prototype.validatorTriggers=function(){return["fetchPerimeterInfoFromMessageMetadata","perimeterType"]},r.prototype.updateValidators=function(e){var t=this.geoActionConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value,r=this.geoActionConfigForm.get("perimeterType").value;t?this.geoActionConfigForm.get("perimeterType").setValidators([]):this.geoActionConfigForm.get("perimeterType").setValidators([i.Validators.required]),t||r!==M.CIRCLE?(this.geoActionConfigForm.get("centerLatitude").setValidators([]),this.geoActionConfigForm.get("centerLongitude").setValidators([]),this.geoActionConfigForm.get("range").setValidators([]),this.geoActionConfigForm.get("rangeUnit").setValidators([])):(this.geoActionConfigForm.get("centerLatitude").setValidators([i.Validators.required,i.Validators.min(-90),i.Validators.max(90)]),this.geoActionConfigForm.get("centerLongitude").setValidators([i.Validators.required,i.Validators.min(-180),i.Validators.max(180)]),this.geoActionConfigForm.get("range").setValidators([i.Validators.required,i.Validators.min(0)]),this.geoActionConfigForm.get("rangeUnit").setValidators([i.Validators.required])),t||r!==M.POLYGON?this.geoActionConfigForm.get("polygonsDefinition").setValidators([]):this.geoActionConfigForm.get("polygonsDefinition").setValidators([i.Validators.required]),this.geoActionConfigForm.get("perimeterType").updateValueAndValidity({emitEvent:!1}),this.geoActionConfigForm.get("centerLatitude").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("centerLongitude").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("range").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("rangeUnit").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("polygonsDefinition").updateValueAndValidity({emitEvent:e})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-action-node-gps-geofencing-config",template:'\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),J=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n}return y(r,e),r.prototype.configForm=function(){return this.msgCountConfigForm},r.prototype.onConfigurationSet=function(e){this.msgCountConfigForm=this.fb.group({interval:[e?e.interval:null,[i.Validators.required,i.Validators.min(1)]],telemetryPrefix:[e?e.telemetryPrefix:null,[i.Validators.required]]})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-action-node-msg-count-config",template:'\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),Y=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n}return y(r,e),r.prototype.configForm=function(){return this.rpcReplyConfigForm},r.prototype.onConfigurationSet=function(e){this.rpcReplyConfigForm=this.fb.group({requestIdMetaDataAttribute:[e?e.requestIdMetaDataAttribute:null,[]]})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-action-node-rpc-reply-config",template:'\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),Z=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n}return y(r,e),r.prototype.configForm=function(){return this.saveToCustomTableConfigForm},r.prototype.onConfigurationSet=function(e){this.saveToCustomTableConfigForm=this.fb.group({tableName:[e?e.tableName:null,[i.Validators.required]],fieldsMapping:[e?e.fieldsMapping:null,[i.Validators.required]]})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-action-node-custom-table-config",template:'\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),X=function(e){function r(t,r,n,a){var o=e.call(this,t)||this;return o.store=t,o.translate=r,o.injector=n,o.fb=a,o.propagateChange=null,o.valueChangeSubscription=null,o}var a;return y(r,e),a=r,Object.defineProperty(r.prototype,"required",{get:function(){return this.requiredValue},set:function(e){this.requiredValue=u.coerceBooleanProperty(e)},enumerable:!0,configurable:!0}),r.prototype.ngOnInit=function(){this.ngControl=this.injector.get(i.NgControl),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.kvListFormGroup=this.fb.group({}),this.kvListFormGroup.addControl("keyVals",this.fb.array([]))},r.prototype.keyValsFormArray=function(){return this.kvListFormGroup.get("keyVals")},r.prototype.registerOnChange=function(e){this.propagateChange=e},r.prototype.registerOnTouched=function(e){},r.prototype.setDisabledState=function(e){this.disabled=e,this.disabled?this.kvListFormGroup.disable({emitEvent:!1}):this.kvListFormGroup.enable({emitEvent:!1})},r.prototype.writeValue=function(e){var t,r,n=this;this.valueChangeSubscription&&this.valueChangeSubscription.unsubscribe();var a=[];if(e)try{for(var o=C(Object.keys(e)),l=o.next();!l.done;l=o.next()){var s=l.value;Object.prototype.hasOwnProperty.call(e,s)&&a.push(this.fb.group({key:[s,[i.Validators.required]],value:[e[s],[i.Validators.required]]}))}}catch(e){t={error:e}}finally{try{l&&!l.done&&(r=o.return)&&r.call(o)}finally{if(t)throw t.error}}this.kvListFormGroup.setControl("keyVals",this.fb.array(a)),this.valueChangeSubscription=this.kvListFormGroup.valueChanges.subscribe((function(){n.updateModel()}))},r.prototype.removeKeyVal=function(e){this.kvListFormGroup.get("keyVals").removeAt(e)},r.prototype.addKeyVal=function(){this.kvListFormGroup.get("keyVals").push(this.fb.group({key:["",[i.Validators.required]],value:["",[i.Validators.required]]}))},r.prototype.validate=function(e){return!this.kvListFormGroup.get("keyVals").value.length&&this.required?{kvMapRequired:!0}:this.kvListFormGroup.valid?null:{kvFieldsRequired:!0}},r.prototype.updateModel=function(){var e=this.kvListFormGroup.get("keyVals").value;if(this.required&&!e.length||!this.kvListFormGroup.valid)this.propagateChange(null);else{var t={};e.forEach((function(e){t[e.key]=e.value})),this.propagateChange(t)}},r.ctorParameters=function(){return[{type:o.Store},{type:n.TranslateService},{type:t.Injector},{type:i.FormBuilder}]},b([t.Input(),h("design:type",Boolean)],r.prototype,"disabled",void 0),b([t.Input(),h("design:type",String)],r.prototype,"requiredText",void 0),b([t.Input(),h("design:type",String)],r.prototype,"keyText",void 0),b([t.Input(),h("design:type",String)],r.prototype,"keyRequiredText",void 0),b([t.Input(),h("design:type",String)],r.prototype,"valText",void 0),b([t.Input(),h("design:type",String)],r.prototype,"valRequiredText",void 0),b([t.Input(),h("design:type",Boolean),h("design:paramtypes",[Boolean])],r.prototype,"required",null),r=a=b([t.Component({selector:"tb-kv-map-config",template:'\n \n \n \n \n \n
\n\n',providers:[{provide:i.NG_VALUE_ACCESSOR,useExisting:t.forwardRef((function(){return a})),multi:!0},{provide:i.NG_VALIDATORS,useExisting:t.forwardRef((function(){return a})),multi:!0}],styles:[":host .tb-kv-map-config{margin-bottom:16px}:host .tb-kv-map-config .header{padding-left:5px;padding-right:5px;padding-bottom:5px}:host .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}:host .tb-kv-map-config .body{padding-left:5px;padding-right:5px;padding-bottom:20px;max-height:300px;overflow:auto}:host .tb-kv-map-config .body .row{padding-top:5px;max-height:40px}:host .tb-kv-map-config .body .cell{padding-left:5px;padding-right:5px}:host ::ng-deep .tb-kv-map-config .body mat-form-field.cell{margin:0;max-height:40px}:host ::ng-deep .tb-kv-map-config .body mat-form-field.cell .mat-form-field-infix{border-top:0}:host ::ng-deep .tb-kv-map-config .body button.mat-button{margin:0}"]}),h("design:paramtypes",[o.Store,n.TranslateService,t.Injector,i.FormBuilder])],r)}(a.PageComponent),ee=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n.directionTypes=Object.keys(a.EntitySearchDirection),n.directionTypeTranslations=a.entitySearchDirectionTranslations,n.entityType=a.EntityType,n.propagateChange=null,n}var n;return y(r,e),n=r,Object.defineProperty(r.prototype,"required",{get:function(){return this.requiredValue},set:function(e){this.requiredValue=u.coerceBooleanProperty(e)},enumerable:!0,configurable:!0}),r.prototype.ngOnInit=function(){var e=this;this.deviceRelationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[i.Validators.required]],maxLevel:[null,[]],relationType:[null],deviceTypes:[null,[i.Validators.required]]}),this.deviceRelationsQueryFormGroup.valueChanges.subscribe((function(t){e.deviceRelationsQueryFormGroup.valid?e.propagateChange(t):e.propagateChange(null)}))},r.prototype.registerOnChange=function(e){this.propagateChange=e},r.prototype.registerOnTouched=function(e){},r.prototype.setDisabledState=function(e){this.disabled=e,this.disabled?this.deviceRelationsQueryFormGroup.disable({emitEvent:!1}):this.deviceRelationsQueryFormGroup.enable({emitEvent:!1})},r.prototype.writeValue=function(e){this.deviceRelationsQueryFormGroup.reset(e,{emitEvent:!1})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},b([t.Input(),h("design:type",Boolean)],r.prototype,"disabled",void 0),b([t.Input(),h("design:type",Boolean),h("design:paramtypes",[Boolean])],r.prototype,"required",null),r=n=b([t.Component({selector:"tb-device-relations-query-config",template:'\n \n {{ \'relation.last-level-relation\' | translate }}\n \n \n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n
\n relation.relation-type
\n \n \n device.device-types
\n \n \n\n',providers:[{provide:i.NG_VALUE_ACCESSOR,useExisting:t.forwardRef((function(){return n})),multi:!0}]}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.PageComponent),te=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n.directionTypes=Object.keys(a.EntitySearchDirection),n.directionTypeTranslations=a.entitySearchDirectionTranslations,n.propagateChange=null,n}var n;return y(r,e),n=r,Object.defineProperty(r.prototype,"required",{get:function(){return this.requiredValue},set:function(e){this.requiredValue=u.coerceBooleanProperty(e)},enumerable:!0,configurable:!0}),r.prototype.ngOnInit=function(){var e=this;this.relationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[i.Validators.required]],maxLevel:[null,[]],filters:[null]}),this.relationsQueryFormGroup.valueChanges.subscribe((function(t){e.relationsQueryFormGroup.valid?e.propagateChange(t):e.propagateChange(null)}))},r.prototype.registerOnChange=function(e){this.propagateChange=e},r.prototype.registerOnTouched=function(e){},r.prototype.setDisabledState=function(e){this.disabled=e,this.disabled?this.relationsQueryFormGroup.disable({emitEvent:!1}):this.relationsQueryFormGroup.enable({emitEvent:!1})},r.prototype.writeValue=function(e){this.relationsQueryFormGroup.reset(e,{emitEvent:!1})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},b([t.Input(),h("design:type",Boolean)],r.prototype,"disabled",void 0),b([t.Input(),h("design:type",Boolean),h("design:paramtypes",[Boolean])],r.prototype,"required",null),r=n=b([t.Component({selector:"tb-relations-query-config",template:'\n',providers:[{provide:i.NG_VALUE_ACCESSOR,useExisting:t.forwardRef((function(){return n})),multi:!0}]}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.PageComponent),re=function(e){function r(t,r,n,o){var i,l,m=e.call(this,t)||this;m.store=t,m.translate=r,m.truncate=n,m.fb=o,m.placeholder="tb.rulenode.message-type",m.separatorKeysCodes=[s.ENTER,s.COMMA,s.SEMICOLON],m.messageTypes=[],m.messageTypesList=[],m.searchText="",m.propagateChange=function(e){},m.messageTypeConfigForm=m.fb.group({messageType:[null]});try{for(var u=C(Object.keys(a.MessageType)),d=u.next();!d.done;d=u.next()){var p=d.value;m.messageTypesList.push({name:a.messageTypeNames.get(a.MessageType[p]),value:p})}}catch(e){i={error:e}}finally{try{d&&!d.done&&(l=u.return)&&l.call(u)}finally{if(i)throw i.error}}return m}var l;return y(r,e),l=r,Object.defineProperty(r.prototype,"required",{get:function(){return this.requiredValue},set:function(e){this.requiredValue=u.coerceBooleanProperty(e)},enumerable:!0,configurable:!0}),r.prototype.registerOnChange=function(e){this.propagateChange=e},r.prototype.registerOnTouched=function(e){},r.prototype.ngOnInit=function(){var e=this;this.filteredMessageTypes=this.messageTypeConfigForm.get("messageType").valueChanges.pipe(f.startWith(""),f.map((function(e){return e||""})),f.mergeMap((function(t){return e.fetchMessageTypes(t)})),f.share())},r.prototype.ngAfterViewInit=function(){},r.prototype.setDisabledState=function(e){this.disabled=e,this.disabled?this.messageTypeConfigForm.disable({emitEvent:!1}):this.messageTypeConfigForm.enable({emitEvent:!1})},r.prototype.writeValue=function(e){var t=this;this.searchText="",this.messageTypes.length=0,e&&e.forEach((function(e){var r=t.messageTypesList.find((function(t){return t.value===e}));r?t.messageTypes.push({name:r.name,value:r.value}):t.messageTypes.push({name:e,value:e})}))},r.prototype.displayMessageTypeFn=function(e){return e?e.name:void 0},r.prototype.textIsNotEmpty=function(e){return!!(e&&null!=e&&e.length>0)},r.prototype.createMessageType=function(e,t){e.preventDefault(),this.transformMessageType(t)},r.prototype.add=function(e){this.transformMessageType(e.value)},r.prototype.fetchMessageTypes=function(e){if(this.searchText=e,this.searchText&&this.searchText.length){var t=this.searchText.toUpperCase();return c.of(this.messageTypesList.filter((function(e){return e.name.toUpperCase().includes(t)})))}return c.of(this.messageTypesList)},r.prototype.transformMessageType=function(e){if((e||"").trim()){var t=null,r=e.trim(),n=this.messageTypesList.find((function(e){return e.name===r}));(t=n?{name:n.name,value:n.value}:{name:r,value:r})&&this.addMessageType(t)}this.clear("")},r.prototype.remove=function(e){var t=this.messageTypes.indexOf(e);t>=0&&(this.messageTypes.splice(t,1),this.updateModel())},r.prototype.selected=function(e){this.addMessageType(e.option.value),this.clear("")},r.prototype.addMessageType=function(e){-1===this.messageTypes.findIndex((function(t){return t.value===e.value}))&&(this.messageTypes.push(e),this.updateModel())},r.prototype.onFocus=function(){this.messageTypeConfigForm.get("messageType").updateValueAndValidity({onlySelf:!0,emitEvent:!0})},r.prototype.clear=function(e){var t=this;void 0===e&&(e=""),this.messageTypeInput.nativeElement.value=e,this.messageTypeConfigForm.get("messageType").patchValue(null,{emitEvent:!0}),setTimeout((function(){t.messageTypeInput.nativeElement.blur(),t.messageTypeInput.nativeElement.focus()}),0)},r.prototype.updateModel=function(){var e=this.messageTypes.map((function(e){return e.value}));this.required?(this.chipList.errorState=!e.length,this.propagateChange(e.length>0?e:null)):(this.chipList.errorState=!1,this.propagateChange(e))},r.ctorParameters=function(){return[{type:o.Store},{type:n.TranslateService},{type:a.TruncatePipe},{type:i.FormBuilder}]},b([t.Input(),h("design:type",Boolean),h("design:paramtypes",[Boolean])],r.prototype,"required",null),b([t.Input(),h("design:type",String)],r.prototype,"label",void 0),b([t.Input(),h("design:type",Object)],r.prototype,"placeholder",void 0),b([t.Input(),h("design:type",Boolean)],r.prototype,"disabled",void 0),b([t.ViewChild("chipList",{static:!1}),h("design:type",d.MatChipList)],r.prototype,"chipList",void 0),b([t.ViewChild("messageTypeAutocomplete",{static:!1}),h("design:type",p.MatAutocomplete)],r.prototype,"matAutocomplete",void 0),b([t.ViewChild("messageTypeInput",{static:!1}),h("design:type",t.ElementRef)],r.prototype,"messageTypeInput",void 0),r=l=b([t.Component({selector:"tb-message-types-config",template:'\n {{ label }}\n \n \n {{messageType.name}}\n close\n \n \n \n \n \n \n \n \n \n
\n tb.rulenode.no-message-types-found\n
\n
\n \n {{ translate.get(\'tb.rulenode.no-message-type-matching\',\n {messageType: truncate.transform(searchText, true, 6, '...')}) | async }}\n \n \n
\n tb.rulenode.create-new-message-type\n \n
\n \n \n \n {{ \'tb.rulenode.message-types-required\' | translate }}\n \n\n',providers:[{provide:i.NG_VALUE_ACCESSOR,useExisting:t.forwardRef((function(){return l})),multi:!0}]}),h("design:paramtypes",[o.Store,n.TranslateService,a.TruncatePipe,i.FormBuilder])],r)}(a.PageComponent),ne=function(){function e(){}return e=b([t.NgModule({declarations:[X,ee,te,re],imports:[r.CommonModule,a.SharedModule,m.HomeComponentsModule],exports:[X,ee,te,re]})],e)}(),ae=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n}return y(r,e),r.prototype.configForm=function(){return this.unassignCustomerConfigForm},r.prototype.onConfigurationSet=function(e){this.unassignCustomerConfigForm=this.fb.group({customerNamePattern:[e?e.customerNamePattern:null,[i.Validators.required]],customerCacheExpiration:[e?e.customerCacheExpiration:null,[i.Validators.required,i.Validators.min(0)]]})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-action-node-un-assign-to-customer-config",template:'\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),oe=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n}return y(r,e),r.prototype.configForm=function(){return this.snsConfigForm},r.prototype.onConfigurationSet=function(e){this.snsConfigForm=this.fb.group({topicArnPattern:[e?e.topicArnPattern:null,[i.Validators.required]],accessKeyId:[e?e.accessKeyId:null,[i.Validators.required]],secretAccessKey:[e?e.secretAccessKey:null,[i.Validators.required]],region:[e?e.region:null,[i.Validators.required]]})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-action-node-sns-config",template:'\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),ie=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n.sqsQueueType=G,n.sqsQueueTypes=Object.keys(G),n.sqsQueueTypeTranslationsMap=z,n}return y(r,e),r.prototype.configForm=function(){return this.sqsConfigForm},r.prototype.onConfigurationSet=function(e){this.sqsConfigForm=this.fb.group({queueType:[e?e.queueType:null,[i.Validators.required]],queueUrlPattern:[e?e.queueUrlPattern:null,[i.Validators.required]],delaySeconds:[e?e.delaySeconds:null,[i.Validators.min(0),i.Validators.max(900)]],messageAttributes:[e?e.messageAttributes:null,[]],accessKeyId:[e?e.accessKeyId:null,[i.Validators.required]],secretAccessKey:[e?e.secretAccessKey:null,[i.Validators.required]],region:[e?e.region:null,[i.Validators.required]]})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-action-node-sqs-config",template:'\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),le=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n}return y(r,e),r.prototype.configForm=function(){return this.pubSubConfigForm},r.prototype.onConfigurationSet=function(e){this.pubSubConfigForm=this.fb.group({projectId:[e?e.projectId:null,[i.Validators.required]],topicName:[e?e.topicName:null,[i.Validators.required]],serviceAccountKey:[e?e.serviceAccountKey:null,[i.Validators.required]],serviceAccountKeyFileName:[e?e.serviceAccountKeyFileName:null,[i.Validators.required]],messageAttributes:[e?e.messageAttributes:null,[]]})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-action-node-pub-sub-config",template:'\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),se=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n.ackValues=["all","-1","0","1"],n}return y(r,e),r.prototype.configForm=function(){return this.kafkaConfigForm},r.prototype.onConfigurationSet=function(e){this.kafkaConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[i.Validators.required]],bootstrapServers:[e?e.bootstrapServers:null,[i.Validators.required]],retries:[e?e.retries:null,[i.Validators.min(0)]],batchSize:[e?e.batchSize:null,[i.Validators.min(0)]],linger:[e?e.linger:null,[i.Validators.min(0)]],bufferMemory:[e?e.bufferMemory:null,[i.Validators.min(0)]],acks:[e?e.acks:null,[i.Validators.required]],keySerializer:[e?e.keySerializer:null,[i.Validators.required]],valueSerializer:[e?e.valueSerializer:null,[i.Validators.required]],otherProperties:[e?e.otherProperties:null,[]]})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-action-node-kafka-config",template:'\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),me=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n.allMqttCredentialsTypes=$,n.mqttCredentialsTypeTranslationsMap=_,n}return y(r,e),r.prototype.configForm=function(){return this.mqttConfigForm},r.prototype.onConfigurationSet=function(e){this.mqttConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[i.Validators.required]],host:[e?e.host:null,[i.Validators.required]],port:[e?e.port:null,[i.Validators.required,i.Validators.min(1),i.Validators.max(65535)]],connectTimeoutSec:[e?e.connectTimeoutSec:null,[i.Validators.required,i.Validators.min(1),i.Validators.max(200)]],clientId:[e?e.clientId:null,[]],cleanSession:[!!e&&e.cleanSession,[]],ssl:[!!e&&e.ssl,[]],credentials:this.fb.group({type:[e&&e.credentials?e.credentials.type:null,[i.Validators.required]],username:[e&&e.credentials?e.credentials.username:null,[]],password:[e&&e.credentials?e.credentials.password:null,[]],caCert:[e&&e.credentials?e.credentials.caCert:null,[]],caCertFileName:[e&&e.credentials?e.credentials.caCertFileName:null,[]],privateKey:[e&&e.credentials?e.credentials.privateKey:null,[]],privateKeyFileName:[e&&e.credentials?e.credentials.privateKeyFileName:null,[]],cert:[e&&e.credentials?e.credentials.cert:null,[]],certFileName:[e&&e.credentials?e.credentials.certFileName:null,[]]})})},r.prototype.prepareOutputConfig=function(e){var t=e.credentials.type;switch(t){case"anonymous":e.credentials={type:t};break;case"basic":e.credentials={type:t,username:e.credentials.username,password:e.credentials.password};break;case"cert.PEM":delete e.credentials.username}return e},r.prototype.validatorTriggers=function(){return["credentials.type"]},r.prototype.updateValidators=function(e){var t=this.mqttConfigForm.get("credentials"),r=t.get("type").value;switch(e&&t.reset({type:r},{emitEvent:!1}),t.get("username").setValidators([]),t.get("password").setValidators([]),t.get("caCert").setValidators([]),t.get("caCertFileName").setValidators([]),t.get("privateKey").setValidators([]),t.get("privateKeyFileName").setValidators([]),t.get("cert").setValidators([]),t.get("certFileName").setValidators([]),r){case"anonymous":break;case"basic":t.get("username").setValidators([i.Validators.required]),t.get("password").setValidators([i.Validators.required]);break;case"cert.PEM":t.get("caCert").setValidators([i.Validators.required]),t.get("caCertFileName").setValidators([i.Validators.required]),t.get("privateKey").setValidators([i.Validators.required]),t.get("privateKeyFileName").setValidators([i.Validators.required]),t.get("cert").setValidators([i.Validators.required]),t.get("certFileName").setValidators([i.Validators.required])}t.get("username").updateValueAndValidity({emitEvent:e}),t.get("password").updateValueAndValidity({emitEvent:e}),t.get("caCert").updateValueAndValidity({emitEvent:e}),t.get("caCertFileName").updateValueAndValidity({emitEvent:e}),t.get("privateKey").updateValueAndValidity({emitEvent:e}),t.get("privateKeyFileName").updateValueAndValidity({emitEvent:e}),t.get("cert").updateValueAndValidity({emitEvent:e}),t.get("certFileName").updateValueAndValidity({emitEvent:e})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-action-node-mqtt-config",template:'\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}"]}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),ue=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"],n}return y(r,e),r.prototype.configForm=function(){return this.rabbitMqConfigForm},r.prototype.onConfigurationSet=function(e){this.rabbitMqConfigForm=this.fb.group({exchangeNamePattern:[e?e.exchangeNamePattern:null,[]],routingKeyPattern:[e?e.routingKeyPattern:null,[]],messageProperties:[e?e.messageProperties:null,[]],host:[e?e.host:null,[i.Validators.required]],port:[e?e.port:null,[i.Validators.required,i.Validators.min(1),i.Validators.max(65535)]],virtualHost:[e?e.virtualHost:null,[]],username:[e?e.username:null,[]],password:[e?e.password:null,[]],automaticRecoveryEnabled:[!!e&&e.automaticRecoveryEnabled,[]],connectionTimeout:[e?e.connectionTimeout:null,[i.Validators.min(0)]],handshakeTimeout:[e?e.handshakeTimeout:null,[i.Validators.min(0)]],clientProperties:[e?e.clientProperties:null,[]]})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-action-node-rabbit-mq-config",template:'\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),de=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n.httpRequestTypes=Object.keys(Q),n}return y(r,e),r.prototype.configForm=function(){return this.restApiCallConfigForm},r.prototype.onConfigurationSet=function(e){this.restApiCallConfigForm=this.fb.group({restEndpointUrlPattern:[e?e.restEndpointUrlPattern:null,[i.Validators.required]],requestMethod:[e?e.requestMethod:null,[i.Validators.required]],useSimpleClientHttpFactory:[!!e&&e.useSimpleClientHttpFactory,[]],readTimeoutMs:[e?e.readTimeoutMs:null,[]],maxParallelRequestsCount:[e?e.maxParallelRequestsCount:null,[i.Validators.min(0)]],headers:[e?e.headers:null,[]],useRedisQueueForMsgPersistence:[!!e&&e.useRedisQueueForMsgPersistence,[]],trimQueue:[!!e&&e.trimQueue,[]],maxQueueSize:[e?e.maxQueueSize:null,[]]})},r.prototype.validatorTriggers=function(){return["useSimpleClientHttpFactory","useRedisQueueForMsgPersistence"]},r.prototype.updateValidators=function(e){var t=this.restApiCallConfigForm.get("useSimpleClientHttpFactory").value,r=this.restApiCallConfigForm.get("useRedisQueueForMsgPersistence").value;t?this.restApiCallConfigForm.get("readTimeoutMs").setValidators([]):this.restApiCallConfigForm.get("readTimeoutMs").setValidators([i.Validators.min(0)]),r?this.restApiCallConfigForm.get("maxQueueSize").setValidators([i.Validators.min(0)]):this.restApiCallConfigForm.get("maxQueueSize").setValidators([]),this.restApiCallConfigForm.get("readTimeoutMs").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("maxQueueSize").updateValueAndValidity({emitEvent:e})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-action-node-rest-api-call-config",template:'\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),pe=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n.smtpProtocols=["smtp","smtps"],n.tlsVersions=["TLSv1","TLSv1.1","TLSv1.2","TLSv1.3"],n}return y(r,e),r.prototype.configForm=function(){return this.sendEmailConfigForm},r.prototype.onConfigurationSet=function(e){this.sendEmailConfigForm=this.fb.group({useSystemSmtpSettings:[!!e&&e.useSystemSmtpSettings,[]],smtpProtocol:[e?e.smtpProtocol:null,[]],smtpHost:[e?e.smtpHost:null,[]],smtpPort:[e?e.smtpPort:null,[]],timeout:[e?e.timeout:null,[]],enableTls:[!!e&&e.enableTls,[]],tlsVersion:[e?e.tlsVersion:null,[]],username:[e?e.username:null,[]],password:[e?e.password:null,[]]})},r.prototype.validatorTriggers=function(){return["useSystemSmtpSettings"]},r.prototype.updateValidators=function(e){this.sendEmailConfigForm.get("useSystemSmtpSettings").value?(this.sendEmailConfigForm.get("smtpProtocol").setValidators([]),this.sendEmailConfigForm.get("smtpHost").setValidators([]),this.sendEmailConfigForm.get("smtpPort").setValidators([]),this.sendEmailConfigForm.get("timeout").setValidators([])):(this.sendEmailConfigForm.get("smtpProtocol").setValidators([i.Validators.required]),this.sendEmailConfigForm.get("smtpHost").setValidators([i.Validators.required]),this.sendEmailConfigForm.get("smtpPort").setValidators([i.Validators.required,i.Validators.min(1),i.Validators.max(65535)]),this.sendEmailConfigForm.get("timeout").setValidators([i.Validators.required,i.Validators.min(0)])),this.sendEmailConfigForm.get("smtpProtocol").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("smtpHost").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("smtpPort").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("timeout").updateValueAndValidity({emitEvent:e})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-action-node-send-email-config",template:'\n \n {{ \'tb.rulenode.use-system-smtp-settings\' | translate }}\n \n \n\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),ce=function(){function e(){}return e=b([t.NgModule({declarations:[T,x,q,S,I,k,N,V,E,A,L,W,J,Y,Z,ae,oe,ie,le,se,me,ue,de,pe],imports:[r.CommonModule,a.SharedModule,ne],exports:[T,x,q,S,I,k,N,V,E,A,L,W,J,Y,Z,ae,oe,ie,le,se,me,ue,de,pe]})],e)}(),fe=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n.separatorKeysCodes=[s.ENTER,s.COMMA,s.SEMICOLON],n}return y(r,e),r.prototype.configForm=function(){return this.checkMessageConfigForm},r.prototype.onConfigurationSet=function(e){this.checkMessageConfigForm=this.fb.group({messageNames:[e?e.messageNames:null,[]],metadataNames:[e?e.metadataNames:null,[]],checkAllKeys:[!!e&&e.checkAllKeys,[]]})},r.prototype.validateConfig=function(){var e=this.checkMessageConfigForm.get("messageNames").value,t=this.checkMessageConfigForm.get("metadataNames").value;return e.length>0||t.length>0},r.prototype.removeMessageName=function(e){var t=this.checkMessageConfigForm.get("messageNames").value,r=t.indexOf(e);r>=0&&(t.splice(r,1),this.checkMessageConfigForm.get("messageNames").setValue(t,{emitEvent:!0}))},r.prototype.removeMetadataName=function(e){var t=this.checkMessageConfigForm.get("metadataNames").value,r=t.indexOf(e);r>=0&&(t.splice(r,1),this.checkMessageConfigForm.get("metadataNames").setValue(t,{emitEvent:!0}))},r.prototype.addMessageName=function(e){var t=e.input,r=e.value;if((r||"").trim()){r=r.trim();var n=this.checkMessageConfigForm.get("messageNames").value;n&&-1!==n.indexOf(r)||(n||(n=[]),n.push(r),this.checkMessageConfigForm.get("messageNames").setValue(n,{emitEvent:!0}))}t&&(t.value="")},r.prototype.addMetadataName=function(e){var t=e.input,r=e.value;if((r||"").trim()){r=r.trim();var n=this.checkMessageConfigForm.get("metadataNames").value;n&&-1!==n.indexOf(r)||(n||(n=[]),n.push(r),this.checkMessageConfigForm.get("metadataNames").setValue(n,{emitEvent:!0}))}t&&(t.value="")},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-filter-node-check-message-config",template:'\n',styles:[":host label.tb-title{margin-bottom:-10px}"]}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),ge=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n.entitySearchDirection=Object.keys(a.EntitySearchDirection),n.entitySearchDirectionTranslationsMap=a.entitySearchDirectionTranslations,n}return y(r,e),r.prototype.configForm=function(){return this.checkRelationConfigForm},r.prototype.onConfigurationSet=function(e){this.checkRelationConfigForm=this.fb.group({checkForSingleEntity:[!!e&&e.checkForSingleEntity,[]],direction:[e?e.direction:null,[]],entityType:[e?e.entityType:null,e&&e.checkForSingleEntity?[i.Validators.required]:[]],entityId:[e?e.entityId:null,e&&e.checkForSingleEntity?[i.Validators.required]:[]],relationType:[e?e.relationType:null,[i.Validators.required]]})},r.prototype.validatorTriggers=function(){return["checkForSingleEntity"]},r.prototype.updateValidators=function(e){var t=this.checkRelationConfigForm.get("checkForSingleEntity").value;this.checkRelationConfigForm.get("entityType").setValidators(t?[i.Validators.required]:[]),this.checkRelationConfigForm.get("entityType").updateValueAndValidity({emitEvent:e}),this.checkRelationConfigForm.get("entityId").setValidators(t?[i.Validators.required]:[]),this.checkRelationConfigForm.get("entityId").updateValueAndValidity({emitEvent:e})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-filter-node-check-relation-config",template:'\n \n {{ \'tb.rulenode.check-relation-to-specific-entity\' | translate }}\n \n tb.rulenode.check-relation-hint
\n \n relation.direction\n \n \n {{ entitySearchDirectionTranslationsMap.get(direction) | translate }}\n \n \n \n \n \n \n \n \n
\n \n \n\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),ye=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n.perimeterType=M,n.perimeterTypes=Object.keys(M),n.perimeterTypeTranslationMap=w,n.rangeUnits=Object.keys(D),n.rangeUnitTranslationMap=B,n}return y(r,e),r.prototype.configForm=function(){return this.geoFilterConfigForm},r.prototype.onConfigurationSet=function(e){this.geoFilterConfigForm=this.fb.group({latitudeKeyName:[e?e.latitudeKeyName:null,[i.Validators.required]],longitudeKeyName:[e?e.longitudeKeyName:null,[i.Validators.required]],fetchPerimeterInfoFromMessageMetadata:[!!e&&e.fetchPerimeterInfoFromMessageMetadata,[]],perimeterType:[e?e.perimeterType:null,[]],centerLatitude:[e?e.centerLatitude:null,[]],centerLongitude:[e?e.centerLatitude:null,[]],range:[e?e.range:null,[]],rangeUnit:[e?e.rangeUnit:null,[]],polygonsDefinition:[e?e.polygonsDefinition:null,[]]})},r.prototype.validatorTriggers=function(){return["fetchPerimeterInfoFromMessageMetadata","perimeterType"]},r.prototype.updateValidators=function(e){var t=this.geoFilterConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value,r=this.geoFilterConfigForm.get("perimeterType").value;t?this.geoFilterConfigForm.get("perimeterType").setValidators([]):this.geoFilterConfigForm.get("perimeterType").setValidators([i.Validators.required]),t||r!==M.CIRCLE?(this.geoFilterConfigForm.get("centerLatitude").setValidators([]),this.geoFilterConfigForm.get("centerLongitude").setValidators([]),this.geoFilterConfigForm.get("range").setValidators([]),this.geoFilterConfigForm.get("rangeUnit").setValidators([])):(this.geoFilterConfigForm.get("centerLatitude").setValidators([i.Validators.required,i.Validators.min(-90),i.Validators.max(90)]),this.geoFilterConfigForm.get("centerLongitude").setValidators([i.Validators.required,i.Validators.min(-180),i.Validators.max(180)]),this.geoFilterConfigForm.get("range").setValidators([i.Validators.required,i.Validators.min(0)]),this.geoFilterConfigForm.get("rangeUnit").setValidators([i.Validators.required])),t||r!==M.POLYGON?this.geoFilterConfigForm.get("polygonsDefinition").setValidators([]):this.geoFilterConfigForm.get("polygonsDefinition").setValidators([i.Validators.required]),this.geoFilterConfigForm.get("perimeterType").updateValueAndValidity({emitEvent:!1}),this.geoFilterConfigForm.get("centerLatitude").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("centerLongitude").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("range").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("rangeUnit").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("polygonsDefinition").updateValueAndValidity({emitEvent:e})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-filter-node-gps-geofencing-config",template:'\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),be=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n}return y(r,e),r.prototype.configForm=function(){return this.messageTypeConfigForm},r.prototype.onConfigurationSet=function(e){this.messageTypeConfigForm=this.fb.group({messageTypes:[e?e.messageTypes:null,[i.Validators.required]]})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-filter-node-message-type-config",template:'\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),he=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n.allowedEntityTypes=[a.EntityType.DEVICE,a.EntityType.ASSET,a.EntityType.ENTITY_VIEW,a.EntityType.TENANT,a.EntityType.CUSTOMER,a.EntityType.USER,a.EntityType.DASHBOARD,a.EntityType.RULE_CHAIN,a.EntityType.RULE_NODE],n}return y(r,e),r.prototype.configForm=function(){return this.originatorTypeConfigForm},r.prototype.onConfigurationSet=function(e){this.originatorTypeConfigForm=this.fb.group({originatorTypes:[e?e.originatorTypes:null,[i.Validators.required]]})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-filter-node-originator-type-config",template:'\n \n \n \n\n',styles:[":host ::ng-deep tb-entity-type-list .mat-form-field-flex{padding-top:0}:host ::ng-deep tb-entity-type-list .mat-form-field-infix{border-top:0}"]}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),Ce=function(e){function r(t,r,n,a){var o=e.call(this,t)||this;return o.store=t,o.fb=r,o.nodeScriptTestService=n,o.translate=a,o}return y(r,e),r.prototype.configForm=function(){return this.scriptConfigForm},r.prototype.onConfigurationSet=function(e){this.scriptConfigForm=this.fb.group({jsScript:[e?e.jsScript:null,[i.Validators.required]]})},r.prototype.testScript=function(){var e=this,t=this.scriptConfigForm.get("jsScript").value;this.nodeScriptTestService.testNodeScript(t,"filter",this.translate.instant("tb.rulenode.filter"),"Filter",["msg","metadata","msgType"],this.ruleNodeId).subscribe((function(t){t&&e.scriptConfigForm.get("jsScript").setValue(t)}))},r.prototype.onValidate=function(){this.jsFuncComponent.validateOnSubmit()},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder},{type:l.NodeScriptTestService},{type:n.TranslateService}]},b([t.ViewChild("jsFuncComponent",{static:!0}),h("design:type",a.JsFuncComponent)],r.prototype,"jsFuncComponent",void 0),r=b([t.Component({selector:"tb-filter-node-script-config",template:'\n \n \n \n \n \n
\n\n'}),h("design:paramtypes",[o.Store,i.FormBuilder,l.NodeScriptTestService,n.TranslateService])],r)}(a.RuleNodeConfigurationComponent),ve=function(e){function r(t,r,n,a){var o=e.call(this,t)||this;return o.store=t,o.fb=r,o.nodeScriptTestService=n,o.translate=a,o}return y(r,e),r.prototype.configForm=function(){return this.switchConfigForm},r.prototype.onConfigurationSet=function(e){this.switchConfigForm=this.fb.group({jsScript:[e?e.jsScript:null,[i.Validators.required]]})},r.prototype.testScript=function(){var e=this,t=this.switchConfigForm.get("jsScript").value;this.nodeScriptTestService.testNodeScript(t,"switch",this.translate.instant("tb.rulenode.switch"),"Switch",["msg","metadata","msgType"],this.ruleNodeId).subscribe((function(t){t&&e.switchConfigForm.get("jsScript").setValue(t)}))},r.prototype.onValidate=function(){this.jsFuncComponent.validateOnSubmit()},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder},{type:l.NodeScriptTestService},{type:n.TranslateService}]},b([t.ViewChild("jsFuncComponent",{static:!0}),h("design:type",a.JsFuncComponent)],r.prototype,"jsFuncComponent",void 0),r=b([t.Component({selector:"tb-filter-node-switch-config",template:'\n \n \n \n \n \n
\n\n'}),h("design:paramtypes",[o.Store,i.FormBuilder,l.NodeScriptTestService,n.TranslateService])],r)}(a.RuleNodeConfigurationComponent),Fe=function(e){function r(t,r,n){var o,l,s=e.call(this,t)||this;s.store=t,s.translate=r,s.fb=n,s.alarmStatusTranslationsMap=a.alarmStatusTranslations,s.alarmStatusList=[],s.searchText="",s.displayStatusFn=s.displayStatus.bind(s);try{for(var m=C(Object.keys(a.AlarmStatus)),u=m.next();!u.done;u=m.next()){var d=u.value;s.alarmStatusList.push(a.AlarmStatus[d])}}catch(e){o={error:e}}finally{try{u&&!u.done&&(l=m.return)&&l.call(m)}finally{if(o)throw o.error}}return s.statusFormControl=new i.FormControl(""),s.filteredAlarmStatus=s.statusFormControl.valueChanges.pipe(f.startWith(""),f.map((function(e){return e||""})),f.mergeMap((function(e){return s.fetchAlarmStatus(e)})),f.share()),s}return y(r,e),r.prototype.ngOnInit=function(){e.prototype.ngOnInit.call(this)},r.prototype.configForm=function(){return this.alarmStatusConfigForm},r.prototype.prepareInputConfig=function(e){return this.searchText="",this.statusFormControl.patchValue("",{emitEvent:!0}),e},r.prototype.onConfigurationSet=function(e){this.alarmStatusConfigForm=this.fb.group({alarmStatusList:[e?e.alarmStatusList:null,[i.Validators.required]]})},r.prototype.displayStatus=function(e){return e?this.translate.instant(a.alarmStatusTranslations.get(e)):void 0},r.prototype.fetchAlarmStatus=function(e){var t=this,r=this.getAlarmStatusList();if(this.searchText=e,this.searchText&&this.searchText.length){var n=this.searchText.toUpperCase();return c.of(r.filter((function(e){return t.translate.instant(a.alarmStatusTranslations.get(a.AlarmStatus[e])).toUpperCase().includes(n)})))}return c.of(r)},r.prototype.alarmStatusSelected=function(e){this.addAlarmStatus(e.option.value),this.clear("")},r.prototype.removeAlarmStatus=function(e){var t=this.alarmStatusConfigForm.get("alarmStatusList").value;if(t){var r=t.indexOf(e);r>=0&&(t.splice(r,1),this.alarmStatusConfigForm.get("alarmStatusList").setValue(t))}},r.prototype.addAlarmStatus=function(e){var t=this.alarmStatusConfigForm.get("alarmStatusList").value;t||(t=[]),-1===t.indexOf(e)&&(t.push(e),this.alarmStatusConfigForm.get("alarmStatusList").setValue(t))},r.prototype.getAlarmStatusList=function(){var e=this;return this.alarmStatusList.filter((function(t){return-1===e.alarmStatusConfigForm.get("alarmStatusList").value.indexOf(t)}))},r.prototype.onAlarmStatusInputFocus=function(){this.statusFormControl.updateValueAndValidity({onlySelf:!0,emitEvent:!0})},r.prototype.clear=function(e){var t=this;void 0===e&&(e=""),this.alarmStatusInput.nativeElement.value=e,this.statusFormControl.patchValue(null,{emitEvent:!0}),setTimeout((function(){t.alarmStatusInput.nativeElement.blur(),t.alarmStatusInput.nativeElement.focus()}),0)},r.ctorParameters=function(){return[{type:o.Store},{type:n.TranslateService},{type:i.FormBuilder}]},b([t.ViewChild("alarmStatusInput",{static:!1}),h("design:type",t.ElementRef)],r.prototype,"alarmStatusInput",void 0),r=b([t.Component({selector:"tb-filter-node-check-alarm-status-config",template:'\n \n tb.rulenode.alarm-status-filter\n \n \n \n {{alarmStatusTranslationsMap.get(alarmStatus) | translate}}\n \n close\n \n \n \n \n \n \n \n \n \n
\n tb.rulenode.no-alarm-status-matching\n
\n
\n \n \n \n \n \n\n\n\n'}),h("design:paramtypes",[o.Store,n.TranslateService,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),Te=function(){function e(){}return e=b([t.NgModule({declarations:[fe,ge,ye,be,he,Ce,ve,Fe],imports:[r.CommonModule,a.SharedModule,ne],exports:[fe,ge,ye,be,he,Ce,ve,Fe]})],e)}(),xe=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n}return y(r,e),r.prototype.configForm=function(){return this.customerAttributesConfigForm},r.prototype.onConfigurationSet=function(e){this.customerAttributesConfigForm=this.fb.group({telemetry:[!!e&&e.telemetry,[]],attrMapping:[e?e.attrMapping:null,[i.Validators.required]]})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-enrichment-node-customer-attributes-config",template:'\n \n \n {{ \'tb.rulenode.latest-telemetry\' | translate }}\n \n \n \n\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),qe=function(e){function r(t,r,n){var a,o,l=e.call(this,t)||this;l.store=t,l.translate=r,l.fb=n,l.entityDetailsTranslationsMap=H,l.entityDetailsList=[],l.searchText="",l.displayDetailsFn=l.displayDetails.bind(l);try{for(var s=C(Object.keys(K)),m=s.next();!m.done;m=s.next()){var u=m.value;l.entityDetailsList.push(K[u])}}catch(e){a={error:e}}finally{try{m&&!m.done&&(o=s.return)&&o.call(s)}finally{if(a)throw a.error}}return l.detailsFormControl=new i.FormControl(""),l.filteredEntityDetails=l.detailsFormControl.valueChanges.pipe(f.startWith(""),f.map((function(e){return e||""})),f.mergeMap((function(e){return l.fetchEntityDetails(e)})),f.share()),l}return y(r,e),r.prototype.ngOnInit=function(){e.prototype.ngOnInit.call(this)},r.prototype.configForm=function(){return this.entityDetailsConfigForm},r.prototype.prepareInputConfig=function(e){return this.searchText="",this.detailsFormControl.patchValue("",{emitEvent:!0}),e},r.prototype.onConfigurationSet=function(e){this.entityDetailsConfigForm=this.fb.group({detailsList:[e?e.detailsList:null,[i.Validators.required]],addToMetadata:[!!e&&e.addToMetadata,[]]})},r.prototype.displayDetails=function(e){return e?this.translate.instant(H.get(e)):void 0},r.prototype.fetchEntityDetails=function(e){var t=this;if(this.searchText=e,this.searchText&&this.searchText.length){var r=this.searchText.toUpperCase();return c.of(this.entityDetailsList.filter((function(e){return t.translate.instant(H.get(K[e])).toUpperCase().includes(r)})))}return c.of(this.entityDetailsList)},r.prototype.detailsFieldSelected=function(e){this.addDetailsField(e.option.value),this.clear("")},r.prototype.removeDetailsField=function(e){var t=this.entityDetailsConfigForm.get("detailsList").value;if(t){var r=t.indexOf(e);r>=0&&(t.splice(r,1),this.entityDetailsConfigForm.get("detailsList").setValue(t))}},r.prototype.addDetailsField=function(e){var t=this.entityDetailsConfigForm.get("detailsList").value;t||(t=[]),-1===t.indexOf(e)&&(t.push(e),this.entityDetailsConfigForm.get("detailsList").setValue(t))},r.prototype.onEntityDetailsInputFocus=function(){this.detailsFormControl.updateValueAndValidity({onlySelf:!0,emitEvent:!0})},r.prototype.clear=function(e){var t=this;void 0===e&&(e=""),this.detailsInput.nativeElement.value=e,this.detailsFormControl.patchValue(null,{emitEvent:!0}),setTimeout((function(){t.detailsInput.nativeElement.blur(),t.detailsInput.nativeElement.focus()}),0)},r.ctorParameters=function(){return[{type:o.Store},{type:n.TranslateService},{type:i.FormBuilder}]},b([t.ViewChild("detailsInput",{static:!1}),h("design:type",t.ElementRef)],r.prototype,"detailsInput",void 0),r=b([t.Component({selector:"tb-enrichment-node-entity-details-config",template:'\n \n tb.rulenode.entity-details\n \n \n \n {{entityDetailsTranslationsMap.get(details) | translate}}\n \n close\n \n \n \n \n \n \n \n \n \n
\n tb.rulenode.no-entity-details-matching\n
\n
\n \n \n \n \n \n {{ \'tb.rulenode.add-to-metadata\' | translate }}\n \n tb.rulenode.add-to-metadata-hint
\n\n',styles:[":host ::ng-deep mat-form-field.entity-fields-list .mat-form-field-wrapper{margin-bottom:-1.25em}"]}),h("design:paramtypes",[o.Store,n.TranslateService,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),Se=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n.separatorKeysCodes=[s.ENTER,s.COMMA,s.SEMICOLON],n}return y(r,e),r.prototype.configForm=function(){return this.deviceAttributesConfigForm},r.prototype.onConfigurationSet=function(e){this.deviceAttributesConfigForm=this.fb.group({deviceRelationsQuery:[e?e.deviceRelationsQuery:null,[i.Validators.required]],tellFailureIfAbsent:[!!e&&e.tellFailureIfAbsent,[]],clientAttributeNames:[e?e.clientAttributeNames:null,[]],sharedAttributeNames:[e?e.sharedAttributeNames:null,[]],serverAttributeNames:[e?e.serverAttributeNames:null,[]],latestTsKeyNames:[e?e.latestTsKeyNames:null,[]],getLatestValueWithTs:[!!e&&e.getLatestValueWithTs,[]]})},r.prototype.removeKey=function(e,t){var r=this.deviceAttributesConfigForm.get(t).value,n=r.indexOf(e);n>=0&&(r.splice(n,1),this.deviceAttributesConfigForm.get(t).setValue(r,{emitEvent:!0}))},r.prototype.addKey=function(e,t){var r=e.input,n=e.value;if((n||"").trim()){n=n.trim();var a=this.deviceAttributesConfigForm.get(t).value;a&&-1!==a.indexOf(n)||(a||(a=[]),a.push(n),this.deviceAttributesConfigForm.get(t).setValue(a,{emitEvent:!0}))}r&&(r.value="")},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-enrichment-node-device-attributes-config",template:'\n',styles:[":host label.tb-title{margin-bottom:-10px}"]}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),Ie=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n.separatorKeysCodes=[s.ENTER,s.COMMA,s.SEMICOLON],n}return y(r,e),r.prototype.configForm=function(){return this.originatorAttributesConfigForm},r.prototype.onConfigurationSet=function(e){this.originatorAttributesConfigForm=this.fb.group({tellFailureIfAbsent:[!!e&&e.tellFailureIfAbsent,[]],clientAttributeNames:[e?e.clientAttributeNames:null,[]],sharedAttributeNames:[e?e.sharedAttributeNames:null,[]],serverAttributeNames:[e?e.serverAttributeNames:null,[]],latestTsKeyNames:[e?e.latestTsKeyNames:null,[]],getLatestValueWithTs:[!!e&&e.getLatestValueWithTs,[]]})},r.prototype.removeKey=function(e,t){var r=this.originatorAttributesConfigForm.get(t).value,n=r.indexOf(e);n>=0&&(r.splice(n,1),this.originatorAttributesConfigForm.get(t).setValue(r,{emitEvent:!0}))},r.prototype.addKey=function(e,t){var r=e.input,n=e.value;if((n||"").trim()){n=n.trim();var a=this.originatorAttributesConfigForm.get(t).value;a&&-1!==a.indexOf(n)||(a||(a=[]),a.push(n),this.originatorAttributesConfigForm.get(t).setValue(a,{emitEvent:!0}))}r&&(r.value="")},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-enrichment-node-originator-attributes-config",template:'\n',styles:[":host label.tb-title{margin-bottom:-10px}"]}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),ke=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n}return y(r,e),r.prototype.configForm=function(){return this.originatorFieldsConfigForm},r.prototype.onConfigurationSet=function(e){this.originatorFieldsConfigForm=this.fb.group({fieldsMapping:[e?e.fieldsMapping:null,[i.Validators.required]]})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-enrichment-node-originator-fields-config",template:'\n \n \n \n\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),Ne=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n.separatorKeysCodes=[s.ENTER,s.COMMA,s.SEMICOLON],n.fetchMode=j,n.fetchModes=Object.keys(j),n.samplingOrders=Object.keys(U),n.timeUnits=Object.keys(R),n.timeUnitsTranslationMap=O,n}return y(r,e),r.prototype.configForm=function(){return this.getTelemetryFromDatabaseConfigForm},r.prototype.onConfigurationSet=function(e){this.getTelemetryFromDatabaseConfigForm=this.fb.group({latestTsKeyNames:[e?e.latestTsKeyNames:null,[]],fetchMode:[e?e.fetchMode:null,[i.Validators.required]],orderBy:[e?e.orderBy:null,[]],limit:[e?e.limit:null,[]],useMetadataIntervalPatterns:[!!e&&e.useMetadataIntervalPatterns,[]],startInterval:[e?e.startInterval:null,[]],startIntervalTimeUnit:[e?e.startIntervalTimeUnit:null,[]],endInterval:[e?e.endInterval:null,[]],endIntervalTimeUnit:[e?e.endIntervalTimeUnit:null,[]],startIntervalPattern:[e?e.startIntervalPattern:null,[]],endIntervalPattern:[e?e.endIntervalPattern:null,[]]})},r.prototype.validatorTriggers=function(){return["fetchMode","useMetadataIntervalPatterns"]},r.prototype.updateValidators=function(e){var t=this.getTelemetryFromDatabaseConfigForm.get("fetchMode").value,r=this.getTelemetryFromDatabaseConfigForm.get("useMetadataIntervalPatterns").value;t&&t===j.ALL?(this.getTelemetryFromDatabaseConfigForm.get("orderBy").setValidators([i.Validators.required]),this.getTelemetryFromDatabaseConfigForm.get("limit").setValidators([i.Validators.required,i.Validators.min(2),i.Validators.max(1e3)])):(this.getTelemetryFromDatabaseConfigForm.get("orderBy").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("limit").setValidators([])),r?(this.getTelemetryFromDatabaseConfigForm.get("startInterval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalTimeUnit").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("endInterval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalTimeUnit").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").setValidators([i.Validators.required]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").setValidators([i.Validators.required])):(this.getTelemetryFromDatabaseConfigForm.get("startInterval").setValidators([i.Validators.required,i.Validators.min(1),i.Validators.max(2147483647)]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalTimeUnit").setValidators([i.Validators.required]),this.getTelemetryFromDatabaseConfigForm.get("endInterval").setValidators([i.Validators.required,i.Validators.min(1),i.Validators.max(2147483647)]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalTimeUnit").setValidators([i.Validators.required]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").setValidators([])),this.getTelemetryFromDatabaseConfigForm.get("orderBy").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("limit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("startInterval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("startIntervalTimeUnit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("endInterval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("endIntervalTimeUnit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").updateValueAndValidity({emitEvent:e})},r.prototype.removeKey=function(e,t){var r=this.getTelemetryFromDatabaseConfigForm.get(t).value,n=r.indexOf(e);n>=0&&(r.splice(n,1),this.getTelemetryFromDatabaseConfigForm.get(t).setValue(r,{emitEvent:!0}))},r.prototype.addKey=function(e,t){var r=e.input,n=e.value;if((n||"").trim()){n=n.trim();var a=this.getTelemetryFromDatabaseConfigForm.get(t).value;a&&-1!==a.indexOf(n)||(a||(a=[]),a.push(n),this.getTelemetryFromDatabaseConfigForm.get(t).setValue(a,{emitEvent:!0}))}r&&(r.value="")},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-enrichment-node-get-telemetry-from-database",template:'\n',styles:[":host label.tb-title{margin-bottom:-10px}"]}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),Ve=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n}return y(r,e),r.prototype.configForm=function(){return this.relatedAttributesConfigForm},r.prototype.onConfigurationSet=function(e){this.relatedAttributesConfigForm=this.fb.group({relationsQuery:[e?e.relationsQuery:null,[i.Validators.required]],telemetry:[!!e&&e.telemetry,[]],attrMapping:[e?e.attrMapping:null,[i.Validators.required]]})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-enrichment-node-related-attributes-config",template:'\n \n \n \n \n \n {{ \'tb.rulenode.latest-telemetry\' | translate }}\n \n \n \n\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),Ee=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n}return y(r,e),r.prototype.configForm=function(){return this.tenantAttributesConfigForm},r.prototype.onConfigurationSet=function(e){this.tenantAttributesConfigForm=this.fb.group({telemetry:[!!e&&e.telemetry,[]],attrMapping:[e?e.attrMapping:null,[i.Validators.required]]})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-enrichment-node-tenant-attributes-config",template:'\n \n \n {{ \'tb.rulenode.latest-telemetry\' | translate }}\n \n \n \n\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),Ae=function(){function e(){}return e=b([t.NgModule({declarations:[xe,qe,Se,Ie,ke,Ne,Ve,Ee],imports:[r.CommonModule,a.SharedModule,ne],exports:[xe,qe,Se,Ie,ke,Ne,Ve,Ee]})],e)}(),Le=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n.originatorSource=v,n.originatorSources=Object.keys(v),n.originatorSourceTranslationMap=P,n}return y(r,e),r.prototype.configForm=function(){return this.changeOriginatorConfigForm},r.prototype.onConfigurationSet=function(e){this.changeOriginatorConfigForm=this.fb.group({originatorSource:[e?e.originatorSource:null,[i.Validators.required]],relationsQuery:[e?e.relationsQuery:null,[]]})},r.prototype.validatorTriggers=function(){return["originatorSource"]},r.prototype.updateValidators=function(e){var t=this.changeOriginatorConfigForm.get("originatorSource").value;t&&t===v.RELATED?this.changeOriginatorConfigForm.get("relationsQuery").setValidators([i.Validators.required]):this.changeOriginatorConfigForm.get("relationsQuery").setValidators([]),this.changeOriginatorConfigForm.get("relationsQuery").updateValueAndValidity({emitEvent:e})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-transformation-node-change-originator-config",template:'\n \n tb.rulenode.originator-source\n \n \n {{ originatorSourceTranslationMap.get(source) | translate }}\n \n \n \n \n \n \n \n \n\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),Me=function(e){function r(t,r,n,a){var o=e.call(this,t)||this;return o.store=t,o.fb=r,o.nodeScriptTestService=n,o.translate=a,o}return y(r,e),r.prototype.configForm=function(){return this.scriptConfigForm},r.prototype.onConfigurationSet=function(e){this.scriptConfigForm=this.fb.group({jsScript:[e?e.jsScript:null,[i.Validators.required]]})},r.prototype.testScript=function(){var e=this,t=this.scriptConfigForm.get("jsScript").value;this.nodeScriptTestService.testNodeScript(t,"update",this.translate.instant("tb.rulenode.transformer"),"Transform",["msg","metadata","msgType"],this.ruleNodeId).subscribe((function(t){t&&e.scriptConfigForm.get("jsScript").setValue(t)}))},r.prototype.onValidate=function(){this.jsFuncComponent.validateOnSubmit()},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder},{type:l.NodeScriptTestService},{type:n.TranslateService}]},b([t.ViewChild("jsFuncComponent",{static:!0}),h("design:type",a.JsFuncComponent)],r.prototype,"jsFuncComponent",void 0),r=b([t.Component({selector:"tb-transformation-node-script-config",template:'\n \n \n \n \n \n
\n\n'}),h("design:paramtypes",[o.Store,i.FormBuilder,l.NodeScriptTestService,n.TranslateService])],r)}(a.RuleNodeConfigurationComponent),Pe=function(e){function r(t,r){var n=e.call(this,t)||this;return n.store=t,n.fb=r,n}return y(r,e),r.prototype.configForm=function(){return this.toEmailConfigForm},r.prototype.onConfigurationSet=function(e){this.toEmailConfigForm=this.fb.group({fromTemplate:[e?e.fromTemplate:null,[i.Validators.required]],toTemplate:[e?e.toTemplate:null,[i.Validators.required]],ccTemplate:[e?e.ccTemplate:null,[]],bccTemplate:[e?e.bccTemplate:null,[]],subjectTemplate:[e?e.subjectTemplate:null,[i.Validators.required]],bodyTemplate:[e?e.bodyTemplate:null,[i.Validators.required]]})},r.ctorParameters=function(){return[{type:o.Store},{type:i.FormBuilder}]},r=b([t.Component({selector:"tb-transformation-node-to-email-config",template:'\n \n tb.rulenode.from-template\n \n \n {{ \'tb.rulenode.from-template-required\' | translate }}\n \n \n \n \n tb.rulenode.to-template\n \n \n {{ \'tb.rulenode.to-template-required\' | translate }}\n \n \n \n \n tb.rulenode.cc-template\n \n \n \n \n tb.rulenode.bcc-template\n \n \n \n \n tb.rulenode.subject-template\n \n \n {{ \'tb.rulenode.subject-template-required\' | translate }}\n \n \n \n \n tb.rulenode.body-template\n \n \n {{ \'tb.rulenode.body-template-required\' | translate }}\n \n \n \n\n'}),h("design:paramtypes",[o.Store,i.FormBuilder])],r)}(a.RuleNodeConfigurationComponent),Re=function(){function e(){}return e=b([t.NgModule({declarations:[Le,Me,Pe],imports:[r.CommonModule,a.SharedModule,ne],exports:[Le,Me,Pe]})],e)}(),we=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 ${metaKeyName} 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 ${metaKeyName} 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 ${metaKeyName} 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.",limit:"Limit","limit-hint":"Min limit value is 2, max - 1000. In case you want to fetch a single entry, select fetch mode 'FIRST' or 'LAST'.","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 ${metaKeyName} to substitute variables from metadata","relation-type-pattern-required":"Relation type pattern is required","relation-types-list":"Relation types to propagate","relation-types-list-hint":"If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.","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 ${metaKeyName} 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","alarm-status-filter":"Alarm status filter","alarm-status-list-empty":"Alarm status list is empty","no-alarm-status-matching":"No alarm status matching were found.",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 ${metaKeyName} 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 ${metaKeyName} 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 ${metaKeyName} to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use ${metaKeyName} 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 ${metaKeyName} to substitute variables from metadata","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory","read-timeout":"Read timeout in millis","read-timeout-hint":"The value of 0 means an infinite timeout","max-parallel-requests-count":"Max number of parallel requests","max-parallel-requests-count-hint":"The value of 0 specifies no limit in parallel processing",headers:"Headers","headers-hint":"Use ${metaKeyName} 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 ${metaKeyName} 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 ${metaKeyName} 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 ${metaKeyName} 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 ${metaKeyName} 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 ${metaKeyName} to substitute variables from metadata","end-interval-pattern-hint":"End interval pattern, use ${metaKeyName} 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","tls-version":"TLS version","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".',"get-latest-value-with-ts":"Fetch Latest telemetry with Timestamp","get-latest-value-with-ts-hint":'If selected, latest telemetry values will be added to the outbound message metadata with timestamp, e.g: "temp": "{\\"ts\\":1574329385897,\\"value\\":42}"',"use-redis-queue":"Use redis queue for message persistence","trim-redis-queue":"Trim redis queue","redis-queue-max-size":"Redis queue max size"},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}},!0)}(e)}return e.ctorParameters=function(){return[{type:n.TranslateService}]},e=b([t.NgModule({declarations:[F],imports:[r.CommonModule,a.SharedModule],exports:[ce,Te,Ae,Re,F]}),h("design:paramtypes",[n.TranslateService])],e)}();e.RuleNodeCoreConfigModule=we,e.ɵa=F,e.ɵb=ce,e.ɵba=ne,e.ɵbb=X,e.ɵbc=ee,e.ɵbd=te,e.ɵbe=re,e.ɵbf=Te,e.ɵbg=fe,e.ɵbh=ge,e.ɵbi=ye,e.ɵbj=be,e.ɵbk=he,e.ɵbl=Ce,e.ɵbm=ve,e.ɵbn=Fe,e.ɵbo=Ae,e.ɵbp=xe,e.ɵbq=qe,e.ɵbr=Se,e.ɵbs=Ie,e.ɵbt=ke,e.ɵbu=Ne,e.ɵbv=Ve,e.ɵbw=Ee,e.ɵbx=Re,e.ɵby=Le,e.ɵbz=Me,e.ɵc=T,e.ɵca=Pe,e.ɵd=x,e.ɵe=q,e.ɵf=S,e.ɵg=I,e.ɵh=k,e.ɵi=N,e.ɵj=V,e.ɵk=E,e.ɵl=A,e.ɵm=L,e.ɵn=W,e.ɵo=J,e.ɵp=Y,e.ɵq=Z,e.ɵr=ae,e.ɵs=oe,e.ɵt=ie,e.ɵu=le,e.ɵv=se,e.ɵw=me,e.ɵx=ue,e.ɵy=de,e.ɵz=pe,Object.defineProperty(e,"__esModule",{value:!0})}));
-//# sourceMappingURL=rulenode-core-config.umd.min.js.map
\ No newline at end of file
+//# sourceMappingURL=rulenode-core-config.umd.min.js.map
diff --git a/ui-ngx/LICENSE b/ui-ngx/LICENSE
new file mode 100644
index 0000000000..f49a4e16e6
--- /dev/null
+++ b/ui-ngx/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
\ No newline at end of file
diff --git a/ui-ngx/angular.json b/ui-ngx/angular.json
index f88469f4f1..2ca77a91ec 100644
--- a/ui-ngx/angular.json
+++ b/ui-ngx/angular.json
@@ -37,7 +37,9 @@
"node_modules/tooltipster/dist/css/plugins/tooltipster/sideTip/themes/tooltipster-sideTip-shadow.min.css",
"src/app/shared/components/json-form/react/json-form.scss",
"node_modules/rc-select/assets/index.less",
- "node_modules/jstree-bootstrap-theme/dist/themes/proton/style.min.css"
+ "node_modules/jstree-bootstrap-theme/dist/themes/proton/style.min.css",
+ "node_modules/leaflet/dist/leaflet.css",
+ "src/app/modules/home/components/widget/lib/maps/markers.scss"
],
"stylePreprocessorOptions": {
"includePaths": [
diff --git a/ui-ngx/package-lock.json b/ui-ngx/package-lock.json
index 57f1a26264..1df4bab465 100644
--- a/ui-ngx/package-lock.json
+++ b/ui-ngx/package-lock.json
@@ -1833,6 +1833,12 @@
"resolved": "https://registry.npmjs.org/@types/flowjs/-/flowjs-2.13.1.tgz",
"integrity": "sha512-cPuORQrWmJV7pmiSt1ApDOsQSooVka53Ugr3LB0MW/bsG/fDtOXSxsT5Aiej98VD3eCIZNyABfk3NBWU7CorsQ=="
},
+ "@types/geojson": {
+ "version": "7946.0.7",
+ "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.7.tgz",
+ "integrity": "sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ==",
+ "dev": true
+ },
"@types/glob": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz",
@@ -1882,6 +1888,30 @@
"@types/jquery": "*"
}
},
+ "@types/leaflet": {
+ "version": "1.5.9",
+ "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.5.9.tgz",
+ "integrity": "sha512-vFj2Y764lC6PpmmoFTui3yEMAKf+Dy08eGIaFgM+efJoKNxDP/bsEr2UHloP6kkJqc+4VO9+KYea2ZhiZCnNrQ==",
+ "dev": true,
+ "requires": {
+ "@types/geojson": "*"
+ }
+ },
+ "@types/leaflet-polylinedecorator": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@types/leaflet-polylinedecorator/-/leaflet-polylinedecorator-1.6.0.tgz",
+ "integrity": "sha512-Z2BXZDjKEqHclwrAmhYdF1RwyFfa/NFxsoF79sitzaj5D/4YWHp/zDRcUZar5cQFKRgK66AYEIF7nKVuMzUGdw==",
+ "dev": true,
+ "requires": {
+ "@types/leaflet": "*"
+ }
+ },
+ "@types/lodash": {
+ "version": "4.14.149",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz",
+ "integrity": "sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==",
+ "dev": true
+ },
"@types/minimatch": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
@@ -1894,9 +1924,9 @@
"integrity": "sha512-13gmo3M2qVvjQrWNseqM3+cR6S2Ss3grbR2NZltgMq94wOwqJYQdgn8qzwDshzgXqMlSUtyPZjysImmktu22ew=="
},
"@types/node": {
- "version": "13.7.2",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.2.tgz",
- "integrity": "sha512-uvilvAQbdJvnSBFcKJ2td4016urcGvsiR+N4dHGU87ml8O2Vl6l+ErOi9w0kXSPiwJ1AYlIW+0pDXDWWMOiWbw==",
+ "version": "13.7.6",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.6.tgz",
+ "integrity": "sha512-eyK7MWD0R1HqVTp+PtwRgFeIsemzuj4gBFSQxfPHY5iMjS7474e5wq+VFgTcdpyHeNxyKSaetYAjdMLJlKoWqA==",
"dev": true
},
"@types/prop-types": {
@@ -8240,6 +8270,47 @@
"invert-kv": "^2.0.0"
}
},
+ "leaflet": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.6.0.tgz",
+ "integrity": "sha512-CPkhyqWUKZKFJ6K8umN5/D2wrJ2+/8UIpXppY7QDnUZW5bZL5+SEI2J7GBpwh4LIupOKqbNSQXgqmrEJopHVNQ=="
+ },
+ "leaflet-geometryutil": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/leaflet-geometryutil/-/leaflet-geometryutil-0.9.3.tgz",
+ "integrity": "sha512-Wi6YvfNx/Xu9q35AEfXpsUXmIFLen/MO+C2qimxHRnjyeyOxBhdcZa6kSiReaOX0cGK7yQInqrzz0dkIqZ8Dpg==",
+ "requires": {
+ "leaflet": ">=0.7.0"
+ }
+ },
+ "leaflet-polylinedecorator": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/leaflet-polylinedecorator/-/leaflet-polylinedecorator-1.6.0.tgz",
+ "integrity": "sha1-nvef0bUwLWe3Lv6Vmo7NJVPycmY=",
+ "requires": {
+ "leaflet-rotatedmarker": "^0.2.0"
+ }
+ },
+ "leaflet-providers": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/leaflet-providers/-/leaflet-providers-1.9.1.tgz",
+ "integrity": "sha512-YpJB9y4/nT5NGicU9vuqlttJaCer6paD3J3b8Wrw+IIQvK9dtcdzE9CsTkDg7Dg9FeGp5NEr3hu17xcHbYI/2w=="
+ },
+ "leaflet-rotatedmarker": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/leaflet-rotatedmarker/-/leaflet-rotatedmarker-0.2.0.tgz",
+ "integrity": "sha1-RGf0n5jRv9VpWb2cZwUgPdJgEnc="
+ },
+ "leaflet.gridlayer.googlemutant": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/leaflet.gridlayer.googlemutant/-/leaflet.gridlayer.googlemutant-0.8.0.tgz",
+ "integrity": "sha512-Ain+jgDKRhlM6qNDDj2QFJa9vXUqV096N0PmpHO3DoNLS4I7EynTQCJXN+9qY4C51ZpV4Q4CI+apNv5XiP5aUA=="
+ },
+ "leaflet.markercluster": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/leaflet.markercluster/-/leaflet.markercluster-1.4.1.tgz",
+ "integrity": "sha512-ZSEpE/EFApR0bJ1w/dUGwTSUvWlpalKqIzkaYdYB7jaftQA/Y2Jav+eT4CMtEYFj+ZK4mswP13Q2acnPBnhGOw=="
+ },
"less": {
"version": "3.10.3",
"resolved": "https://registry.npmjs.org/less/-/less-3.10.3.tgz",
diff --git a/ui-ngx/package.json b/ui-ngx/package.json
index d11f30e086..beeaed5dad 100644
--- a/ui-ngx/package.json
+++ b/ui-ngx/package.json
@@ -56,6 +56,12 @@
"json-schema-defaults": "^0.4.0",
"jstree": "^3.3.9",
"jstree-bootstrap-theme": "^1.0.1",
+ "leaflet": "^1.6.0",
+ "leaflet-geometryutil": "^0.9.3",
+ "leaflet-polylinedecorator": "^1.6.0",
+ "leaflet-providers": "^1.9.1",
+ "leaflet.gridlayer.googlemutant": "^0.8.0",
+ "leaflet.markercluster": "^1.4.1",
"material-design-icons": "^3.0.1",
"messageformat": "^2.3.0",
"moment": "^2.24.0",
@@ -99,7 +105,9 @@
"@types/jquery": "^3.3.32",
"@types/js-beautify": "^1.8.1",
"@types/jstree": "^3.3.39",
- "@types/node": "^13.7.2",
+ "@types/leaflet": "^1.5.9",
+ "@types/leaflet-polylinedecorator": "^1.6.0",
+ "@types/lodash": "^4.14.149",
"@types/raphael": "^2.1.30",
"@types/react": "^16.9.20",
"@types/react-dom": "^16.9.5",
diff --git a/ui-ngx/src/app/core/schema-utils.ts b/ui-ngx/src/app/core/schema-utils.ts
new file mode 100644
index 0000000000..eb84fba92f
--- /dev/null
+++ b/ui-ngx/src/app/core/schema-utils.ts
@@ -0,0 +1,83 @@
+///
+/// Copyright © 2016-2020 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+import { JsonSchema, JsonSettingsSchema } from '@app/shared/public-api';
+
+
+export function initSchema(): JsonSettingsSchema {
+ return {
+ schema: {
+ type: 'object',
+ properties: {},
+ required: []
+ },
+ form: [],
+ groupInfoes: []
+ };
+}
+
+export function addGroupInfo(schema: JsonSettingsSchema, title: string) {
+ schema.groupInfoes.push({
+ formIndex: schema.groupInfoes?.length || 0,
+ GroupTitle: title
+ });
+}
+
+export function addToSchema(schema: JsonSettingsSchema, newSchema: JsonSettingsSchema) {
+ Object.assign(schema.schema.properties, newSchema.schema.properties);
+ schema.schema.required = schema.schema.required.concat(newSchema.schema.required);
+ schema.form.push(newSchema.form);
+}
+
+export function mergeSchemes(schemes: JsonSettingsSchema[]): JsonSettingsSchema {
+ return schemes.reduce((finalSchema: JsonSettingsSchema, schema: JsonSettingsSchema) => {
+ return {
+ schema: {
+ properties: {
+ ...finalSchema.schema.properties,
+ ...schema.schema.properties
+ },
+ required: [
+ ...finalSchema.schema.required,
+ ...schema.schema.required
+ ]
+ },
+ form: [
+ ...finalSchema.form,
+ ...schema.form
+ ]
+ } as JsonSettingsSchema;
+ }, initSchema());
+}
+
+export function addCondition(schema: JsonSettingsSchema, condition: String): JsonSettingsSchema {
+ schema.form = schema.form.map(element => {
+ if (typeof element === 'string') {
+ return {
+ key: element,
+ condition
+ }
+ }
+ if (typeof element == 'object') {
+ if (element.condition) {
+ element.condition += ' && ' + condition
+ }
+ else element.condition = condition;
+ }
+ return element;
+ });
+ return schema;
+}
\ No newline at end of file
diff --git a/ui-ngx/src/app/core/utils.ts b/ui-ngx/src/app/core/utils.ts
index 381aadfdb7..cddbed643b 100644
--- a/ui-ngx/src/app/core/utils.ts
+++ b/ui-ngx/src/app/core/utils.ts
@@ -15,8 +15,8 @@
///
import _ from 'lodash';
-import { Observable, Subject } from 'rxjs';
-import { finalize, share } from 'rxjs/operators';
+import { Observable, Subject, from, fromEvent, of } from 'rxjs';
+import { finalize, share, map } from 'rxjs/operators';
import base64js from 'base64-js';
export function onParentScrollOrWindowResize(el: Node): Observable {
@@ -101,7 +101,7 @@ export function isNumber(value: any): boolean {
}
export function isNumeric(value: any): boolean {
- return (value - parseFloat( value ) + 1) >= 0;
+ return (value - parseFloat(value) + 1) >= 0;
}
export function isString(value: any): boolean {
@@ -221,6 +221,18 @@ function scrollParents(node: Node): Node[] {
return scrollParentNodes;
}
+function hashCode(str) {
+ let hash = 0;
+ let i, char;
+ if (str.length == 0) return hash;
+ for (i = 0; i < str.length; i++) {
+ char = str.charCodeAt(i);
+ hash = ((hash << 5) - hash) + char;
+ hash = hash & hash; // Convert to 32bit integer
+ }
+ return hash;
+}
+
function easeInOut(
currentTime: number,
startTime: number,
@@ -411,3 +423,147 @@ export function snakeCase(name: string, separator: string): string {
export function getDescendantProp(obj: any, path: string): any {
return path.split('.').reduce((acc, part) => acc && acc[part], obj);
}
+
+export function imageLoader(imageUrl: string): Observable {
+ const image = new Image();
+ const imageLoad$ = fromEvent(image, 'load').pipe(map(event => image));
+ image.src = imageUrl;
+ return imageLoad$;
+}
+
+const imageAspectMap = {};
+
+export function aspectCache(imageUrl: string): Observable {
+ if (imageUrl?.length) {
+ const hash = hashCode(imageUrl);
+ let aspect = imageAspectMap[hash];
+ if (aspect) {
+ return of(aspect);
+ }
+ else return imageLoader(imageUrl).pipe(map(image => {
+ aspect = image.width / image.height;
+ imageAspectMap[hash] = aspect;
+ return aspect;
+ }))
+ }
+}
+
+
+export function parseArray(input: any[]): any[] {
+ const alliases: any = _(input).groupBy(el => el?.datasource?.aliasName).values().value();
+ return alliases.map((alliasArray, dsIndex) =>
+ alliasArray[0].data.map((el, i) => {
+ const obj = {
+ aliasName: alliasArray[0]?.datasource?.aliasName,
+ entityName: alliasArray[0]?.datasource?.entityName,
+ $datasource: alliasArray[0]?.datasource,
+ dsIndex,
+ time: el[0],
+ deviceType: null
+ };
+ alliasArray.forEach(el => {
+ obj[el?.dataKey?.label] = el?.data[i][1];
+ obj[el?.dataKey?.label + '|ts'] = el?.data[0][0];
+ if (el?.dataKey?.label === 'type') {
+ obj.deviceType = el?.data[0][1];
+ }
+ });
+ return obj;
+ })
+ );
+}
+
+export function parseData(input: any[]): any[] {
+ return _(input).groupBy(el => el?.datasource?.aliasName).values().value().map((alliasArray, i) => {
+ const obj = {
+ aliasName: alliasArray[0]?.datasource?.aliasName,
+ entityName: alliasArray[0]?.datasource?.entityName,
+ $datasource: alliasArray[0]?.datasource,
+ dsIndex: i,
+ deviceType: null
+ };
+ alliasArray.forEach(el => {
+ obj[el?.dataKey?.label] = el?.data[0][1];
+ obj[el?.dataKey?.label + '|ts'] = el?.data[0][0];
+ if (el?.dataKey?.label === 'type') {
+ obj.deviceType = el?.data[0][1];
+ }
+ });
+ return obj;
+ });
+}
+
+export function safeExecute(func: Function, params = []) {
+ let res = null;
+ if (func && typeof (func) === 'function') {
+ try {
+ res = func(...params);
+ }
+ catch (err) {
+ console.log('error in external function:', err);
+ res = null;
+ }
+ }
+ return res;
+}
+
+export function parseFunction(source: any, params: string[] = []): Function {
+ let res = null;
+ if (source?.length) {
+ try {
+ res = new Function(...params, source);
+ }
+ catch (err) {
+ console.error(err);
+ res = null;
+ }
+ }
+ return res;
+}
+
+export function parseTemplate(template: string, data: object) {
+ let res = '';
+ try {
+ let variables = '';
+ const expressions = template
+ .match(/\{(.*?)\}/g) // find expressions
+ .map(exp => exp.replace(/{|}/g, '')) // remove brackets
+ .map(exp => exp.split(':'))
+ .map(arr => {
+ variables += `let ${arr[0]} = ''; `;
+ return arr;
+ })
+ .filter(arr => !!arr[1]) // filter expressions without format
+ .reduce((res, current) => {
+ res[current[0]] = current[1];
+ return res;
+ }, {});
+
+ for (const key in data) {
+ if (!key.includes('|'))
+ variables += `${key} = '${expressions[key] ? padValue(data[key], +expressions[key]) : data[key]}';`;
+ }
+ template = template.replace(/:\d+}/g, '}');
+ res = safeExecute(parseFunction(variables + 'return' + '`' + template + '`'));
+ }
+ catch (ex) {
+ }
+ return res;
+}
+
+export function padValue(val: any, dec: number): string {
+ let strVal;
+ let n;
+
+ val = parseFloat(val);
+ n = (val < 0);
+ val = Math.abs(val);
+
+ if (dec > 0) {
+ strVal = val.toFixed(dec).toString()
+ } else {
+ strVal = Math.round(val).toString();
+ }
+ strVal = (n ? '-' : '') + strVal;
+ return strVal;
+}
\ No newline at end of file
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/canvas-digital-gauge.ts b/ui-ngx/src/app/modules/home/components/widget/lib/canvas-digital-gauge.ts
index 5712f31e52..507d84fcc4 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/canvas-digital-gauge.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/canvas-digital-gauge.ts
@@ -18,7 +18,7 @@ import * as CanvasGauges from 'canvas-gauges';
import { FontStyle, FontWeight } from '@home/components/widget/lib/settings.models';
import * as tinycolor_ from 'tinycolor2';
import { ColorFormats } from 'tinycolor2';
-import { isDefined, isString, isUndefined } from '@core/utils';
+import { isDefined, isString, isUndefined, padValue } from '@core/utils';
import GenericOptions = CanvasGauges.GenericOptions;
import BaseGauge = CanvasGauges.BaseGauge;
@@ -786,24 +786,6 @@ function drawDigitalMinMax(context: DigitalGaugeCanvasRenderingContext2D, option
drawText(context, options, 'MinMax', options.maxValue+'', maxX, maxY);
}
-function padValue(val: any, options: CanvasDigitalGaugeOptions): string {
- const dec = options.valueDec;
- let strVal;
- let n;
-
- val = parseFloat(val);
- n = (val < 0);
- val = Math.abs(val);
-
- if (dec > 0) {
- strVal = val.toFixed(dec).toString()
- } else {
- strVal = Math.round(val).toString();
- }
- strVal = (n ? '-' : '') + strVal;
- return strVal;
-}
-
function drawDigitalValue(context: DigitalGaugeCanvasRenderingContext2D, options: CanvasDigitalGaugeOptions, value: any) {
if (options.hideValue) return;
@@ -813,7 +795,7 @@ function drawDigitalValue(context: DigitalGaugeCanvasRenderingContext2D, options
const textX = Math.round(baseX + width / 2);
const textY = valueY;
- let text = options.valueText || padValue(value, options);
+ let text = options.valueText || padValue(value, options.valueDec);
text += options.symbol;
context.save();
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts
new file mode 100644
index 0000000000..9c15a378fd
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts
@@ -0,0 +1,310 @@
+///
+/// Copyright © 2016-2020 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+import L from 'leaflet';
+
+import 'leaflet-providers';
+import 'leaflet.markercluster/dist/MarkerCluster.css'
+import 'leaflet.markercluster/dist/MarkerCluster.Default.css'
+import 'leaflet.markercluster/dist/leaflet.markercluster'
+
+import { MapSettings, MarkerSettings, FormattedData, UnitedMapSettings, PolygonSettings } from './map-models';
+import { Marker } from './markers';
+import { Observable, of, BehaviorSubject, Subject } from 'rxjs';
+import { filter } from 'rxjs/operators';
+import { Polyline } from './polyline';
+import { Polygon } from './polygon';
+
+export default abstract class LeafletMap {
+
+ markers: Map = new Map();
+ dragMode = true;
+ poly: Polyline;
+ polygon: Polygon;
+ map: L.Map;
+ map$: BehaviorSubject = new BehaviorSubject(null);
+ ready$: Observable = this.map$.pipe(filter(map => !!map));
+ options: UnitedMapSettings;
+ isMarketCluster: boolean;
+ bounds: L.LatLngBounds;
+ newMarker: L.Marker;
+ datasources: FormattedData[];
+
+ constructor(public $container: HTMLElement, options: UnitedMapSettings) {
+ this.options = options;
+ }
+
+ public initSettings(options: MapSettings) {
+ const { initCallback,
+ disableScrollZooming, }: MapSettings = options;
+ if (disableScrollZooming) {
+ this.map.scrollWheelZoom.disable();
+ }
+ if (initCallback) {
+ setTimeout(options.initCallback, 0);
+ }
+ }
+
+ addMarkerControl() {
+ if (this.options.draggableMarker) {
+ let mousePositionOnMap: L.LatLng;
+ let addMarker: L.Control;
+ this.map.on('mouseup', (e: L.LeafletMouseEvent) => {
+ mousePositionOnMap = e.latlng
+ })
+ const dragListener = (e: L.DragEndEvent) => {
+ if (e.type === 'dragend' && mousePositionOnMap) {
+ const newMarker = L.marker(mousePositionOnMap).addTo(this.map);
+ const datasourcesList = document.createElement('div');
+ const customLatLng = this.convertToCustomFormat(mousePositionOnMap);
+ this.datasources.forEach(ds => {
+ const dsItem = document.createElement('p');
+ dsItem.appendChild(document.createTextNode(ds.entityName));
+ dsItem.setAttribute('style', 'font-size: 14px');
+ dsItem.onclick = () => {
+ const updatedEnttity = { ...ds, ...customLatLng };
+ this.saveMarkerLocation(updatedEnttity);
+ this.map.removeLayer(newMarker);
+ this.deleteMarker(ds.aliasName);
+ this.createMarker(ds.aliasName, updatedEnttity, this.datasources, this.options, false);
+ }
+ datasourcesList.append(dsItem);
+ })
+ const popup = L.popup();
+ popup.setContent(datasourcesList);
+ newMarker.bindPopup(popup).openPopup();
+
+ }
+ addMarker.setPosition('topright')
+ }
+ L.Control.AddMarker = L.Control.extend({
+ onAdd(map) {
+ const img = L.DomUtil.create('img') as any;
+ img.src = `assets/add_location.svg`;
+ img.style.width = '32px';
+ img.style.height = '32px';
+ img.onclick = this.dragMarker;
+ img.draggable = true;
+ const draggableImg = new L.Draggable(img);
+ draggableImg.enable();
+ draggableImg.on('dragend', dragListener)
+ return img;
+ },
+ onRemove(map) {
+ },
+ dragMarker: this.dragMarker
+
+ } as any);
+
+ L.control.addMarker = (opts) => {
+ return new L.Control.AddMarker(opts);
+ }
+
+ addMarker = L.control.addMarker({ position: 'topright' }).addTo(this.map);
+ }
+ }
+
+ public setMap(map: L.Map) {
+ this.map = map;
+ if (this.options.useDefaultCenterPosition) {
+ this.map.panTo(this.options.defaultCenterPosition);
+ this.bounds = map.getBounds();
+ }
+ else this.bounds = new L.LatLngBounds(null, null);
+ if (this.options.draggableMarker) {
+ this.addMarkerControl();
+ }
+ this.map$.next(this.map);
+ }
+
+ public setDataSources(dataSources) {
+ this.datasources = dataSources;
+ }
+
+ public saveMarkerLocation(e) {
+
+ }
+
+ createLatLng(lat: number, lng: number): L.LatLng {
+ return L.latLng(lat, lng);
+ }
+
+ createBounds(): L.LatLngBounds {
+ return this.map.getBounds();
+ }
+
+ extendBounds(bounds: L.LatLngBounds, polyline: L.Polyline) {
+ if (polyline && polyline.getLatLngs() && polyline.getBounds()) {
+ bounds.extend(polyline.getBounds());
+ }
+ }
+
+ invalidateSize() {
+ this.map?.invalidateSize(true);
+ }
+
+ onResize() {
+
+ }
+
+ getCenter() {
+ return this.map.getCenter();
+ }
+
+ convertPosition(expression: object): L.LatLng {
+ const lat = expression[this.options.latKeyName];
+ const lng = expression[this.options.lngKeyName];
+ if (isNaN(lat) || isNaN(lng))
+ return null;
+ else
+ return L.latLng(lat, lng) as L.LatLng;
+ }
+
+ convertToCustomFormat(position: L.LatLng): object {
+ return {
+ [this.options.latKeyName]: position.lat,
+ [this.options.lngKeyName]: position.lng
+ }
+ }
+
+ // Markers
+ updateMarkers(markersData) {
+ markersData.forEach(data => {
+ if (this.convertPosition(data)) {
+ if (data.rotationAngle) {
+ this.options.icon = L.divIcon({
+ html: ``
+ })
+ }
+ else {
+ this.options.icon = null;
+ }
+ if (this.markers.get(data.aliasName)) {
+ this.updateMarker(data.aliasName, data, markersData, this.options)
+ }
+ else {
+ this.createMarker(data.aliasName, data, markersData, this.options as MarkerSettings);
+ }
+ }
+ });
+ }
+
+ dragMarker = (e, data?) => {
+ if (e.type !== 'dragend') return;
+ this.saveMarkerLocation({ ...data, ...this.convertToCustomFormat(e.target._latlng) });
+ }
+
+ private createMarker(key, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings, setFocus = true) {
+ this.ready$.subscribe(() => {
+ const newMarker = new Marker(this.map, this.convertPosition(data), settings, data, dataSources, () => { }, this.dragMarker);
+ if (setFocus)
+ this.map.fitBounds(this.bounds.extend(newMarker.leafletMarker.getLatLng()));
+ this.markers.set(key, newMarker);
+ });
+ }
+
+ private updateMarker(key, data, dataSources, settings: MarkerSettings) {
+ const marker: Marker = this.markers.get(key);
+ const location = this.convertPosition(data)
+ if (!location.equals(marker.location)) {
+ marker.updateMarkerPosition(location);
+ }
+ if (settings.showTooltip) {
+ marker.updateMarkerTooltip(data);
+ }
+ marker.setDataSources(data, dataSources);
+ marker.updateMarkerIcon(settings);
+ }
+
+ deleteMarker(key) {
+ let marker = this.markers.get(key)?.leafletMarker;
+ if (marker) {
+ this.map.removeLayer(marker);
+ this.markers.delete(key);
+ marker = null;
+ }
+ }
+
+ // Polyline
+
+ updatePolylines(polyData: Array
>) {
+ polyData.forEach(data => {
+ if (data.length) {
+ const dataSource = polyData.map(arr => arr[0]);
+ if (this.poly) {
+ this.updatePolyline(data, dataSource, this.options);
+ }
+ else {
+ this.createPolyline(data, dataSource, this.options);
+ }
+ }
+ })
+ }
+
+ createPolyline(data: any[], dataSources, settings) {
+ if (data.length)
+ this.ready$.subscribe(() => {
+ this.poly = new Polyline(this.map,
+ data.map(el => this.convertPosition(el)).filter(el => !!el), data, dataSources, settings);
+ const bounds = this.bounds.extend(this.poly.leafletPoly.getBounds());
+ if (bounds.isValid()) {
+ this.map.fitBounds(bounds);
+ this.bounds = bounds;
+ }
+ });
+ }
+
+ updatePolyline(data, dataSources, settings) {
+ this.ready$.subscribe(() => {
+ this.poly.updatePolyline(settings, data, dataSources);
+ });
+ }
+
+ // Polygon
+
+ updatePolygons(polyData: any[]) {
+ polyData.forEach((data: any) => {
+ if (data.data.length && data.dataKey.name === this.options.polygonKeyName) {
+ if (typeof (data?.data[0][1]) === 'string') {
+ data.data = JSON.parse(data.data[0][1]);
+ }
+ if (this.polygon) {
+ this.updatePolygon(data.data, polyData, this.options);
+ }
+ else {
+ this.createPolygon(data.data, polyData, this.options);
+ }
+ }
+ });
+ }
+
+ createPolygon(data: FormattedData, dataSources: FormattedData[], settings: PolygonSettings) {
+ this.ready$.subscribe(() => {
+ this.polygon = new Polygon(this.map, data, dataSources, settings);
+ const bounds = this.bounds.extend(this.polygon.leafletPoly.getBounds());
+ if (bounds.isValid()) {
+ this.map.fitBounds(bounds);
+ this.bounds = bounds;
+ }
+ });
+ }
+
+ updatePolygon(data, dataSources, settings) {
+ this.ready$.subscribe(() => {
+ // this.polygon.updatePolygon(settings, data, dataSources);
+ });
+ }
+}
\ No newline at end of file
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts
new file mode 100644
index 0000000000..92b6c094d9
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts
@@ -0,0 +1,133 @@
+///
+/// Copyright © 2016-2020 The Thingsboard Authors
+///
+/// Licensed under the Apache License; Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing; software
+/// distributed under the License is distributed on an "AS IS" BASIS;
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND; either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+import { LatLngTuple } from 'leaflet';
+
+export type GenericFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string;
+export type MarkerImageFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string;
+
+export type MapSettings = {
+ polygonKeyName: any;
+ draggableMarker: boolean;
+ initCallback?: () => any;
+ defaultZoomLevel?: number;
+ dontFitMapBounds?: boolean;
+ disableScrollZooming?: boolean;
+ minZoomLevel?: number;
+ latKeyName?: string;
+ lngKeyName?: string;
+ xPosKeyName?: string;
+ yPosKeyName?: string;
+ mapProvider: MapProviders;
+ mapUrl?: string;
+ mapImageUrl?: string;
+ provider?: MapProviders;
+ credentials?: any; // declare credentials format
+ defaultCenterPosition?: LatLngTuple;
+ markerClusteringSetting?;
+ useDefaultCenterPosition?: boolean;
+ gmDefaultMapType?: string;
+ useLabelFunction: string;
+ icon?: any;
+}
+
+export enum MapProviders {
+ google = 'google-map',
+ openstreet = 'openstreet-map',
+ here = 'here',
+ image = 'image-map',
+ tencent = 'tencent-map'
+}
+
+export type MarkerSettings = {
+ tooltipPattern?: any;
+ icon?: any;
+ showLabel?: boolean;
+ label: string;
+ labelColor: string;
+ labelText: string;
+ useLabelFunction: boolean;
+ draggableMarker: boolean;
+ showTooltip?: boolean;
+ color?: string;
+ autocloseTooltip: boolean;
+ displayTooltipAction: string;
+ currentImage?: string;
+ useMarkerImageFunction?: boolean;
+ markerImages?: string[];
+ useMarkerImage: boolean;
+ markerImageSize: number;
+ markerImage: {
+ length: number
+ }
+
+ colorFunction: GenericFunction;
+ tooltipFunction: GenericFunction;
+ labelFunction: GenericFunction;
+ markerImageFunction?: MarkerImageFunction;
+}
+
+export interface FormattedData {
+ aliasName: string;
+ entityName: string;
+ $datasource: string;
+ dsIndex: number;
+ deviceType: string
+}
+
+export type PolygonSettings = {
+ showPolygon: boolean;
+ showTooltip: any;
+ polygonStrokeOpacity: number;
+ polygonOpacity: number;
+ polygonStrokeWeight: number;
+ polygonStrokeColor: string;
+ polygonColor: string;
+ autocloseTooltip: boolean;
+ displayTooltipAction: string;
+
+ polygonColorFunction?: GenericFunction;
+}
+
+export type PolylineSettings = {
+ usePolylineDecorator: any;
+ autocloseTooltip: boolean;
+ displayTooltipAction: string;
+ useColorFunction: any;
+ color: string;
+ useStrokeOpacityFunction: any;
+ strokeOpacity: number;
+ useStrokeWeightFunction: any;
+ strokeWeight: number;
+ decoratorOffset: string | number;
+ endDecoratorOffset: string | number;
+ decoratorRepeat: string | number;
+ decoratorSymbol: any;
+ decoratorSymbolSize: any;
+ useDecoratorCustomColor: any;
+ decoratorCustomColor: any;
+
+
+ colorFunction: GenericFunction;
+ strokeOpacityFunction: GenericFunction;
+ strokeWeightFunction: GenericFunction;
+}
+
+export interface HistorySelectSettings {
+ buttonColor: string;
+}
+
+export type UnitedMapSettings = MapSettings & PolygonSettings & MarkerSettings & PolygonSettings;
\ No newline at end of file
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget.interface.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget.interface.ts
new file mode 100644
index 0000000000..1547ea16c9
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget.interface.ts
@@ -0,0 +1,31 @@
+///
+/// Copyright © 2016-2020 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+import { JsonSettingsSchema } from '@app/shared/public-api';
+
+export interface MapWidgetInterface {
+ resize(),
+ update(),
+ onInit(),
+ onDestroy();
+}
+
+export interface MapWidgetStaticInterface {
+ settingsSchema(mapProvider?, drawRoutes?): JsonSettingsSchema;
+ getProvidersSchema():JsonSettingsSchema
+ dataKeySettingsSchema(): object;
+ actionSources(): object;
+}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts
new file mode 100644
index 0000000000..049a985353
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts
@@ -0,0 +1,258 @@
+///
+/// Copyright © 2016-2020 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+import { MapProviders, UnitedMapSettings } from './map-models';
+import LeafletMap from './leaflet-map';
+import {
+ openstreetMapSettingsSchema,
+ googleMapSettingsSchema,
+ imageMapSettingsSchema,
+ tencentMapSettingsSchema,
+ commonMapSettingsSchema,
+ routeMapSettingsSchema,
+ markerClusteringSettingsSchema,
+ markerClusteringSettingsSchemaLeaflet,
+ hereMapSettingsSchema,
+ mapProviderSchema
+} from './schemes';
+import { MapWidgetStaticInterface, MapWidgetInterface } from './map-widget.interface';
+import { OpenStreetMap, TencentMap, GoogleMap, HEREMap, ImageMap } from './providers';
+import { parseFunction, parseArray, parseData } from '@app/core/utils';
+import { initSchema, addToSchema, mergeSchemes, addCondition, addGroupInfo } from '@app/core/schema-utils';
+import { AttributeScope, EntityId, JsonSettingsSchema } from '@app/shared/public-api';
+import { forkJoin } from 'rxjs';
+import { WidgetContext } from '@app/modules/home/models/widget-component.models';
+import { AttributeService } from '@app/core/public-api';
+import { getDefCenterPosition } from './maps-utils';
+
+let providerSets;
+let defaultSettings;
+
+export class MapWidgetController implements MapWidgetInterface {
+
+ constructor(public mapProvider: MapProviders, private drawRoutes: boolean, public ctx: WidgetContext, $element: HTMLElement) {
+ if (this.map) {
+ this.map.map.remove();
+ delete this.map;
+ }
+
+ this.data = ctx.data;
+ if (!$element) {
+ $element = ctx.$container[0];
+ }
+ this.settings = this.initSettings(ctx.settings);
+
+ const MapClass = providerSets[this.provider]?.MapClass;
+ if (!MapClass) {
+ return;
+ }
+ this.map = new MapClass($element, this.settings);
+ this.map.saveMarkerLocation = this.setMarkerLocation;
+ }
+
+ map: LeafletMap;
+ provider: MapProviders;
+ schema: JsonSettingsSchema;
+ data;
+ settings: UnitedMapSettings;
+
+ public static dataKeySettingsSchema(): object {
+ return {};
+ }
+
+ public static getProvidersSchema() {
+ return mergeSchemes([mapProviderSchema,
+ ...Object.values(providerSets)?.map(
+ (setting: IProvider) => addCondition(setting?.schema, `model.provider === '${setting.name}'`))]);
+ }
+
+ public static settingsSchema(mapProvider: MapProviders, drawRoutes: boolean): JsonSettingsSchema {
+ const schema = initSchema();
+ addToSchema(schema, this.getProvidersSchema());
+ addGroupInfo(schema, 'Map Provider Settings');
+ addToSchema(schema, commonMapSettingsSchema);
+ addGroupInfo(schema, 'Common Map Settings');
+
+ if (drawRoutes) {
+ addToSchema(schema, routeMapSettingsSchema);
+ addGroupInfo(schema, 'Route Map Settings');
+ } else if (mapProvider !== 'image-map') {
+ const clusteringSchema = mergeSchemes([markerClusteringSettingsSchemaLeaflet, markerClusteringSettingsSchema])
+ addToSchema(schema, clusteringSchema);
+ addGroupInfo(schema, 'Markers Clustering Settings');
+ }
+ return schema;
+ }
+
+ public static actionSources(): object {
+ return {
+ markerClick: {
+ name: 'widget-action.marker-click',
+ multiple: false
+ },
+ polygonClick: {
+ name: 'widget-action.polygon-click',
+ multiple: false
+ },
+ tooltipAction: {
+ name: 'widget-action.tooltip-tag-action',
+ multiple: true
+ }
+ };
+ }
+
+ onInit() {
+ }
+
+ setMarkerLocation = (e) => {
+ const attributeService = this.ctx.$injector.get(AttributeService);
+ forkJoin(
+ this.data.filter(data => !!e[data.dataKey.name])
+ .map(data => {
+ const entityId: EntityId = {
+ entityType: data.datasource.entityType,
+ id: data.datasource.entityId
+ };
+ return attributeService.saveEntityAttributes(
+ entityId,
+ AttributeScope.SHARED_SCOPE,
+ [{
+ key: data.dataKey.name,
+ value: e[data.dataKey.name]
+ }]
+ );
+ })).subscribe(res => {
+ console.log('MapWidgetController -> setMarkerLocation -> res', res)
+ });
+ }
+
+ initSettings(settings: UnitedMapSettings) {
+ const functionParams = ['data', 'dsData', 'dsIndex'];
+ this.provider = settings.provider || this.mapProvider;
+ const customOptions = {
+ provider: this.provider,
+ mapUrl: settings?.mapImageUrl,
+ labelFunction: parseFunction(settings.labelFunction, functionParams),
+ tooltipFunction: parseFunction(settings.tooltipFunction, functionParams),
+ colorFunction: parseFunction(settings.colorFunction, functionParams),
+ polygonColorFunction: parseFunction(settings.polygonColorFunction, functionParams),
+ markerImageFunction: parseFunction(settings.markerImageFunction, ['data', 'images', 'dsData', 'dsIndex']),
+ labelColor: this.ctx.widgetConfig.color,
+ tooltipPattern: settings.tooltipPattern ||
+ '${entityName}
Latitude: ${' +
+ settings.latKeyName + ':7}
Longitude: ${' + settings.lngKeyName + ':7}',
+ defaultCenterPosition: getDefCenterPosition(settings?.defaultCenterPosition),
+ currentImage: (settings.useMarkerImage && settings.markerImage?.length) ? {
+ url: settings.markerImage,
+ size: settings.markerImageSize || 34
+ } : null
+ }
+ return { ...defaultSettings, ...settings, ...customOptions, }
+ }
+
+ update() {
+ if (this.drawRoutes)
+ this.map.updatePolylines(parseArray(this.data));
+ if (this.settings.showPolygon) {
+ this.map.updatePolygons(this.data);
+ }
+ if (this.settings.draggableMarker) {
+ this.map.setDataSources(parseData(this.data));
+ }
+ else
+ this.map.updateMarkers(parseData(this.data));
+ }
+
+ resize() {
+ this.map?.invalidateSize();
+ this.map.onResize();
+ }
+
+ onDestroy() {
+ }
+}
+
+export let TbMapWidgetV2: MapWidgetStaticInterface = MapWidgetController;
+
+interface IProvider {
+ MapClass: LeafletMap,
+ schema: JsonSettingsSchema,
+ name: string
+}
+
+providerSets = {
+ 'openstreet-map': {
+ MapClass: OpenStreetMap,
+ schema: openstreetMapSettingsSchema,
+ name: 'openstreet-map',
+ },
+ 'tencent-map': {
+ MapClass: TencentMap,
+ schema: tencentMapSettingsSchema,
+ name: 'tencent-map'
+ },
+ 'google-map': {
+ MapClass: GoogleMap,
+ schema: googleMapSettingsSchema,
+ name: 'google-map'
+ },
+ here: {
+ MapClass: HEREMap,
+ schema: hereMapSettingsSchema,
+ name: 'here'
+ },
+ 'image-map': {
+ MapClass: ImageMap,
+ schema: imageMapSettingsSchema,
+ name: 'image-map'
+ }
+}
+
+defaultSettings = {
+ xPosKeyName: 'xPos',
+ yPosKeyName: 'yPos',
+ markerOffsetX: 0.5,
+ markerOffsetY: 1,
+ latKeyName: 'latitude',
+ lngKeyName: 'longitude',
+ polygonKeyName: 'coordinates',
+ showLabel: false,
+ label: '${entityName}',
+ showTooltip: false,
+ useDefaultCenterPosition: false,
+ showTooltipAction: 'click',
+ autocloseTooltip: false,
+ showPolygon: true,
+ labelColor: '#000000',
+ color: '#FE7569',
+ polygonColor: '#0000ff',
+ polygonStrokeColor: '#fe0001',
+ polygonOpacity: 0.5,
+ polygonStrokeOpacity: 1,
+ polygonStrokeWeight: 1,
+ useLabelFunction: false,
+ markerImages: [],
+ strokeWeight: 2,
+ strokeOpacity: 1.0,
+ initCallback: () => { },
+ defaultZoomLevel: 8,
+ dontFitMapBounds: false,
+ disableScrollZooming: false,
+ minZoomLevel: 16,
+ credentials: '',
+ markerClusteringSetting: null,
+ draggableMarker: false
+}
\ No newline at end of file
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts
new file mode 100644
index 0000000000..7af0828891
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts
@@ -0,0 +1,55 @@
+///
+/// Copyright © 2016-2020 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+import L from 'leaflet';
+import _ from 'lodash';
+import { MarkerSettings, PolylineSettings, PolygonSettings } from './map-models';
+
+export function createTooltip(target: L.Layer, settings: MarkerSettings | PolylineSettings | PolygonSettings): L.Popup {
+ const popup = L.popup();
+ popup.setContent('');
+ target.bindPopup(popup, { autoClose: settings.autocloseTooltip, closeOnClick: false });
+ if (settings.displayTooltipAction === 'hover') {
+ target.off('click');
+ target.on('mouseover', function () {
+ this.openPopup();
+ });
+ target.on('mouseout', function () {
+ this.closePopup();
+ });
+ }
+ return popup;
+}
+
+export function getRatio(firsMoment: number, secondMoment: number, intermediateMoment: number): number {
+ return (intermediateMoment - firsMoment) / (secondMoment - firsMoment);
+};
+
+export function findAngle(startPoint, endPoint) {
+ let angle = -Math.atan2(endPoint.latitude - startPoint.latitude, endPoint.longitude - startPoint.longitude);
+ angle = angle * 180 / Math.PI;
+ return parseInt(angle.toFixed(2), 10);
+}
+
+
+export function getDefCenterPosition(position) {
+ if (typeof (position) === 'string')
+ return position.split(',');
+ if (typeof (position) === 'object')
+ return position;
+ return [0, 0];
+}
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.scss b/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.scss
new file mode 100644
index 0000000000..a017edeb9f
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.scss
@@ -0,0 +1,31 @@
+/**
+ * Copyright © 2016-2020 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+.arrow {
+ height: 30px;
+ width: 30px;
+ background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC0AAAAtCAMAAAANxBKoAAACAVBMVEUAAAAAqv9AgP8ggP8udP8qgP8ndv8kbf8kgP8eeP8rcf8mc/8jdP8hev8me/8kdv8jcv8pcP8ic/8neP8leP8ofP8mef8pev8nef8oe/8nef8pef8qfv8qgf8qe/8mev8off8oev8oef8oe/8pev8qe/8oe/8off8pfP8oev8pev8qe/8qfP8pev8pe/8qfP8pe/8oev8pe/8qe/8qfP8qfP8qff8qff8qfP8qff8pe/8qff8qfP8qfP8pev8pev8JYf8KYP8UY/8UZf8WY/8WZv8YZP8bZP8fZv8fZ/8gZv8gZ/8hZP8iZ/8jaf8jav8kaP8kav8ka/8kbP8kbf8kb/8lbP8lbf8lbv8lb/8lcP8mb/8mcP8mcf8ncf8ncv8nc/8ndP8ndf8ocv4odP8odf8odv8od/8oeP8peP8pef8pev8qev8qe/8qfP8rbP8rfP8rff8tc/4vcP4xbv4zcf42dP49df4+gP4/fv5Rif5biP1ul/14mvx4nvyMrfuMrvuWsfuctvuguPuww/vD0fnH1vrI1frK2PrL2fnV3vnb4vnc5fnd5fnd5vne5fnj6vnl6/jm7Pno7fXo7fbo7ffo7fjp7fnp7vjq7vjs8fnt8Pjt8fjw8fXw8fbw8fjy8/jz9fn5+fj7+/f++/j/+/X/+/b/+/f/+/j//Pj//vhoUaHMAAAAQHRSTlMAAwQICwwNDg4REhQWFxscHTI1QkRGSktOU1RdYWFscXJzhYWKqre6wMHDw8PHx8rN09TW1uLi7e/x8vLz9fj5hfwW0QAAAnVJREFUSMeV1fdb00AYB/BzoQjurSjuvQcuFLcgWqTVtLVpL0GS2GuDuLeouPeAiLOuwOFf6WU0ucvq4/d52v6Qz/M+18u97wHgyfh5m5vUYlHt2rV8BojOqGUqonNhVgTegfxZE2JXoMAUFgXYsSoKy3YfnoIiUqxm8UwUnSoaT0WVUuPi6lCUz+ft/+pqNYwqsiwrlm8s45XBVpE7BQgFyeZLbB1ihVwmefjpDSjbi7HwziArCdlU/MArjDHfaestBh4dZs9/JBb/4kVbo5FEr/JaWRL4VPvZ39jMVV4q63qiCx7bIfDJtst/LYt/JnLldaNuACbS9JwiQj4Zu/3HlDr5nEyJivMcgPn0Nogwfaq1Z9igQ7pO9AuqNEJzQINTV5ZghjvywLFG6dYMVRptAF3uPvBc8xPsWH0Q44ccpEqjvaBoa0lI7/ts2kHdCsbDJ5jSSC2/SEU8856x+hDGd+JZujQquvp6ieT7Nyel0o/jCSgzTefq0y81I30f7PRr2i1fbWrd+98yXtMGfOt29wTyiebHpu83dZ+m3U969qSBeje5DHeod8Dx5PeYZ7/rPO8y0XLvk+1J8UcJyLzLScw5kTsgz8Xu2vXJV0uaOSfsGcznZZGcwdi1L5qV13HmDILV3tY1z3fbxa8Wv+Se74VEj/H3jmz0TvuVN4Z+x/ZORF8efU64s/CtUT0vCbk0d/DZTSgxPQ/WhswTSYTZrDNPllaaVQo1q/Y4k63mv+YgmFZxxk6gR/LsCngcO+6nR9lCrfcqqQ2/dxqD7rT1IXhxyB24O8BuCr9eq9axU7F7wYjoy3ty3TZVLaCC2rRxru/hPwVgtEdFK5vOAAAAAElFTkSuQmCC");
+ background-size: cover;
+ background-repeat: no-repeat;
+ background-position: center center;
+}
+
+.leaflet-div-icon,
+.tb-marker-label,
+.tb-marker-label:before {
+ border: none;
+ background: none;
+ box-shadow: none;
+}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts
new file mode 100644
index 0000000000..ca5400d4f6
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts
@@ -0,0 +1,184 @@
+///
+/// Copyright © 2016-2020 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+import L from 'leaflet';
+import { MarkerSettings, FormattedData } from './map-models';
+import { aspectCache, safeExecute, parseTemplate } from '@app/core/utils';
+import { createTooltip } from './maps-utils';
+
+export class Marker {
+
+ leafletMarker: L.Marker;
+ tooltipOffset: [number, number];
+ tooltip: L.Popup;
+ location: L.LatLngExpression;
+ data: FormattedData;
+ dataSources: FormattedData[];
+
+ constructor(private map: L.Map, location: L.LatLngExpression, public settings: MarkerSettings,
+ data?, dataSources?, onClickListener?, onDragendListener?) {
+ this.setDataSources(data, dataSources);
+ this.leafletMarker = L.marker(location, {
+ draggable: settings.draggableMarker
+ });
+
+ this.createMarkerIcon((iconInfo) => {
+ this.leafletMarker.setIcon(iconInfo.icon);
+ this.tooltipOffset = [0, -iconInfo.size[1] + 10];
+ this.updateMarkerLabel(settings);
+ this.leafletMarker.addTo(map);
+ });
+
+ if (settings.showTooltip) {
+ this.tooltip = createTooltip(this.leafletMarker, settings);
+ this.tooltip.setContent(parseTemplate(this.settings.tooltipPattern, data));
+ }
+
+ if (onClickListener) {
+ this.leafletMarker.on('click', onClickListener);
+ }
+
+ if (onDragendListener) {
+ this.leafletMarker.on('dragend', (e) => onDragendListener(e, this.data));
+ }
+ }
+
+ setDataSources(data: FormattedData, dataSources: FormattedData[]) {
+ this.data = data;
+ this.dataSources = dataSources;
+ }
+
+ updateMarkerTooltip(data: FormattedData) {
+ this.tooltip.setContent(parseTemplate(this.settings.tooltipPattern, data));
+ }
+
+ updateMarkerPosition(position: L.LatLngExpression) {
+ this.leafletMarker.setLatLng(position);
+ }
+
+ updateMarkerLabel(settings: MarkerSettings) {
+ this.leafletMarker.unbindTooltip();
+
+ if (settings.showLabel) {
+ if (settings.useLabelFunction) {
+ settings.labelText = safeExecute(settings.labelFunction, [this.data, this.dataSources, this.data.dsIndex])
+ }
+ else settings.labelText = parseTemplate(settings.label, this.data);
+ this.leafletMarker.bindTooltip(`${settings.labelText}
`,
+ { className: 'tb-marker-label', permanent: true, direction: 'top', offset: this.tooltipOffset });
+ }
+ }
+
+ updateMarkerColor(color) {
+ this.createDefaultMarkerIcon(color, (iconInfo) => {
+ this.leafletMarker.setIcon(iconInfo.icon);
+ });
+ }
+
+ updateMarkerIcon(settings: MarkerSettings) {
+ this.createMarkerIcon((iconInfo) => {
+ this.leafletMarker.setIcon(iconInfo.icon);
+ this.tooltipOffset = [0, -iconInfo.size[1] + 10];
+ this.updateMarkerLabel(settings);
+ });
+ }
+
+ createMarkerIcon(onMarkerIconReady) {
+
+ if (this.settings.icon) {
+ onMarkerIconReady({
+ size: [30, 30],
+ icon: this.settings.icon,
+ });
+ return;
+ }
+
+ const currentImage = this.settings.useMarkerImageFunction ?
+ safeExecute(this.settings.markerImageFunction,
+ [this.data, this.settings.markerImages, this.dataSources, this.data.dsIndex]) : this.settings.currentImage;
+
+
+ if (currentImage && currentImage.url) {
+ aspectCache(currentImage.url).subscribe(
+ (aspect) => {
+ if (aspect) {
+ let width;
+ let height;
+ if (aspect > 1) {
+ width = currentImage.size;
+ height = currentImage.size / aspect;
+ } else {
+ width = currentImage.size * aspect;
+ height = currentImage.size;
+ }
+ const icon = L.icon({
+ iconUrl: currentImage.url,
+ iconSize: [width, height],
+ iconAnchor: [width / 2, height],
+ popupAnchor: [0, -height]
+ });
+ const iconInfo = {
+ size: [width, height],
+ icon
+ };
+ onMarkerIconReady(iconInfo);
+ } else {
+ this.createDefaultMarkerIcon(this.settings.color, onMarkerIconReady);
+ }
+ }
+ );
+ } else {
+ this.createDefaultMarkerIcon(this.settings.color, onMarkerIconReady);
+ }
+ }
+
+ createDefaultMarkerIcon(color, onMarkerIconReady) {
+ const pinColor = color.substr(1);
+ const icon = L.icon({
+ iconUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|' + pinColor,
+ iconSize: [21, 34],
+ iconAnchor: [10, 34],
+ popupAnchor: [0, -34],
+ shadowUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_shadow',
+ shadowSize: [40, 37],
+ shadowAnchor: [12, 35]
+ });
+ const iconInfo = {
+ size: [21, 34],
+ icon
+ };
+ onMarkerIconReady(iconInfo);
+ }
+
+
+
+ removeMarker() {
+ /* this.map$.subscribe(map =>
+ this.leafletMarker.addTo(map))*/
+ }
+
+ extendBoundsWithMarker(bounds) {
+ bounds.extend(this.leafletMarker.getLatLng());
+ }
+
+ getMarkerPosition() {
+ return this.leafletMarker.getLatLng();
+ }
+
+ setMarkerPosition(latLng) {
+ this.leafletMarker.setLatLng(latLng);
+ }
+}
\ No newline at end of file
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts
new file mode 100644
index 0000000000..e38eaae1c6
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts
@@ -0,0 +1,70 @@
+///
+/// Copyright © 2016-2020 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+import L, { LatLngExpression } from 'leaflet';
+import { createTooltip } from './maps-utils';
+import { PolygonSettings } from './map-models';
+
+export class Polygon {
+
+ leafletPoly: L.Polygon;
+ tooltip;
+
+ constructor(public map, coordinates, dataSources, settings: PolygonSettings, onClickListener?) {
+ this.leafletPoly = L.polygon(coordinates, {
+ fill: true,
+ fillColor: settings.polygonColor,
+ color: settings.polygonStrokeColor,
+ weight: settings.polygonStrokeWeight,
+ fillOpacity: settings.polygonOpacity,
+ opacity: settings.polygonStrokeOpacity
+ }).addTo(this.map);
+
+ if (settings.showTooltip) {
+ this.tooltip = createTooltip(this.leafletPoly, settings);
+ }
+ if (onClickListener) {
+ this.leafletPoly.on('click', onClickListener);
+ }
+ }
+
+ removePolygon() {
+ this.map.removeLayer(this.leafletPoly);
+ }
+
+ updatePolygonColor(settings) {
+ console.log('Polygon -> updatePolygonColor -> settings', settings)
+ const style: L.PathOptions = {
+
+ fill: true,
+ fillColor: settings.color,
+ color: settings.color,
+ weight: settings.polygonStrokeWeight,
+ fillOpacity: settings.polygonOpacity,
+ opacity: settings.polygonStrokeOpacity
+ };
+ this.leafletPoly.setStyle(style);
+ }
+
+ getPolygonLatLngs() {
+ return this.leafletPoly.getLatLngs();
+ }
+
+ setPolygonLatLngs(latLngs: LatLngExpression[]) {
+ this.leafletPoly.setLatLngs(latLngs);
+ this.leafletPoly.redraw();
+ }
+}
\ No newline at end of file
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/polyline.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/polyline.ts
new file mode 100644
index 0000000000..9feb0ff08e
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/polyline.ts
@@ -0,0 +1,92 @@
+///
+/// Copyright © 2016-2020 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+import L, { PolylineDecoratorOptions } from 'leaflet';
+import 'leaflet-polylinedecorator';
+
+import { safeExecute } from '@app/core/utils';
+import { PolylineSettings } from './map-models';
+
+export class Polyline {
+
+ leafletPoly: L.Polyline;
+ polylineDecorator: L.PolylineDecorator;
+ dataSources;
+ data;
+
+ constructor(private map: L.Map, locations, data, dataSources, settings: PolylineSettings) {
+ this.dataSources = dataSources;
+ this.data = data;
+
+ this.leafletPoly = L.polyline(locations,
+ this.getPolyStyle(settings)
+ ).addTo(this.map);
+ if (settings.usePolylineDecorator) {
+ this.polylineDecorator = L.polylineDecorator(this.leafletPoly, this.getDecoratorSettings(settings)).addTo(this.map);
+ }
+ }
+
+ getDecoratorSettings(settings: PolylineSettings): PolylineDecoratorOptions {
+ return {
+ patterns: [
+ {
+ offset: settings.decoratorOffset,
+ endOffset: settings.endDecoratorOffset,
+ repeat: settings.decoratorRepeat,
+ symbol: L.Symbol[settings.decoratorSymbol]({
+ pixelSize: settings.decoratorSymbolSize,
+ polygon: false,
+ pathOptions: {
+ color: settings.useDecoratorCustomColor ? settings.decoratorCustomColor : this.getPolyStyle(settings).color,
+ stroke: true
+ }
+ })
+ }
+ ],
+ interactive: false,
+ } as PolylineDecoratorOptions
+ }
+
+ updatePolyline(settings, data, dataSources) {
+ this.leafletPoly.setStyle(this.getPolyStyle(settings));
+ // this.setPolylineLatLngs(data);
+ if(this.polylineDecorator)
+ this.polylineDecorator.setPaths(this.leafletPoly);
+ }
+
+ getPolyStyle(settings: PolylineSettings): L.PolylineOptions {
+ return {
+ color: settings.useColorFunction ?
+ safeExecute(settings.colorFunction, [this.data, this.dataSources, this.data[0]?.dsIndex]) : settings.color,
+ opacity: settings.useStrokeOpacityFunction ?
+ safeExecute(settings.strokeOpacityFunction, [this.data, this.dataSources, this.data[0]?.dsIndex]) : settings.strokeOpacity,
+ weight: settings.useStrokeWeightFunction ?
+ safeExecute(settings.strokeWeightFunction, [this.data, this.dataSources, this.data[0]?.dsIndex]) : settings.strokeWeight,
+ }
+ }
+
+ removePolyline() {
+ this.map.removeLayer(this.leafletPoly);
+ }
+
+ getPolylineLatLngs() {
+ return this.leafletPoly.getLatLngs();
+ }
+
+ setPolylineLatLngs(latLngs) {
+ this.leafletPoly.setLatLngs(latLngs);
+ }
+}
\ No newline at end of file
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/google-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/google-map.ts
new file mode 100644
index 0000000000..0b0d269ce1
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/google-map.ts
@@ -0,0 +1,57 @@
+///
+/// Copyright © 2016-2020 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+
+import L from 'leaflet';
+import LeafletMap from '../leaflet-map';
+import { MapSettings, UnitedMapSettings } from '../map-models';
+import 'leaflet.gridlayer.googlemutant';
+
+let googleLoaded = false;
+
+
+export class GoogleMap extends LeafletMap {
+ constructor($container, options: UnitedMapSettings) {
+
+ super($container, options);
+ this.loadGoogle(() => {
+ const map = L.map($container).setView(options?.defaultCenterPosition, options?.defaultZoomLevel);
+ const roads = (L.gridLayer as any).googleMutant({
+ type: options?.gmDefaultMapType || 'roadmap'
+ }).addTo(map);
+ super.setMap(map);
+ }, options.credentials.apiKey);
+ super.initSettings(options);
+ }
+
+ private loadGoogle(callback, apiKey = 'AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q') {
+ if (googleLoaded) {
+ callback()
+ }
+ else {
+ googleLoaded = true;
+ const script = document.createElement('script');
+ script.onload = () => {
+ callback();
+ }
+ script.onerror = () => {
+ googleLoaded = false;
+ }
+ script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}`;
+ document.getElementsByTagName('head')[0].appendChild(script);
+ }
+ }
+}
\ No newline at end of file
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/here-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/here-map.ts
new file mode 100644
index 0000000000..60221b62a3
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/here-map.ts
@@ -0,0 +1,35 @@
+///
+/// Copyright © 2016-2020 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+import L from 'leaflet';
+import LeafletMap from '../leaflet-map';
+import { MapSettings, UnitedMapSettings } from '../map-models';
+
+export class HEREMap extends LeafletMap {
+ constructor($container, options: UnitedMapSettings) {
+ const defaultCredentials =
+ {
+ app_id: 'AhM6TzD9ThyK78CT3ptx',
+ app_code: 'p6NPiITB3Vv0GMUFnkLOOg'
+ }
+ super($container, options);
+ const map = L.map($container).setView(options?.defaultCenterPosition, options?.defaultZoomLevel);
+ const tileLayer = (L.tileLayer as any).provider(options.mapProvider || 'HERE.normalDay', options.credentials || defaultCredentials);
+ tileLayer.addTo(map);
+ super.setMap(map);
+ super.initSettings(options);
+ }
+}
\ No newline at end of file
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts
new file mode 100644
index 0000000000..d90bb42db3
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts
@@ -0,0 +1,133 @@
+///
+/// Copyright © 2016-2020 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+import L from 'leaflet';
+import LeafletMap from '../leaflet-map';
+import { MapSettings, UnitedMapSettings } from '../map-models';
+import { aspectCache } from '@app/core/utils';
+
+const maxZoom = 4;// ?
+
+export class ImageMap extends LeafletMap {
+
+ imageOverlay;
+ aspect = 0;
+ width = 0;
+ height = 0;
+
+ constructor($container: HTMLElement, options: UnitedMapSettings) {
+ super($container, options);
+ aspectCache(options.mapUrl).subscribe(aspect => {
+ this.aspect = aspect;
+ this.onResize();
+ super.setMap(this.map);
+ super.initSettings(options);
+ });
+ }
+
+ updateBounds(updateImage?, lastCenterPos?) {
+ const w = this.width;
+ const h = this.height;
+ let southWest = this.pointToLatLng(0, h);
+ let northEast = this.pointToLatLng(w, 0);
+ const bounds = new L.LatLngBounds(southWest, northEast);
+
+ if (updateImage && this.imageOverlay) {
+ this.imageOverlay.remove();
+ this.imageOverlay = null;
+ }
+
+ if (this.imageOverlay) {
+ this.imageOverlay.setBounds(bounds);
+ } else {
+ this.imageOverlay = L.imageOverlay(this.options.mapUrl, bounds).addTo(this.map);
+
+ }
+ const padding = 200 * maxZoom;
+ southWest = this.pointToLatLng(-padding, h + padding);
+ northEast = this.pointToLatLng(w + padding, -padding);
+ const maxBounds = new L.LatLngBounds(southWest, northEast);
+ this.map.setMaxBounds(maxBounds);
+ if (lastCenterPos) {
+ lastCenterPos.x *= w;
+ lastCenterPos.y *= h;
+ /* this.ctx.$scope.$injector.get('$mdUtil').nextTick(() => {
+ this.map.panTo(center, { animate: false });
+ });*/
+ }
+ }
+
+ onResize(updateImage?) {
+ let width = this.$container.clientWidth;
+ if (width > 0 && this.aspect) {
+ let height = width / this.aspect;
+ const imageMapHeight = this.$container.clientHeight;
+ if (imageMapHeight > 0 && height > imageMapHeight) {
+ height = imageMapHeight;
+ width = height * this.aspect;
+ }
+ width *= maxZoom;
+ const prevWidth = this.width;
+ const prevHeight = this.height;
+ if (this.width !== width) {
+ this.width = width;
+ this.height = width / this.aspect;
+ if (!this.map) {
+ this.initMap(updateImage);
+ } else {
+ const lastCenterPos = this.latLngToPoint(this.map.getCenter());
+ lastCenterPos.x /= prevWidth;
+ lastCenterPos.y /= prevHeight;
+ this.updateBounds(updateImage, lastCenterPos);
+ this.map.invalidateSize(true);
+ // this.updateMarkers();
+ }
+
+ }
+ }
+ }
+
+ initMap(updateImage?) {
+ if (!this.map && this.aspect > 0) {
+ const center = this.pointToLatLng(this.width / 2, this.height / 2);
+ this.map = L.map(this.$container, {
+ minZoom: 1,
+ maxZoom,
+ scrollWheelZoom: !this.options.disableScrollZooming,
+ center,
+ zoom: 1,
+ crs: L.CRS.Simple,
+ attributionControl: false
+ });
+ this.updateBounds(updateImage);
+ // this.updateMarkers();
+ }
+ }
+
+ convertPosition(expression): L.LatLng {
+ return this.pointToLatLng(
+ expression[this.options.xPosKeyName] * this.width,
+ expression[this.options.yPosKeyName] * this.height);
+ }
+
+ pointToLatLng(x, y): L.LatLng {
+ return L.CRS.Simple.pointToLatLng({ x, y } as L.PointExpression, maxZoom - 1);
+ }
+
+ latLngToPoint(latLng) {
+ return L.CRS.Simple.latLngToPoint(latLng, maxZoom - 1);
+ }
+}
\ No newline at end of file
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/index.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/index.ts
new file mode 100644
index 0000000000..16d411c167
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/index.ts
@@ -0,0 +1,21 @@
+///
+/// Copyright © 2016-2020 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+export * from './tencent-map';
+export * from './google-map';
+export * from './here-map';
+export * from './image-map';
+export * from './openstreet-map';
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/openstreet-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/openstreet-map.ts
new file mode 100644
index 0000000000..0412b0450d
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/openstreet-map.ts
@@ -0,0 +1,30 @@
+///
+/// Copyright © 2016-2020 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+import L from 'leaflet';
+import LeafletMap from '../leaflet-map';
+import { MapSettings, UnitedMapSettings } from '../map-models';
+
+export class OpenStreetMap extends LeafletMap {
+ constructor($container, options: UnitedMapSettings) {
+ super($container, options);
+ const map = L.map($container).setView(options?.defaultCenterPosition, options?.defaultZoomLevel);
+ const tileLayer = (L.tileLayer as any).provider('OpenStreetMap.Mapnik');
+ tileLayer.addTo(map);
+ super.setMap(map);
+ super.initSettings(options);
+ }
+}
\ No newline at end of file
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/tencent-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/tencent-map.ts
new file mode 100644
index 0000000000..980846796c
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/tencent-map.ts
@@ -0,0 +1,32 @@
+///
+/// Copyright © 2016-2020 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+
+import L from 'leaflet';
+import LeafletMap from '../leaflet-map';
+import { MapSettings, UnitedMapSettings } from '../map-models';
+
+export class TencentMap extends LeafletMap {
+ constructor($container, options: UnitedMapSettings) {
+ super($container, options);
+ const txUrl = 'http://rt{s}.map.gtimg.com/realtimerender?z={z}&x={x}&y={y}&type=vector&style=0';
+ const map = L.map($container).setView(options?.defaultCenterPosition, options?.defaultZoomLevel);
+ const txLayer = L.tileLayer(txUrl, { subdomains: '0123', tms: true }).addTo(map);
+ txLayer.addTo(map);
+ super.setMap(map);
+ super.initSettings(options);
+ }
+}
\ No newline at end of file
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts
new file mode 100644
index 0000000000..f2bcf35f15
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts
@@ -0,0 +1,1250 @@
+///
+/// Copyright © 2016-2020 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+export const googleMapSettingsSchema =
+{
+ schema: {
+ title: 'Google Map Configuration',
+ type: 'object',
+ properties: {
+ gmApiKey: {
+ title: 'Google Maps API Key',
+ type: 'string'
+ },
+ gmDefaultMapType: {
+ title: 'Default map type',
+ type: 'string',
+ default: 'roadmap'
+ }
+ },
+ required: [
+ ]
+ },
+ form: [
+ 'gmApiKey',
+ {
+ key: 'gmDefaultMapType',
+ type: 'rc-select',
+ multiple: false,
+ items: [
+ {
+ value: 'roadmap',
+ label: 'Roadmap'
+ },
+ {
+ value: 'satellite',
+ label: 'Satellite'
+ },
+ {
+ value: 'hybrid',
+ label: 'Hybrid'
+ },
+ {
+ value: 'terrain',
+ label: 'Terrain'
+ }
+ ]
+ }
+ ]
+};
+
+export const tencentMapSettingsSchema =
+{
+ schema: {
+ title: 'Tencent Map Configuration',
+ type: 'object',
+ properties: {
+ tmApiKey: {
+ title: 'Tencent Maps API Key',
+ type: 'string'
+ },
+ tmDefaultMapType: {
+ title: 'Default map type',
+ type: 'string',
+ default: 'roadmap'
+ }
+ },
+ required: [
+ ]
+ },
+ form: [
+ 'tmApiKey',
+ {
+ key: 'tmDefaultMapType',
+ type: 'rc-select',
+ multiple: false,
+ items: [
+ {
+ value: 'roadmap',
+ label: 'Roadmap'
+ },
+ {
+ value: 'satellite',
+ label: 'Satellite'
+ },
+ {
+ value: 'hybrid',
+ label: 'Hybrid'
+ },
+ ]
+ }
+ ]
+};
+
+export const hereMapSettingsSchema =
+{
+ schema: {
+ title: 'HERE Map Configuration',
+ type: 'object',
+ properties: {
+ mapProvider: {
+ title: 'Map layer',
+ type: 'string',
+ default: 'HERE.normalDay'
+ },
+ credentials: {
+ type: 'object',
+ properties: {
+ app_id: {
+ title: 'HERE app id',
+ type: 'string'
+ },
+ app_code: {
+ title: 'HERE app code',
+ type: 'string'
+ }
+ },
+ required: ['app_id', 'app_code']
+ }
+ },
+ required: []
+ },
+ form: [
+ {
+ key: 'mapProvider',
+ type: 'rc-select',
+ multiple: false,
+ items: [
+ {
+ value: 'HERE.normalDay',
+ label: 'HERE.normalDay (Default)'
+ },
+ {
+ value: 'HERE.normalNight',
+ label: 'HERE.normalNight'
+ },
+ {
+ value: 'HERE.hybridDay',
+ label: 'HERE.hybridDay'
+ },
+ {
+ value: 'HERE.terrainDay',
+ label: 'HERE.terrainDay'
+ }
+ ]
+ },
+ 'credentials'
+ ]
+};
+
+export const openstreetMapSettingsSchema =
+{
+ schema: {
+ title: 'Openstreet Map Configuration',
+ type: 'object',
+ properties: {
+ mapProvider: {
+ title: 'Map provider',
+ type: 'string',
+ default: 'OpenStreetMap.Mapnik'
+ },
+ useCustomProvider: {
+ title: 'Use custom provider',
+ type: 'boolean',
+ default: false
+ },
+ customProviderTileUrl: {
+ title: 'Custom provider tile URL',
+ type: 'string',
+ default: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
+ }
+ },
+ required: []
+ },
+ form: [
+ {
+ key: 'mapProvider',
+ type: 'rc-select',
+ multiple: false,
+ items: [
+ {
+ value: 'OpenStreetMap.Mapnik',
+ label: 'OpenStreetMap.Mapnik (Default)'
+ },
+ {
+ value: 'OpenStreetMap.BlackAndWhite',
+ label: 'OpenStreetMap.BlackAndWhite'
+ },
+ {
+ value: 'OpenStreetMap.HOT',
+ label: 'OpenStreetMap.HOT'
+ },
+ {
+ value: 'Esri.WorldStreetMap',
+ label: 'Esri.WorldStreetMap'
+ },
+ {
+ value: 'Esri.WorldTopoMap',
+ label: 'Esri.WorldTopoMap'
+ },
+ {
+ value: 'CartoDB.Positron',
+ label: 'CartoDB.Positron'
+ },
+ {
+ value: 'CartoDB.DarkMatter',
+ label: 'CartoDB.DarkMatter'
+ }
+ ]
+ },
+ 'useCustomProvider',
+ {
+ key: 'customProviderTileUrl',
+ condition: 'model.useCustomProvider === true',
+ }
+ ]
+};
+
+export const commonMapSettingsSchema =
+{
+ schema: {
+ title: 'Map Configuration',
+ type: 'object',
+ properties: {
+ defaultZoomLevel: {
+ title: 'Default map zoom level (0 - 20)',
+ type: 'number'
+ },
+ useDefaultCenterPosition: {
+ title: 'Use default map center position',
+ type: 'boolean',
+ default: false
+ },
+ defaultCenterPosition: {
+ title: 'Default map center position (0,0)',
+ type: 'string',
+ default: '0,0'
+ },
+ fitMapBounds: {
+ title: 'Fit map bounds to cover all markers',
+ type: 'boolean',
+ default: true
+ },
+ disableScrollZooming: {
+ title: 'Disable scroll zooming',
+ type: 'boolean',
+ default: false
+ },
+ latKeyName: {
+ title: 'Latitude key name',
+ type: 'string',
+ default: 'latitude'
+ },
+ lngKeyName: {
+ title: 'Longitude key name',
+ type: 'string',
+ default: 'longitude'
+ },
+ showLabel: {
+ title: 'Show label',
+ type: 'boolean',
+ default: true
+ },
+ label: {
+ title: 'Label (pattern examples: \'${entityName}\', \'${entityName}: (Text ${keyName} units.)\' )',
+ type: 'string',
+ default: '${entityName}'
+ },
+ useLabelFunction: {
+ title: 'Use label function',
+ type: 'boolean',
+ default: false
+ },
+ labelFunction: {
+ title: 'Label function: f(data, dsData, dsIndex)',
+ type: 'string'
+ },
+ showTooltip: {
+ title: 'Show tooltip',
+ type: 'boolean',
+ default: true
+ },
+ showTooltipAction: {
+ title: 'Action for displaying the tooltip',
+ type: 'string',
+ default: 'click'
+ },
+ autocloseTooltip: {
+ title: 'Auto-close tooltips',
+ type: 'boolean',
+ default: true
+ },
+ tooltipPattern: {
+ title: 'Tooltip (for ex. \'Text ${keyName} units.\' or Link text\')',
+ type: 'string',
+ default: '${entityName}
Latitude: ${latitude:7}
Longitude: ${longitude:7}'
+ },
+ useTooltipFunction: {
+ title: 'Use tooltip function',
+ type: 'boolean',
+ default: false
+ },
+ tooltipFunction: {
+ title: 'Tooltip function: f(data, dsData, dsIndex)',
+ type: 'string'
+ },
+ color: {
+ title: 'Color',
+ type: 'string'
+ },
+ useColorFunction: {
+ title: 'Use color function',
+ type: 'boolean',
+ default: false
+ },
+ colorFunction: {
+ title: 'Color function: f(data, dsData, dsIndex)',
+ type: 'string'
+ },
+ showPolygon: {
+ title: 'Show polygon',
+ type: 'boolean',
+ default: false
+ },
+ polygonKeyName: {
+ title: 'Polygon key name',
+ type: 'string',
+ default: 'coordinates'
+ },
+ polygonColor: {
+ title: 'Polygon color',
+ type: 'string'
+ },
+ polygonOpacity: {
+ title: 'Polygon opacity',
+ type: 'number',
+ default: 0.5
+ },
+ polygonStrokeColor: {
+ title: 'Stroke color',
+ type: 'string'
+ },
+ polygonStrokeOpacity: {
+ title: 'Stroke opacity',
+ type: 'number',
+ default: 1
+ },
+ polygonStrokeWeight: {
+ title: 'Stroke weight',
+ type: 'number',
+ default: 1
+ },
+ usePolygonColorFunction: {
+ title: 'Use polygon color function',
+ type: 'boolean',
+ default: false
+ },
+ polygonColorFunction: {
+ title: 'Polygon Color function: f(data, dsData, dsIndex)',
+ type: 'string'
+ },
+ draggableMarker: {
+ title: 'Draggable Marker',
+ type: 'boolean',
+ default: false
+ },
+ markerImage: {
+ title: 'Custom marker image',
+ type: 'string'
+ },
+ markerImageSize: {
+ title: 'Custom marker image size (px)',
+ type: 'number',
+ default: 34
+ },
+ useMarkerImageFunction: {
+ title: 'Use marker image function',
+ type: 'boolean',
+ default: false
+ },
+ markerImageFunction: {
+ title: 'Marker image function: f(data, images, dsData, dsIndex)',
+ type: 'string'
+ },
+ markerImages: {
+ title: 'Marker images',
+ type: 'array',
+ items: {
+ title: 'Marker image',
+ type: 'string'
+ }
+ }
+ },
+ required: []
+ },
+ form: [
+ 'defaultZoomLevel',
+ 'useDefaultCenterPosition',
+ 'defaultCenterPosition',
+ 'fitMapBounds',
+ 'disableScrollZooming',
+ 'latKeyName',
+ 'lngKeyName',
+ 'showLabel',
+ 'label',
+ 'useLabelFunction',
+ 'draggableMarker',
+ {
+ key: 'labelFunction',
+ type: 'javascript'
+ },
+ 'showTooltip',
+ {
+ key: 'showTooltipAction',
+ type: 'rc-select',
+ multiple: false,
+ items: [
+ {
+ value: 'click',
+ label: 'Show tooltip on click (Default)'
+ },
+ {
+ value: 'hover',
+ label: 'Show tooltip on hover'
+ }
+ ]
+ },
+ 'autocloseTooltip',
+ {
+ key: 'tooltipPattern',
+ type: 'textarea'
+ },
+ 'useTooltipFunction',
+ {
+ key: 'tooltipFunction',
+ type: 'javascript'
+ },
+ {
+ key: 'color',
+ type: 'color'
+ },
+ 'useColorFunction',
+ {
+ key: 'colorFunction',
+ type: 'javascript'
+ }, 'showPolygon', 'polygonKeyName',
+ {
+ key: 'polygonColor',
+ type: 'color'
+ },
+ 'polygonOpacity',
+ {
+ key: 'polygonStrokeColor',
+ type: 'color'
+ },
+ 'polygonStrokeOpacity', 'polygonStrokeWeight', 'usePolygonColorFunction',
+ {
+ key: 'polygonColorFunction',
+ type: 'javascript'
+ },
+ {
+ key: 'markerImage',
+ type: 'image'
+ },
+ 'markerImageSize',
+ 'useMarkerImageFunction',
+ {
+ key: 'markerImageFunction',
+ type: 'javascript'
+ },
+ {
+ key: 'markerImages',
+ items: [
+ {
+ key: 'markerImages[]',
+ type: 'image'
+ }
+ ]
+ }
+ ]
+};
+
+export const routeMapSettingsSchema =
+{
+ schema: {
+ title: 'Route Map Configuration',
+ type: 'object',
+ properties: {
+ strokeWeight: {
+ title: 'Stroke weight',
+ type: 'number',
+ default: 2
+ },
+ strokeOpacity: {
+ title: 'Stroke opacity',
+ type: 'number',
+ default: 1.0
+ }
+ },
+ required: []
+ },
+ form: [
+ 'strokeWeight',
+ 'strokeOpacity'
+ ]
+};
+
+export const markerClusteringSettingsSchema =
+{
+ schema: {
+ title: 'Markers Clustering Configuration',
+ type: 'object',
+ properties: {
+ useClusterMarkers: {
+ title: 'Use map markers clustering',
+ type: 'boolean',
+ default: false
+ },
+ zoomOnClick: {
+ title: 'Zoom when clicking on a cluster',
+ type: 'boolean',
+ default: true
+ },
+ maxZoom: {
+ title: 'The maximum zoom level when a marker can be part of a cluster (0 - 18)',
+ type: 'number'
+ }
+ },
+ required: []
+ },
+ form: [
+ 'useClusterMarkers',
+ 'zoomOnClick',
+ 'maxZoom'
+ ]
+};
+
+export const markerClusteringSettingsSchemaGoogle =
+{
+ schema: {
+ title: 'Marker Clustering Configuration Google',
+ type: 'object',
+ properties: {
+ gridSize: {
+ title: 'Maximum radius that a cluster will cover in pixels',
+ type: 'number',
+ default: 60
+ },
+ minimumClusterSize: {
+ title: 'The minimum number of markers in a cluster',
+ type: 'number'
+ }
+ },
+ required: []
+ },
+ form: [
+ 'gridSize',
+ 'minimumClusterSize'
+ ]
+};
+
+export const markerClusteringSettingsSchemaLeaflet =
+{
+ schema: {
+ title: 'Markers Clustering Configuration Leaflet',
+ type: 'object',
+ properties: {
+ showCoverageOnHover: {
+ title: 'Show the bounds of markers when mouse over a cluster',
+ type: 'boolean',
+ default: true
+ },
+ animate: {
+ title: 'Show animation on markers when zooming',
+ type: 'boolean',
+ default: true
+ },
+ maxClusterRadius: {
+ title: 'Maximum radius that a cluster will cover in pixels',
+ type: 'number',
+ default: 80
+ },
+ chunkedLoading: {
+ title: 'Use chunks for adding markers so that the page does not freeze',
+ type: 'boolean',
+ default: false
+ },
+ removeOutsideVisibleBounds: {
+ title: 'Use lazy load for adding markers',
+ type: 'boolean',
+ default: true
+ }
+ },
+ required: []
+ },
+ form: [
+ 'showCoverageOnHover',
+ 'animate',
+ 'maxClusterRadius',
+ 'chunkedLoading',
+ 'removeOutsideVisibleBounds'
+ ]
+};
+
+export const imageMapSettingsSchema =
+{
+ schema: {
+ title: 'Image Map Configuration',
+ type: 'object',
+ properties: {
+ mapImageUrl: {
+ title: 'Image map background',
+ type: 'string',
+ default: 'data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg=='
+ },
+ imageEntityAlias: {
+ title: 'Image URL source entity alias',
+ type: 'string',
+ default: ''
+ },
+ imageUrlAttribute: {
+ title: 'Image URL source entity attribute',
+ type: 'string',
+ default: ''
+ },
+ disableScrollZooming: {
+ title: 'Disable scroll zooming',
+ type: 'boolean',
+ default: false
+ },
+ xPosKeyName: {
+ title: 'X position key name',
+ type: 'string',
+ default: 'xPos'
+ },
+ yPosKeyName: {
+ title: 'Y position key name',
+ type: 'string',
+ default: 'yPos'
+ },
+ showLabel: {
+ title: 'Show label',
+ type: 'boolean',
+ default: true
+ },
+ label: {
+ title: 'Label (pattern examples: \'${entityName}\', \'${entityName}: (Text ${keyName} units.)\' )',
+ type: 'string',
+ default: '${entityName}'
+ },
+ useLabelFunction: {
+ title: 'Use label function',
+ type: 'boolean',
+ default: false
+ },
+ labelFunction: {
+ title: 'Label function: f(data, dsData, dsIndex)',
+ type: 'string'
+ },
+ showTooltip: {
+ title: 'Show tooltip',
+ type: 'boolean',
+ default: true
+ },
+ showTooltipAction: {
+ title: 'Action for displaying the tooltip',
+ type: 'string',
+ default: 'click'
+ },
+ autocloseTooltip: {
+ title: 'Auto-close tooltips',
+ type: 'boolean',
+ default: true
+ },
+ tooltipPattern: {
+ title: 'Tooltip (for ex. \'Text ${keyName} units.\' or Link text\')',
+ type: 'string',
+ default: '${entityName}
X Pos: ${xPos:2}
Y Pos: ${yPos:2}'
+ },
+ useTooltipFunction: {
+ title: 'Use tooltip function',
+ type: 'boolean',
+ default: false
+ },
+ tooltipFunction: {
+ title: 'Tooltip function: f(data, dsData, dsIndex)',
+ type: 'string'
+ },
+ color: {
+ title: 'Color',
+ type: 'string'
+ },
+ posFunction: {
+ title: 'Position conversion function: f(origXPos, origYPos), should return x,y coordinates as double from 0 to 1 each',
+ type: 'string',
+ default: 'return {x: origXPos, y: origYPos};'
+ },
+ markerOffsetX: {
+ title: 'Marker X offset relative to position',
+ type: 'number',
+ default: 0.5
+ },
+ markerOffsetY: {
+ title: 'Marker Y offset relative to position',
+ type: 'number',
+ default: 1
+ },
+ useColorFunction: {
+ title: 'Use color function',
+ type: 'boolean',
+ default: false
+ },
+ colorFunction: {
+ title: 'Color function: f(data, dsData, dsIndex)',
+ type: 'string'
+ },
+ markerImage: {
+ title: 'Custom marker image',
+ type: 'string'
+ },
+ markerImageSize: {
+ title: 'Custom marker image size (px)',
+ type: 'number',
+ default: 34
+ },
+ useMarkerImageFunction: {
+ title: 'Use marker image function',
+ type: 'boolean',
+ default: false
+ },
+ markerImageFunction: {
+ title: 'Marker image function: f(data, images, dsData, dsIndex)',
+ type: 'string'
+ },
+ markerImages: {
+ title: 'Marker images',
+ type: 'array',
+ items: {
+ title: 'Marker image',
+ type: 'string'
+ }
+ }
+ },
+ required: []
+ },
+ form: [
+ {
+ key: 'mapImageUrl',
+ type: 'image'
+ },
+ 'imageEntityAlias',
+ 'imageUrlAttribute',
+ 'disableScrollZooming',
+ 'xPosKeyName',
+ 'yPosKeyName',
+ 'showLabel',
+ 'label',
+ 'useLabelFunction',
+ {
+ key: 'labelFunction',
+ type: 'javascript'
+ },
+ 'showTooltip',
+ {
+ key: 'showTooltipAction',
+ type: 'rc-select',
+ multiple: false,
+ items: [
+ {
+ value: 'click',
+ label: 'Show tooltip on click (Default)'
+ },
+ {
+ value: 'hover',
+ label: 'Show tooltip on hover'
+ }
+ ]
+ },
+ 'autocloseTooltip',
+ {
+ key: 'tooltipPattern',
+ type: 'textarea'
+ },
+ 'useTooltipFunction',
+ {
+ key: 'tooltipFunction',
+ type: 'javascript'
+ },
+ {
+ key: 'color',
+ type: 'color'
+ },
+ {
+ key: 'posFunction',
+ type: 'javascript'
+ },
+ 'markerOffsetX',
+ 'markerOffsetY',
+ 'useColorFunction',
+ {
+ key: 'colorFunction',
+ type: 'javascript'
+ },
+ {
+ key: 'markerImage',
+ type: 'image'
+ },
+ 'markerImageSize',
+ 'useMarkerImageFunction',
+ {
+ key: 'markerImageFunction',
+ type: 'javascript'
+ },
+ {
+ key: 'markerImages',
+ items: [
+ {
+ key: 'markerImages[]',
+ type: 'image'
+ }
+ ]
+ }
+ ]
+};
+
+export const mapProviderSchema =
+{
+ schema: {
+ title: 'Map Provider Configuration',
+ type: 'object',
+ properties: {
+ provider: {
+ title: 'Map Provider',
+ type: 'string',
+ default: 'openstreet-map'
+ }
+ },
+ required: []
+ },
+ form: [
+ {
+ key: 'provider',
+ type: 'rc-select',
+ multiple: false,
+ items: [
+ {
+ value: 'google-map',
+ label: 'Google maps'
+ },
+ {
+ value: 'openstreet-map',
+ label: 'Openstreet maps'
+ },
+ {
+ value: 'here',
+ label: 'HERE maps'
+ },
+ {
+ value: 'image-map',
+ label: 'Image map'
+ },
+ {
+ value: 'tencent-map',
+ label: 'Tencent maps'
+ }
+ ]
+ }
+ ]
+};
+
+
+export const tripAnimationSchema = {
+ schema: {
+ title: 'Openstreet Map Configuration',
+ type: 'object',
+ properties: {
+ normalizationStep: {
+ title: 'Normalization data step (ms)',
+ type: 'number',
+ default: 1000
+ },
+ latKeyName: {
+ title: 'Latitude key name',
+ type: 'string',
+ default: 'latitude'
+ },
+ lngKeyName: {
+ title: 'Longitude key name',
+ type: 'string',
+ default: 'longitude'
+ },
+ polKeyName: {
+ title: 'Polygon key name',
+ type: 'string',
+ default: 'coordinates'
+ },
+ showLabel: {
+ title: 'Show label',
+ type: 'boolean',
+ default: true
+ },
+ label: {
+ title: 'Label (pattern examples: \'${entityName}\', \'${entityName}: (Text ${keyName} units.)\' )',
+ type: 'string',
+ default: '${entityName}'
+ },
+ useLabelFunction: {
+ title: 'Use label function',
+ type: 'boolean',
+ default: false
+ },
+ labelFunction: {
+ title: 'Label function: f(data, dsData, dsIndex)',
+ type: 'string'
+ },
+ showTooltip: {
+ title: 'Show tooltip',
+ type: 'boolean',
+ default: true
+ },
+ tooltipColor: {
+ title: 'Tooltip background color',
+ type: 'string',
+ default: '#fff'
+ },
+ tooltipFontColor: {
+ title: 'Tooltip font color',
+ type: 'string',
+ default: '#000'
+ },
+ tooltipOpacity: {
+ title: 'Tooltip opacity (0-1)',
+ type: 'number',
+ default: 1
+ },
+ tooltipPattern: {
+ title: 'Tooltip (for ex. \'Text ${keyName} units.\' or Link text\')',
+ type: 'string',
+ default: '${entityName}
Latitude: ${latitude:7}
Longitude: ${longitude:7}'
+ },
+ useTooltipFunction: {
+ title: 'Use tooltip function',
+ type: 'boolean',
+ default: false
+ },
+ tooltipFunction: {
+ title: 'Tooltip function: f(data, dsData, dsIndex)',
+ type: 'string'
+ },
+ color: {
+ title: 'Path color',
+ type: 'string'
+ },
+ strokeWeight: {
+ title: 'Stroke weight',
+ type: 'number',
+ default: 2
+ },
+ strokeOpacity: {
+ title: 'Stroke opacity',
+ type: 'number',
+ default: 1
+ },
+ useColorFunction: {
+ title: 'Use path color function',
+ type: 'boolean',
+ default: false
+ },
+ colorFunction: {
+ title: 'Path color function: f(data, dsData, dsIndex)',
+ type: 'string'
+ },
+ usePolylineDecorator: {
+ title: 'Use path decorator',
+ type: 'boolean',
+ default: false
+ },
+ decoratorSymbol: {
+ title: 'Decorator symbol',
+ type: 'string',
+ default: 'arrowHead'
+ },
+ decoratorSymbolSize: {
+ title: 'Decorator symbol size (px)',
+ type: 'number',
+ default: 10
+ },
+ useDecoratorCustomColor: {
+ title: 'Use path decorator custom color',
+ type: 'boolean',
+ default: false
+ },
+ decoratorCustomColor: {
+ title: 'Decorator custom color',
+ type: 'string',
+ default: '#000'
+ },
+ decoratorOffset: {
+ title: 'Decorator offset',
+ type: 'string',
+ default: '20px'
+ },
+ endDecoratorOffset: {
+ title: 'End decorator offset',
+ type: 'string',
+ default: '20px'
+ },
+ decoratorRepeat: {
+ title: 'Decorator repeat',
+ type: 'string',
+ default: '20px'
+ },
+ showPolygon: {
+ title: 'Show polygon',
+ type: 'boolean',
+ default: false
+ },
+ polygonTooltipPattern: {
+ title: 'Tooltip (for ex. \'Text ${keyName} units.\' or Link text\')',
+ type: 'string',
+ default: '${entityName}
TimeStamp: ${ts:7}'
+ },
+ usePolygonTooltipFunction: {
+ title: 'Use polygon tooltip function',
+ type: 'boolean',
+ default: false
+ },
+ polygonTooltipFunction: {
+ title: 'Polygon tooltip function: f(data, dsData, dsIndex)',
+ type: 'string'
+ },
+ polygonColor: {
+ title: 'Polygon color',
+ type: 'string'
+ },
+ polygonOpacity: {
+ title: 'Polygon opacity',
+ type: 'number',
+ default: 0.5
+ },
+ polygonStrokeColor: {
+ title: 'Polygon border color',
+ type: 'string'
+ },
+ polygonStrokeOpacity: {
+ title: 'Polygon border opacity',
+ type: 'number',
+ default: 1
+ },
+ polygonStrokeWeight: {
+ title: 'Polygon border weight',
+ type: 'number',
+ default: 1
+ },
+ usePolygonColorFunction: {
+ title: 'Use polygon color function',
+ type: 'boolean',
+ default: false
+ },
+ polygonColorFunction: {
+ title: 'Polygon Color function: f(data, dsData, dsIndex)',
+ type: 'string'
+ },
+ showPoints: {
+ title: 'Show points',
+ type: 'boolean',
+ default: false
+ },
+ pointColor: {
+ title: 'Point color',
+ type: 'string'
+ },
+ pointSize: {
+ title: 'Point size (px)',
+ type: 'number',
+ default: 10
+ },
+ usePointAsAnchor: {
+ title: 'Use point as anchor',
+ type: 'boolean',
+ default: false
+ },
+ pointAsAnchorFunction: {
+ title: 'Point as anchor function: f(data, dsData, dsIndex)',
+ type: 'string'
+ },
+ pointTooltipOnRightPanel: {
+ title: 'Independant point tooltip',
+ type: 'boolean',
+ default: true
+ },
+ autocloseTooltip: {
+ title: 'Auto-close point popup',
+ type: 'boolean',
+ default: true
+ },
+ markerImage: {
+ title: 'Custom marker image',
+ type: 'string'
+ },
+ markerImageSize: {
+ title: 'Custom marker image size (px)',
+ type: 'number',
+ default: 34
+ },
+ rotationAngle: {
+ title: 'Set additional rotation angle for marker (deg)',
+ type: 'number',
+ default: 180
+ },
+ useMarkerImageFunction: {
+ title: 'Use marker image function',
+ type: 'boolean',
+ default: false
+ },
+ markerImageFunction: {
+ title: 'Marker image function: f(data, images, dsData, dsIndex)',
+ type: 'string'
+ },
+ markerImages: {
+ title: 'Marker images',
+ type: 'array',
+ items: {
+ title: 'Marker image',
+ type: 'string'
+ }
+ }
+ },
+ required: []
+ },
+ form: [{
+ key: 'mapProvider',
+ type: 'rc-select',
+ multiple: false,
+ items: [{
+ value: 'OpenStreetMap.Mapnik',
+ label: 'OpenStreetMap.Mapnik (Default)'
+ }, {
+ value: 'OpenStreetMap.BlackAndWhite',
+ label: 'OpenStreetMap.BlackAndWhite'
+ }, {
+ value: 'OpenStreetMap.HOT',
+ label: 'OpenStreetMap.HOT'
+ }, {
+ value: 'Esri.WorldStreetMap',
+ label: 'Esri.WorldStreetMap'
+ }, {
+ value: 'Esri.WorldTopoMap',
+ label: 'Esri.WorldTopoMap'
+ }, {
+ value: 'CartoDB.Positron',
+ label: 'CartoDB.Positron'
+ }, {
+ value: 'CartoDB.DarkMatter',
+ label: 'CartoDB.DarkMatter'
+ }]
+ }, 'normalizationStep', 'latKeyName', 'lngKeyName', 'polKeyName', 'showLabel', 'label', 'useLabelFunction', {
+ key: 'labelFunction',
+ type: 'javascript'
+ }, 'showTooltip', {
+ key: 'tooltipColor',
+ type: 'color'
+ }, {
+ key: 'tooltipFontColor',
+ type: 'color'
+ }, 'tooltipOpacity', {
+ key: 'tooltipPattern',
+ type: 'textarea'
+ }, 'useTooltipFunction', {
+ key: 'tooltipFunction',
+ type: 'javascript'
+ }, {
+ key: 'color',
+ type: 'color'
+ }, 'useColorFunction', {
+ key: 'colorFunction',
+ type: 'javascript'
+ }, 'usePolylineDecorator', {
+ key: 'decoratorSymbol',
+ type: 'rc-select',
+ multiple: false,
+ items: [{
+ value: 'arrowHead',
+ label: 'Arrow'
+ }, {
+ value: 'dash',
+ label: 'Dash'
+ }]
+ }, 'decoratorSymbolSize', 'useDecoratorCustomColor', {
+ key: 'decoratorCustomColor',
+ type: 'color'
+ }, {
+ key: 'decoratorOffset',
+ type: 'textarea'
+ }, {
+ key: 'endDecoratorOffset',
+ type: 'textarea'
+ }, {
+ key: 'decoratorRepeat',
+ type: 'textarea'
+ }, 'strokeWeight', 'strokeOpacity', 'showPolygon', {
+ key: 'polygonTooltipPattern',
+ type: 'textarea'
+ }, 'usePolygonTooltipFunction', {
+ key: 'polygonTooltipFunction',
+ type: 'javascript'
+ }, {
+ key: 'polygonColor',
+ type: 'color'
+ }, 'polygonOpacity', {
+ key: 'polygonStrokeColor',
+ type: 'color'
+ }, 'polygonStrokeOpacity', 'polygonStrokeWeight', 'usePolygonColorFunction', {
+ key: 'polygonColorFunction',
+ type: 'javascript'
+ }, 'showPoints', {
+ key: 'pointColor',
+ type: 'color'
+ }, 'pointSize', 'usePointAsAnchor', {
+ key: 'pointAsAnchorFunction',
+ type: 'javascript'
+ }, 'pointTooltipOnRightPanel', 'autocloseTooltip', {
+ key: 'markerImage',
+ type: 'image'
+ }, 'markerImageSize', 'rotationAngle', 'useMarkerImageFunction',
+ {
+ key: 'markerImageFunction',
+ type: 'javascript'
+ }, {
+ key: 'markerImages',
+ items: [
+ {
+ key: 'markerImages[]',
+ type: 'image'
+ }
+ ]
+ }]
+}
\ No newline at end of file
diff --git a/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.html b/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.html
new file mode 100644
index 0000000000..e6f6a1c741
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.html
@@ -0,0 +1,37 @@
+
+
\ No newline at end of file
diff --git a/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.scss b/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.scss
new file mode 100644
index 0000000000..d7be4629c3
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.scss
@@ -0,0 +1,89 @@
+/**
+ * Copyright © 2016-2020 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.trip-animation-widget {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ font-size: 16px;
+ line-height: 24px;
+ display: flex;
+ flex-direction: column;
+
+ .trip-animation-label-container {
+ height: 24px;
+ }
+
+ .trip-animation-container {
+ position: relative;
+ z-index: 1;
+ flex: 1;
+ width: 100%;
+
+ .map {
+ width: 100%;
+ height: 100%;
+ }
+
+ .trip-animation-info-panel {
+ position: absolute;
+ top: 0;
+ right: 0;
+ pointer-events: none;
+
+ .tooltip-button {
+ top: 0;
+ left: 0;
+ width: 32px;
+ min-width: 32px;
+ height: 32px;
+ min-height: 32px;
+ padding: 0 0 2px;
+ margin: 2px;
+ line-height: 24px;
+ z-index: 999;
+
+ &::ng-deep .mat-button-wrapper {
+ padding: 0;
+ }
+
+ mat-icon {
+ width: 24px;
+ height: 24px;
+
+ svg {
+ width: inherit;
+ height: inherit;
+ }
+ }
+ }
+ }
+
+ .trip-animation-tooltip {
+ position: absolute;
+ top: 38px;
+ right: 0;
+ z-index: 400;
+ padding: 10px;
+ background-color: #fff;
+ transition: 0.3s ease-in-out;
+
+ &-hidden {
+ transform: translateX(110%);
+ }
+ }
+ }
+}
diff --git a/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts b/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts
new file mode 100644
index 0000000000..07e2c2d697
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts
@@ -0,0 +1,156 @@
+///
+/// Copyright © 2016-2020 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+import L from 'leaflet';
+import _ from 'lodash';
+import tinycolor from 'tinycolor2';
+import { interpolateOnPointSegment } from 'leaflet-geometryutil';
+
+import { Component, OnInit, Input, ViewChild, AfterViewInit, ChangeDetectorRef, SecurityContext } from '@angular/core';
+import { MapWidgetController, TbMapWidgetV2 } from '../lib/maps/map-widget2';
+import { MapProviders } from '../lib/maps/map-models';
+import { parseArray, parseTemplate, safeExecute } from '@app/core/utils';
+import { initSchema, addToSchema, addGroupInfo } from '@app/core/schema-utils';
+import { tripAnimationSchema } from '../lib/maps/schemes';
+import { DomSanitizer } from '@angular/platform-browser';
+import { WidgetConfig, JsonSchema, JsonSettingsSchema } from '@app/shared/public-api';
+import { WidgetContext } from '@app/modules/home/models/widget-component.models';
+import { getRatio, findAngle } from '../lib/maps/maps-utils';
+
+
+@Component({
+ selector: 'trip-animation',
+ templateUrl: './trip-animation.component.html',
+ styleUrls: ['./trip-animation.component.scss']
+})
+export class TripAnimationComponent implements OnInit, AfterViewInit {
+
+ constructor(private cd: ChangeDetectorRef, private sanitizer: DomSanitizer) { }
+
+ @Input() ctx: WidgetContext;
+
+ @ViewChild('map') mapContainer;
+
+ mapWidget: MapWidgetController;
+ historicalData;
+ intervals;
+ normalizationStep = 1000;
+ interpolatedData = [];
+ widgetConfig: WidgetConfig;
+ settings;
+ mainTooltip = '';
+ visibleTooltip = false;
+ activeTrip;
+
+ static getSettingsSchema(): JsonSettingsSchema {
+ const schema = initSchema();
+ addToSchema(schema, TbMapWidgetV2.getProvidersSchema());
+ addGroupInfo(schema, 'Map Provider Settings');
+ addToSchema(schema, tripAnimationSchema);
+ addGroupInfo(schema, 'Trip Animation Settings');
+ return schema;
+ }
+
+ ngOnInit(): void {
+ this.widgetConfig = this.ctx.widgetConfig;
+ const settings = {
+ normalizationStep: 1000,
+ showLabel: false,
+ buttonColor: tinycolor(this.widgetConfig.color).setAlpha(0.54).toRgbString(),
+ disabledButtonColor: tinycolor(this.widgetConfig.color).setAlpha(0.3).toRgbString(),
+ rotationAngle: 0
+ }
+ this.settings = { ...settings, ...this.ctx.settings };
+ const subscription = this.ctx.subscriptions[Object.keys(this.ctx.subscriptions)[0]];
+ if (subscription) subscription.callbacks.onDataUpdated = (updated) => {
+ this.historicalData = parseArray(this.ctx.data);
+ this.activeTrip = this.historicalData[0][0];
+ this.calculateIntervals();
+ this.timeUpdated(this.intervals[0]);
+ this.mapWidget.map.updatePolylines(this.interpolatedData.map(ds => _.values(ds)));
+
+ this.mapWidget.map.map?.invalidateSize();
+ this.cd.detectChanges();
+ }
+ }
+
+ ngAfterViewInit() {
+ const ctxCopy: WidgetContext = _.cloneDeep(this.ctx);
+ ctxCopy.settings.showLabel = false;
+ ctxCopy.settings.showTooltip = false;
+ this.mapWidget = new MapWidgetController(MapProviders.openstreet, false, ctxCopy, this.mapContainer.nativeElement);
+ }
+
+ timeUpdated(time: number) {
+ const currentPosition = this.interpolatedData.map(dataSource => dataSource[time]);
+ this.activeTrip = currentPosition[0];
+ if (this.settings.showPolygon) {
+ this.mapWidget.map.updatePolygons(this.interpolatedData);
+ }
+ this.mapWidget.map.updateMarkers(currentPosition);
+ }
+
+ setActiveTrip() {
+
+ }
+
+ calculateIntervals() {
+ this.historicalData.forEach((dataSource, index) => {
+ this.intervals = [];
+ for (let time = dataSource[0]?.time; time < dataSource[dataSource.length - 1]?.time; time += this.normalizationStep) {
+ this.intervals.push(time);
+ }
+ this.intervals.push(dataSource[dataSource.length - 1]?.time);
+ this.interpolatedData[index] = this.interpolateArray(dataSource, this.intervals);
+ });
+ }
+
+ showHideTooltip() {
+ const tooltipText: string = this.settings.useTooltipFunction ?
+ safeExecute(this.settings.tooolTipFunction, [this.activeTrip, this.historicalData, 0])
+ : this.settings.tooltipPattern;
+
+ this.mainTooltip = this.sanitizer.sanitize(SecurityContext.HTML, parseTemplate(tooltipText, this.activeTrip))
+ this.visibleTooltip = !this.visibleTooltip;
+ }
+
+ interpolateArray(originData, interpolatedIntervals) {
+
+ const result = {};
+
+ for (let i = 1, j = 0; i < originData.length && j < interpolatedIntervals.length;) {
+ const currentTime = interpolatedIntervals[j];
+ while (originData[i].time < currentTime) i++;
+ const before = originData[i - 1];
+ const after = originData[i];
+ const interpolation = interpolateOnPointSegment(
+ new L.Point(before.latitude, before.longitude),
+ new L.Point(after.latitude, after.longitude),
+ getRatio(before.time, after.time, currentTime));
+ result[currentTime] = ({
+ ...originData[i],
+ rotationAngle: findAngle(before, after) + this.settings.rotationAngle,
+ latitude: interpolation.x,
+ longitude: interpolation.y
+ });
+ j++;
+ }
+ return result;
+ }
+}
+
+export let TbTripAnimationWidget = TripAnimationComponent;
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts
index 6598db5436..c849c09193 100644
--- a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts
@@ -31,6 +31,7 @@ import {
DateRangeNavigatorWidgetComponent
} from '@home/components/widget/lib/date-range-navigator/date-range-navigator.component';
import { MultipleInputWidgetComponent } from './lib/multiple-input-widget.component';
+import { TripAnimationComponent } from './trip-animation/trip-animation.component';
import { WebCameraInputWidgetComponent } from './lib/web-camera-input.component';
@NgModule({
@@ -45,6 +46,7 @@ import { WebCameraInputWidgetComponent } from './lib/web-camera-input.component'
DateRangeNavigatorWidgetComponent,
DateRangeNavigatorPanelComponent,
MultipleInputWidgetComponent,
+ TripAnimationComponent,
WebCameraInputWidgetComponent
],
imports: [
@@ -61,6 +63,7 @@ import { WebCameraInputWidgetComponent } from './lib/web-camera-input.component'
RpcWidgetsModule,
DateRangeNavigatorWidgetComponent,
MultipleInputWidgetComponent,
+ TripAnimationComponent,
WebCameraInputWidgetComponent
],
providers: [
diff --git a/ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.ts b/ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.ts
index 5fc3882a35..aacedd7f3a 100644
--- a/ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.ts
+++ b/ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.ts
@@ -14,13 +14,13 @@
/// limitations under the License.
///
-import {Component, Inject, OnInit} from "@angular/core";
-import {DialogComponent} from "@shared/components/dialog.component";
-import {Store} from "@ngrx/store";
-import {AppState} from "@core/core.state";
-import {Router} from "@angular/router";
-import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
-import {FormBuilder, FormGroup} from "@angular/forms";
+import {Component, Inject, OnInit} from '@angular/core';
+import {DialogComponent} from '@shared/components/dialog.component';
+import {Store} from '@ngrx/store';
+import {AppState} from '@core/core.state';
+import {Router} from '@angular/router';
+import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
+import {FormBuilder, FormGroup} from '@angular/forms';
export interface JsonObjectEdittDialogData {
jsonValue: Object;
diff --git a/ui-ngx/src/app/shared/components/directives/tb-json-to-string.directive.ts b/ui-ngx/src/app/shared/components/directives/tb-json-to-string.directive.ts
index cf7cd0a0bd..f19022e590 100644
--- a/ui-ngx/src/app/shared/components/directives/tb-json-to-string.directive.ts
+++ b/ui-ngx/src/app/shared/components/directives/tb-json-to-string.directive.ts
@@ -14,7 +14,7 @@
/// limitations under the License.
///
-import {Directive, ElementRef, forwardRef, HostListener, Renderer2, SkipSelf} from "@angular/core";
+import {Directive, ElementRef, forwardRef, HostListener, Renderer2, SkipSelf} from '@angular/core';
import {
ControlValueAccessor,
FormControl, FormGroupDirective,
@@ -22,8 +22,8 @@ import {
NG_VALUE_ACCESSOR, NgForm,
ValidationErrors,
Validator
-} from "@angular/forms";
-import {ErrorStateMatcher} from "@angular/material/core";
+} from '@angular/forms';
+import {ErrorStateMatcher} from '@angular/material/core';
@Directive({
selector: '[tb-json-to-string]',
diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-utils.ts b/ui-ngx/src/app/shared/components/json-form/react/json-form-utils.ts
index 18e985ed45..b83b11d7d5 100644
--- a/ui-ngx/src/app/shared/components/json-form/react/json-form-utils.ts
+++ b/ui-ngx/src/app/shared/components/json-form/react/json-form-utils.ts
@@ -113,7 +113,7 @@ const canonicalTitleMap = (titleMap: any, originalEnum?: string[]): { name: stri
const stdFormObj = (name: string, schema: any, options: DefaultsFormOptions): any => {
options = options || {};
- const f = options.global && options.global.formDefaults ? _.cloneDeep(options.global.formDefaults) : {};
+ const f: any = options.global && options.global.formDefaults ? _.cloneDeep(options.global.formDefaults) : {};
if (options.global && options.global.supressPropertyTitles === true) {
f.title = schema.title;
} else {
diff --git a/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.html b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.html
new file mode 100644
index 0000000000..05d6b20c89
--- /dev/null
+++ b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.html
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{speedValue}}
+
+
+
+ {{ this.intervals[this.index] | date:'medium'}}
+ {{ "widget.no-data-found" | translate}}
+
\ No newline at end of file
diff --git a/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.scss b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.scss
new file mode 100644
index 0000000000..d4e4ae17ed
--- /dev/null
+++ b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.scss
@@ -0,0 +1,140 @@
+/**
+ * Copyright © 2016-2020 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+.trip-animation-widget {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ font-size: 16px;
+ line-height: 24px;
+
+ .trip-animation-label-container {
+ height: 24px;
+ }
+
+ .trip-animation-container {
+ position: relative;
+ z-index: 1;
+ flex: 1;
+ width: 100%;
+
+ #trip-animation-map {
+ z-index: 1;
+ width: 100%;
+ height: 100%;
+
+ .pointsLayerMarkerIcon {
+ border-radius: 50%;
+ }
+ }
+
+ .trip-animation-info-panel {
+ position: absolute;
+ top: 0;
+ right: 0;
+ z-index: 2;
+ pointer-events: none;
+
+ .md-button {
+ top: 0;
+ left: 0;
+ width: 32px;
+ min-width: 32px;
+ height: 32px;
+ min-height: 32px;
+ padding: 0 0 2px;
+ margin: 2px;
+ line-height: 24px;
+
+ ng-md-icon {
+ width: 24px;
+ height: 24px;
+
+ svg {
+ width: inherit;
+ height: inherit;
+ }
+ }
+ }
+ }
+
+ .trip-animation-tooltip {
+ position: absolute;
+ top: 38px;
+ right: 0;
+ z-index: 2;
+ padding: 10px;
+ background-color: #fff;
+ transition: 0.3s ease-in-out;
+
+ &-hidden {
+ transform: translateX(110%);
+ }
+ }
+ }
+
+ .trip-animation-control-panel {
+ position: relative;
+ box-sizing: border-box;
+ width: 100%;
+ padding-bottom: 16px;
+ padding-left: 10px;
+
+ md-slider-container {
+ md-slider {
+ min-width: 80px;
+ }
+
+ button.md-button.md-icon-button {
+ width: 44px;
+ min-width: 44px;
+ height: 44px;
+ min-height: 44px;
+ margin: 0;
+ line-height: 28px;
+
+ md-icon {
+ width: 28px;
+ height: 28px;
+ font-size: 28px;
+
+ svg {
+ width: inherit;
+ height: inherit;
+ }
+ }
+ }
+
+ md-select {
+ margin: 0;
+ }
+ }
+
+ .panel-timer {
+ max-width: none;
+ padding-right: 250px;
+ padding-left: 90px;
+ margin-top: -20px;
+ font-size: 12px;
+ font-weight: 500;
+ text-align: center;
+ }
+ }
+}
+
+.speed-select{
+ width: 50px;
+ margin-left: 20px;
+}
diff --git a/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts
new file mode 100644
index 0000000000..2ab3acc283
--- /dev/null
+++ b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts
@@ -0,0 +1,119 @@
+///
+/// Copyright © 2016-2020 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+import { Component, OnInit, OnChanges, Input, Output, EventEmitter, ChangeDetectorRef } from '@angular/core';
+import { interval, Subscription, Subscriber, SubscriptionLike, Observer } from 'rxjs';
+import { filter, tap } from 'rxjs/operators';
+import { HistorySelectSettings } from '@app/modules/home/components/widget/lib/maps/map-models';
+
+@Component({
+ selector: 'tb-history-selector',
+ templateUrl: './history-selector.component.html',
+ styleUrls: ['./history-selector.component.scss']
+})
+export class HistorySelectorComponent implements OnInit, OnChanges {
+
+ @Input() settings: HistorySelectSettings
+ @Input() intervals = [];
+
+ @Output() timeUpdated: EventEmitter
= new EventEmitter();
+
+ animationTime;
+ minTimeIndex = 0;
+ maxTimeIndex = 0;
+ speed = 1;
+ index = 0;
+ playing = false;
+ interval;
+ speeds = [1, 5, 10, 25];
+
+
+ constructor(private cd: ChangeDetectorRef) { }
+
+ ngOnInit(): void {
+ }
+
+ ngOnChanges() {
+ this.maxTimeIndex = this.intervals?.length - 1;
+ }
+
+ play() {
+ this.playing = true;
+ if (!this.interval)
+ this.interval = interval(1000 / this.speed)
+ .pipe(
+ filter(() => this.playing),
+ tap(() => this.index++)).subscribe(() => {
+ if (this.index < this.maxTimeIndex) {
+ this.cd.detectChanges();
+ this.timeUpdated.emit(this.intervals[this.index]);
+ }
+ else {
+ this.interval.complete();
+ }
+ }, err => {
+ console.log(err);
+ }, () => {
+ this.index = this.minTimeIndex;
+ this.playing = false;
+ this.interval = null;
+ this.cd.detectChanges();
+ });
+ }
+
+ reeneble() {
+ if (this.playing) {
+ const position = this.index;
+ this.interval.complete();
+ this.index = position;
+ this.play();
+ }
+ }
+
+ pause() {
+ this.playing = false;
+ this.cd.detectChanges();
+ this.timeUpdated.emit(this.intervals[this.index]);
+ }
+
+ moveNext() {
+ if (this.index < this.maxTimeIndex) {
+ this.index++;
+ }
+ this.pause();
+ }
+
+ movePrev() {
+ if (this.index > this.minTimeIndex) {
+ this.index++;
+ }
+ this.pause();
+ }
+
+ moveStart() {
+ this.index = this.minTimeIndex;
+ this.pause();
+ }
+
+ moveEnd() {
+ this.index = this.maxTimeIndex;
+ this.pause();
+ }
+
+ changeIndex() {
+ this.timeUpdated.emit(this.intervals[this.index]);
+ }
+}
diff --git a/ui-ngx/src/app/shared/components/value-input.component.ts b/ui-ngx/src/app/shared/components/value-input.component.ts
index 0913aefe12..f067e6e1b7 100644
--- a/ui-ngx/src/app/shared/components/value-input.component.ts
+++ b/ui-ngx/src/app/shared/components/value-input.component.ts
@@ -17,12 +17,12 @@
import { Component, forwardRef, Input, OnInit, ViewChild } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR, NgForm } from '@angular/forms';
import { ValueType, valueTypesMap } from '@shared/models/constants';
-import { isObject } from "@core/utils";
-import { MatDialog } from "@angular/material/dialog";
+import { isObject } from '@core/utils';
+import { MatDialog } from '@angular/material/dialog';
import {
JsonObjectEditDialogComponent,
JsonObjectEdittDialogData
-} from "@shared/components/dialog/json-object-edit-dialog.component";
+} from '@shared/components/dialog/json-object-edit-dialog.component';
@Component({
selector: 'tb-value-input',
@@ -79,7 +79,7 @@ export class ValueInputComponent implements OnInit, ControlValueAccessor {
(res) => {
if (res) {
this.modelValue = res;
- this.inputForm.control.patchValue({'value': this.modelValue});
+ this.inputForm.control.patchValue({value: this.modelValue});
}
}
);
diff --git a/ui-ngx/src/app/shared/pipe/public-api.ts b/ui-ngx/src/app/shared/pipe/public-api.ts
index 4f801b7d51..72edfae70d 100644
--- a/ui-ngx/src/app/shared/pipe/public-api.ts
+++ b/ui-ngx/src/app/shared/pipe/public-api.ts
@@ -20,3 +20,4 @@ export * from './keyboard-shortcut.pipe';
export * from './milliseconds-to-time-string.pipe';
export * from './nospace.pipe';
export * from './truncate.pipe';
+export * from './template.pipe';
diff --git a/ui-ngx/src/app/shared/pipe/tbJson.pipe.ts b/ui-ngx/src/app/shared/pipe/tbJson.pipe.ts
index c31cf161ab..5a5fa684fc 100644
--- a/ui-ngx/src/app/shared/pipe/tbJson.pipe.ts
+++ b/ui-ngx/src/app/shared/pipe/tbJson.pipe.ts
@@ -15,7 +15,7 @@
///
import {Pipe, PipeTransform} from '@angular/core';
-import {isObject, isNumber} from "@core/utils";
+import {isObject, isNumber} from '@core/utils';
@Pipe({name: 'tbJson'})
export class TbJsonPipe implements PipeTransform {
diff --git a/ui-ngx/src/app/shared/pipe/template.pipe.ts b/ui-ngx/src/app/shared/pipe/template.pipe.ts
new file mode 100644
index 0000000000..e57863e07c
--- /dev/null
+++ b/ui-ngx/src/app/shared/pipe/template.pipe.ts
@@ -0,0 +1,26 @@
+///
+/// Copyright © 2016-2020 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+import { Pipe, PipeTransform } from '@angular/core';
+import { parseTemplate } from '@app/core/utils';
+
+@Pipe({ name: 'tbParseTemplate' })
+export class TbTemplatePipe implements PipeTransform {
+ transform(template, data): string {
+ console.log('TbTemplatePipe -> transform -> template, data', template, data)
+ return parseTemplate(template, data);
+ }
+}
diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts
index 1df4469fb2..2b714c61f7 100644
--- a/ui-ngx/src/app/shared/shared.module.ts
+++ b/ui-ngx/src/app/shared/shared.module.ts
@@ -104,7 +104,7 @@ import { TbErrorComponent } from '@shared/components/tb-error.component';
import { EntityTypeListComponent } from '@shared/components/entity/entity-type-list.component';
import { EntitySubTypeListComponent } from '@shared/components/entity/entity-subtype-list.component';
import { TruncatePipe } from '@shared/pipe/truncate.pipe';
-import { TbJsonPipe } from "@shared/pipe/tbJson.pipe";
+import { TbJsonPipe } from '@shared/pipe/tbJson.pipe';
import { ColorPickerDialogComponent } from '@shared/components/dialog/color-picker-dialog.component';
import { MatChipDraggableDirective } from '@shared/components/mat-chip-draggable.directive';
import { ColorInputComponent } from '@shared/components/color-input.component';
@@ -125,8 +125,10 @@ import { TbCheatSheetComponent } from '@shared/components/cheatsheet.component';
import { TbHotkeysDirective } from '@shared/components/hotkeys.directive';
import { NavTreeComponent } from '@shared/components/nav-tree.component';
import { LedLightComponent } from '@shared/components/led-light.component';
-import { TbJsonToStringDirective } from "@shared/components/directives/tb-json-to-string.directive";
-import { JsonObjectEditDialogComponent } from "@shared/components/dialog/json-object-edit-dialog.component";
+import { TbJsonToStringDirective } from '@shared/components/directives/tb-json-to-string.directive';
+import { JsonObjectEditDialogComponent } from '@shared/components/dialog/json-object-edit-dialog.component';
+import { HistorySelectorComponent } from './components/time/history-selector/history-selector.component';
+import { TbTemplatePipe } from './pipe/public-api';
@NgModule({
providers: [
@@ -207,9 +209,11 @@ import { JsonObjectEditDialogComponent } from "@shared/components/dialog/json-ob
HighlightPipe,
TruncatePipe,
TbJsonPipe,
+ TbTemplatePipe,
KeyboardShortcutPipe,
TbJsonToStringDirective,
- JsonObjectEditDialogComponent
+ JsonObjectEditDialogComponent,
+ HistorySelectorComponent
],
imports: [
CommonModule,
@@ -365,9 +369,11 @@ import { JsonObjectEditDialogComponent } from "@shared/components/dialog/json-ob
HighlightPipe,
TruncatePipe,
TbJsonPipe,
+ TbTemplatePipe,
KeyboardShortcutPipe,
TranslateModule,
- JsonObjectEditDialogComponent
+ JsonObjectEditDialogComponent,
+ HistorySelectorComponent
]
})
export class SharedModule { }
diff --git a/ui-ngx/src/assets/add_location.svg b/ui-ngx/src/assets/add_location.svg
new file mode 100644
index 0000000000..e3150115b7
--- /dev/null
+++ b/ui-ngx/src/assets/add_location.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ui-ngx/src/polyfills.ts b/ui-ngx/src/polyfills.ts
index 88f93cac56..68a59ba5c3 100644
--- a/ui-ngx/src/polyfills.ts
+++ b/ui-ngx/src/polyfills.ts
@@ -93,6 +93,8 @@ import { TbAnalogueCompass } from '@home/components/widget/lib/analogue-compass'
import { TbAnalogueRadialGauge } from '@home/components/widget/lib/analogue-radial-gauge';
import { TbAnalogueLinearGauge } from '@home/components/widget/lib/analogue-linear-gauge';
import { TbCanvasDigitalGauge } from '@home/components/widget/lib/digital-gauge';
+import { TbMapWidgetV2 } from '@home/components/widget/lib/maps/map-widget2';
+import { TbTripAnimationWidget } from '@app/modules/home/components/widget/trip-animation/trip-animation.component';
import * as tinycolor_ from 'tinycolor2';
@@ -106,3 +108,5 @@ const tinycolor = tinycolor_;
(window as any).TbAnalogueRadialGauge = TbAnalogueRadialGauge;
(window as any).TbAnalogueLinearGauge = TbAnalogueLinearGauge;
(window as any).TbCanvasDigitalGauge = TbCanvasDigitalGauge;
+(window as any).TbMapWidgetV2 = TbMapWidgetV2;
+(window as any).TbTripAnimationWidget = TbTripAnimationWidget;
diff --git a/ui-ngx/src/typings/add-marker.d.ts b/ui-ngx/src/typings/add-marker.d.ts
new file mode 100644
index 0000000000..9762de3c96
--- /dev/null
+++ b/ui-ngx/src/typings/add-marker.d.ts
@@ -0,0 +1,12 @@
+import * as L from 'leaflet'
+
+declare module 'leaflet' {
+
+ namespace Control {
+ class AddMarker extends L.Control { }
+ }
+
+ namespace control {
+ function addMarker(options): Control.AddMarker;
+ }
+}
\ No newline at end of file
diff --git a/ui-ngx/tsconfig.json b/ui-ngx/tsconfig.json
index a35b8a413f..8e1880e048 100644
--- a/ui-ngx/tsconfig.json
+++ b/ui-ngx/tsconfig.json
@@ -19,7 +19,8 @@
"src/typings/jquery.typings.d.ts",
"src/typings/jquery.flot.typings.d.ts",
"src/typings/jquery.jstree.typings.d.ts",
- "src/typings/split.js.typings.d.ts"
+ "src/typings/split.js.typings.d.ts",
+ "src/typings/add-marker.d.ts"
],
"paths": {
"@app/*": ["src/app/*"],
@@ -33,6 +34,7 @@
},
"lib": [
"es2018",
+ "es2019",
"dom"
]
}
diff --git a/ui/src/app/widget/lib/map-widget2.js b/ui/src/app/widget/lib/map-widget2.js
index bf300630cc..3fc4084835 100644
--- a/ui/src/app/widget/lib/map-widget2.js
+++ b/ui/src/app/widget/lib/map-widget2.js
@@ -433,7 +433,7 @@ export default class TbMapWidgetV2 {
function calculateLocationColor(location, dataMap) {
if (location.settings.useColorFunction && location.settings.colorFunction) {
var color;
- try {
+ try {
color = location.settings.colorFunction(dataMap.dataMap, dataMap.dsDataMap, location.dsIndex);
} catch (e) {/**/
}
@@ -450,7 +450,7 @@ export default class TbMapWidgetV2 {
if (location.settings.usePolygonColorFunction && location.settings.polygonColorFunction) {
var color;
try {
- color = location.settings.polygonColorFunction(dataMap.dataMap, dataMap.dsDataMap, location.dsIndex);
+ color = location.settings.polygonColorFunction(dataMap.dataMap, dataMap.dsDataMap, location.dsIndex);
} catch (e) {/**/
}
if (!color) {
@@ -484,7 +484,7 @@ export default class TbMapWidgetV2 {
function calculateLocationMarkerImage(location, dataMap) {
if (location.settings.useMarkerImageFunction && location.settings.markerImageFunction) {
var image = null;
- try {
+ try {
image = location.settings.markerImageFunction(dataMap.dataMap, location.settings.markerImages, dataMap.dsDataMap, location.dsIndex);
} catch (e) {
image = null;