Commit 42c52bfdba14d846720fee003e33f57982469954

Authored by ArtemHalushko
Committed by GitHub
1 parent 1af55e84

Map/3.0 (#2535)

* add base map infrastructure

* add leaflet css

* add tencent map

* add google maps support

* added image map support

* refactor schemes

* here maps support && WIP on markers

* add simple marker suppor

* data update & polyline support

* map bouds support

* add some settings support

* add map provider select to settings

* labels support

* WIP on trip animation widget

* WIP on history control and route interpolation

* trip-animation map provider & custom markers

* comleted track marker & history controls

* add license headers

* label fix & tooltips support

* WIP on polygons

* marker dropping support

* add polygon support

* add label to trip animation

* WIP on tooltips

* lint anf typed leaflet AddMarker

* some typing and poly improvements

* add typing

* add marker creation

* update proxy

* save position fix

Co-authored-by: Artem Halushko <ahalushko@thingboards.io>
Co-authored-by: Adsumus <artemtv42@gmail.com>
Showing 44 changed files with 3926 additions and 54 deletions
... ... @@ -13,4 +13,4 @@
13 13 See the Apache Version 2.0 License for specific language governing permissions
14 14 and limitations under the License.
15 15 ***************************************************************************** */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:"<div></div>"}),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:'<section [formGroup]="attributesConfigForm" fxLayout="column">\n <mat-form-field fxFlex class="mat-block">\n <mat-label translate>attribute.attributes-scope</mat-label>\n <mat-select formControlName="scope" required>\n <mat-option *ngFor="let scope of attributeScopes" [value]="scope">\n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n</section>\n'}),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:'<section [formGroup]="timeseriesConfigForm" fxLayout="column">\n <mat-form-field fxFlex class="mat-block">\n <mat-label translate>tb.rulenode.default-ttl</mat-label>\n <input type="number" min="0" step="1" matInput formControlName="defaultTTL" required>\n <mat-error *ngIf="timeseriesConfigForm.get(\'defaultTTL\').hasError(\'required\')">\n {{ \'tb.rulenode.default-ttl-required\' | translate }}\n </mat-error>\n <mat-error *ngIf="timeseriesConfigForm.get(\'defaultTTL\').hasError(\'min\')">\n {{ \'tb.rulenode.min-default-ttl-message\' | translate }}\n </mat-error>\n </mat-form-field>\n</section>\n'}),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:'<section [formGroup]="rpcRequestConfigForm" fxLayout="column">\n <mat-form-field fxFlex class="mat-block">\n <mat-label translate>tb.rulenode.timeout-sec</mat-label>\n <input type="number" min="0" step="1" matInput formControlName="timeoutInSeconds" required>\n <mat-error *ngIf="rpcRequestConfigForm.get(\'timeoutInSeconds\').hasError(\'required\')">\n {{ \'tb.rulenode.timeout-required\' | translate }}\n </mat-error>\n <mat-error *ngIf="rpcRequestConfigForm.get(\'timeoutInSeconds\').hasError(\'min\')">\n {{ \'tb.rulenode.min-timeout-message\' | translate }}\n </mat-error>\n </mat-form-field>\n</section>\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:'<section [formGroup]="logConfigForm" fxLayout="column">\n <label translate class="tb-title no-padding">tb.rulenode.to-string</label>\n <tb-js-func #jsFuncComponent\n formControlName="jsScript"\n functionName="ToString"\n [functionArgs]="[\'msg\', \'metadata\', \'msgType\']"\n noValidate="true">\n </tb-js-func>\n <div fxLayout="row">\n <button mat-button mat-raised-button color="primary" (click)="testScript()">\n {{ \'tb.rulenode.test-to-string-function\' | translate }}\n </button>\n </div>\n</section>\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:'<section [formGroup]="assignCustomerConfigForm" fxLayout="column">\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.customer-name-pattern</mat-label>\n <input required matInput formControlName="customerNamePattern">\n <mat-error *ngIf="assignCustomerConfigForm.get(\'customerNamePattern\').hasError(\'required\')">\n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.customer-name-pattern-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n <mat-checkbox fxFlex formControlName="createCustomerIfNotExists" style="padding-bottom: 16px;">\n {{ \'tb.rulenode.create-customer-if-not-exists\' | translate }}\n </mat-checkbox>\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.customer-cache-expiration</mat-label>\n <input required type="number" min="0" step="1" matInput formControlName="customerCacheExpiration">\n <mat-error *ngIf="assignCustomerConfigForm.get(\'customerCacheExpiration\').hasError(\'required\')">\n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n </mat-error>\n <mat-error *ngIf="assignCustomerConfigForm.get(\'customerCacheExpiration\').hasError(\'min\')">\n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.customer-cache-expiration-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n</section>\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:'<section [formGroup]="clearAlarmConfigForm" fxLayout="column">\n <label translate class="tb-title no-padding">tb.rulenode.alarm-details-builder</label>\n <tb-js-func #jsFuncComponent\n formControlName="alarmDetailsBuildJs"\n functionName="Details"\n [functionArgs]="[\'msg\', \'metadata\', \'msgType\']"\n noValidate="true">\n </tb-js-func>\n <div fxLayout="row" style="padding-bottom: 16px;">\n <button mat-button mat-raised-button color="primary" (click)="testScript()">\n {{ \'tb.rulenode.test-details-function\' | translate }}\n </button>\n </div>\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.alarm-type</mat-label>\n <input required matInput formControlName="alarmType">\n <mat-error *ngIf="clearAlarmConfigForm.get(\'alarmType\').hasError(\'required\')">\n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.entity-type-pattern-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n</section>\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:'<section [formGroup]="createAlarmConfigForm" fxLayout="column">\n <label translate class="tb-title no-padding">tb.rulenode.alarm-details-builder</label>\n <tb-js-func #jsFuncComponent\n formControlName="alarmDetailsBuildJs"\n functionName="Details"\n [functionArgs]="[\'msg\', \'metadata\', \'msgType\']"\n noValidate="true">\n </tb-js-func>\n <div fxLayout="row" style="padding-bottom: 16px;">\n <button mat-button mat-raised-button color="primary" (click)="testScript()">\n {{ \'tb.rulenode.test-details-function\' | translate }}\n </button>\n </div>\n <mat-checkbox formControlName="useMessageAlarmData" style="padding-bottom: 16px;">\n {{ \'tb.rulenode.use-message-alarm-data\' | translate }}\n </mat-checkbox>\n <section fxLayout="column" *ngIf="createAlarmConfigForm.get(\'useMessageAlarmData\').value === false">\n <section fxLayout="column" fxLayout.gt-sm="row" fxLayoutGap.gt-sm="8px">\n <mat-form-field fxFlex class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.alarm-type</mat-label>\n <input required matInput formControlName="alarmType">\n <mat-error *ngIf="createAlarmConfigForm.get(\'alarmType\').hasError(\'required\')">\n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.entity-type-pattern-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n <mat-form-field fxFlex class="mat-block">\n <mat-label translate>tb.rulenode.alarm-severity</mat-label>\n <mat-select formControlName="severity" required>\n <mat-option *ngFor="let severity of alarmSeverities" [value]="severity">\n {{ alarmSeverityTranslationMap.get(severity) | translate }}\n </mat-option>\n </mat-select>\n <mat-error *ngIf="createAlarmConfigForm.get(\'severity\').hasError(\'required\')">\n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n </mat-error>\n </mat-form-field>\n </section>\n <mat-checkbox formControlName="propagate" style="padding-bottom: 16px;">\n {{ \'tb.rulenode.propagate\' | translate }}\n </mat-checkbox>\n <section *ngIf="createAlarmConfigForm.get(\'propagate\').value === true">\n <mat-form-field floatLabel="always" class="mat-block">\n <mat-label translate>tb.rulenode.relation-types-list</mat-label>\n <mat-chip-list #relationTypesChipList>\n <mat-chip\n *ngFor="let key of createAlarmConfigForm.get(\'relationTypes\').value;"\n (removed)="removeKey(key, \'relationTypes\')">\n {{key}}\n <mat-icon matChipRemove>close</mat-icon>\n </mat-chip>\n <input matInput type="text" placeholder="{{\'tb.rulenode.relation-types-list\' | translate}}"\n style="max-width: 200px;"\n [matChipInputFor]="relationTypesChipList"\n [matChipInputSeparatorKeyCodes]="separatorKeysCodes"\n (matChipInputTokenEnd)="addKey($event, \'relationTypes\')"\n [matChipInputAddOnBlur]="true">\n </mat-chip-list>\n <mat-hint innerHTML="{{ \'tb.rulenode.relation-types-list-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n </section>\n </section>\n</section>\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:'<section [formGroup]="createRelationConfigForm" fxLayout="column">\n <mat-form-field class="mat-block" style="min-width: 100px;">\n <mat-label translate>relation.direction</mat-label>\n <mat-select required matInput formControlName="direction">\n <mat-option *ngFor="let type of directionTypes" [value]="type">\n {{ directionTypeTranslations.get(type) | translate }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <div fxLayout="row" fxLayoutGap="8px">\n <tb-entity-type-select\n showLabel\n style="min-width: 100px;"\n required\n formControlName="entityType">\n </tb-entity-type-select>\n <mat-form-field *ngIf="createRelationConfigForm.get(\'entityType\').value" fxFlex class="mat-block" style="padding-bottom: 32px;">\n <mat-label translate>tb.rulenode.entity-name-pattern</mat-label>\n <input required matInput formControlName="entityNamePattern">\n <mat-error *ngIf="createRelationConfigForm.get(\'entityNamePattern\').hasError(\'required\')">\n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.entity-name-pattern-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n <mat-form-field *ngIf="createRelationConfigForm.get(\'entityType\').value === entityType.DEVICE ||\n createRelationConfigForm.get(\'entityType\').value === entityType.ASSET"\n fxFlex class="mat-block" style="padding-bottom: 32px;">\n <mat-label translate>tb.rulenode.entity-type-pattern</mat-label>\n <input required matInput formControlName="entityTypePattern">\n <mat-error *ngIf="createRelationConfigForm.get(\'entityTypePattern\').hasError(\'required\')">\n {{ \'tb.rulenode.entity-type-pattern-required\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.entity-type-pattern-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n </div>\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.relation-type-pattern</mat-label>\n <input required matInput formControlName="relationType">\n <mat-error *ngIf="createRelationConfigForm.get(\'relationType\').hasError(\'required\')">\n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.relation-type-pattern-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n <section *ngIf="createRelationConfigForm.get(\'entityType\').value === entityType.CUSTOMER ||\n createRelationConfigForm.get(\'entityType\').value === entityType.DEVICE ||\n createRelationConfigForm.get(\'entityType\').value === entityType.ASSET">\n <mat-checkbox formControlName="createEntityIfNotExists">\n {{ \'tb.rulenode.create-entity-if-not-exists\' | translate }}\n </mat-checkbox>\n <div class="tb-hint" style="padding-bottom: 16px;" translate>tb.rulenode.create-entity-if-not-exists-hint</div>\n </section>\n <mat-checkbox formControlName="removeCurrentRelations">\n {{ \'tb.rulenode.remove-current-relations\' | translate }}\n </mat-checkbox>\n <div class="tb-hint" style="padding-bottom: 16px;" translate>tb.rulenode.remove-current-relations-hint</div>\n <mat-checkbox formControlName="changeOriginatorToRelatedEntity">\n {{ \'tb.rulenode.change-originator-to-related-entity\' | translate }}\n </mat-checkbox>\n <div class="tb-hint" style="padding-bottom: 16px;" translate>tb.rulenode.change-originator-to-related-entity-hint</div>\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.entity-cache-expiration</mat-label>\n <input required type="number" min="0" step="1" matInput formControlName="entityCacheExpiration">\n <mat-error *ngIf="createRelationConfigForm.get(\'entityCacheExpiration\').hasError(\'required\')">\n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n </mat-error>\n <mat-error *ngIf="createRelationConfigForm.get(\'entityCacheExpiration\').hasError(\'min\')">\n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.entity-cache-expiration-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n</section>\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:'<section [formGroup]="msgDelayConfigForm" fxLayout="column">\n <mat-checkbox formControlName="useMetadataPeriodInSecondsPatterns">\n {{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }}\n </mat-checkbox>\n <div class="tb-hint" style="padding-bottom: 16px;" translate>tb.rulenode.use-metadata-period-in-seconds-patterns-hint</div>\n <mat-form-field *ngIf="msgDelayConfigForm.get(\'useMetadataPeriodInSecondsPatterns\').value !== true; else periodInSecondsPattern"\n class="mat-block">\n <mat-label translate>tb.rulenode.period-seconds</mat-label>\n <input required type="number" min="0" step="1" matInput formControlName="periodInSeconds">\n <mat-error *ngIf="msgDelayConfigForm.get(\'periodInSeconds\').hasError(\'required\')">\n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n </mat-error>\n <mat-error *ngIf="msgDelayConfigForm.get(\'periodInSeconds\').hasError(\'min\')">\n {{ \'tb.rulenode.min-period-0-seconds-message\' | translate }}\n </mat-error>\n </mat-form-field>\n <ng-template #periodInSecondsPattern>\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.period-in-seconds-pattern</mat-label>\n <input required matInput formControlName="periodInSecondsPattern">\n <mat-error *ngIf="msgDelayConfigForm.get(\'periodInSecondsPattern\').hasError(\'required\')">\n {{ \'tb.rulenode.period-in-seconds-pattern-required\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.period-in-seconds-pattern-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n </ng-template>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.max-pending-messages</mat-label>\n <input required type="number" min="1" max="100000" step="1" matInput formControlName="maxPendingMsgs">\n <mat-error *ngIf="msgDelayConfigForm.get(\'maxPendingMsgs\').hasError(\'required\')">\n {{ \'tb.rulenode.max-pending-messages-required\' | translate }}\n </mat-error>\n <mat-error *ngIf="msgDelayConfigForm.get(\'maxPendingMsgs\').hasError(\'min\')">\n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n </mat-error>\n <mat-error *ngIf="msgDelayConfigForm.get(\'maxPendingMsgs\').hasError(\'max\')">\n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n </mat-error>\n </mat-form-field>\n</section>\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:'<section [formGroup]="deleteRelationConfigForm" fxLayout="column">\n <mat-checkbox formControlName="deleteForSingleEntity">\n {{ \'tb.rulenode.delete-relation-to-specific-entity\' | translate }}\n </mat-checkbox>\n <div class="tb-hint" style="padding-bottom: 16px;" translate>tb.rulenode.delete-relation-hint</div>\n <mat-form-field class="mat-block" style="min-width: 100px;">\n <mat-label translate>relation.direction</mat-label>\n <mat-select required matInput formControlName="direction">\n <mat-option *ngFor="let type of directionTypes" [value]="type">\n {{ directionTypeTranslations.get(type) | translate }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <div *ngIf="deleteRelationConfigForm.get(\'deleteForSingleEntity\').value" fxLayout="row" fxLayoutGap="8px">\n <tb-entity-type-select\n showLabel\n style="min-width: 100px;"\n required\n formControlName="entityType">\n </tb-entity-type-select>\n <mat-form-field *ngIf="deleteRelationConfigForm.get(\'entityType\').value" fxFlex class="mat-block" style="padding-bottom: 32px;">\n <mat-label translate>tb.rulenode.entity-name-pattern</mat-label>\n <input required matInput formControlName="entityNamePattern">\n <mat-error *ngIf="deleteRelationConfigForm.get(\'entityNamePattern\').hasError(\'required\')">\n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.entity-name-pattern-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n </div>\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.relation-type-pattern</mat-label>\n <input required matInput formControlName="relationType">\n <mat-error *ngIf="deleteRelationConfigForm.get(\'relationType\').hasError(\'required\')">\n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.relation-type-pattern-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.entity-cache-expiration</mat-label>\n <input required type="number" min="0" step="1" matInput formControlName="entityCacheExpiration">\n <mat-error *ngIf="deleteRelationConfigForm.get(\'entityCacheExpiration\').hasError(\'required\')">\n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n </mat-error>\n <mat-error *ngIf="deleteRelationConfigForm.get(\'entityCacheExpiration\').hasError(\'min\')">\n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.entity-cache-expiration-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n</section>\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:'<section [formGroup]="generatorConfigForm" fxLayout="column">\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.message-count</mat-label>\n <input required type="number" min="0" step="1" matInput formControlName="msgCount">\n <mat-error *ngIf="generatorConfigForm.get(\'msgCount\').hasError(\'required\')">\n {{ \'tb.rulenode.message-count-required\' | translate }}\n </mat-error>\n <mat-error *ngIf="generatorConfigForm.get(\'msgCount\').hasError(\'min\')">\n {{ \'tb.rulenode.min-message-count-message\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.period-seconds</mat-label>\n <input required type="number" min="1" step="1" matInput formControlName="periodInSeconds">\n <mat-error *ngIf="generatorConfigForm.get(\'periodInSeconds\').hasError(\'required\')">\n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n </mat-error>\n <mat-error *ngIf="generatorConfigForm.get(\'periodInSeconds\').hasError(\'min\')">\n {{ \'tb.rulenode.min-period-seconds-message\' | translate }}\n </mat-error>\n </mat-form-field>\n <div fxLayout="column">\n <label class="tb-small">{{ \'tb.rulenode.originator\' | translate }}</label>\n <tb-entity-select\n required="false"\n formControlName="originator">\n </tb-entity-select>\n </div>\n <label translate class="tb-title no-padding">tb.rulenode.generate</label>\n <tb-js-func #jsFuncComponent\n formControlName="jsScript"\n functionName="Generate"\n [functionArgs]="[\'prevMsg\', \'prevMetadata\', \'prevMsgType\']"\n noValidate="true">\n </tb-js-func>\n <div fxLayout="row" style="padding-bottom: 16px;">\n <button mat-button mat-raised-button color="primary" (click)="testScript()">\n {{ \'tb.rulenode.test-generator-function\' | translate }}\n </button>\n </div>\n</section>\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:'<section [formGroup]="geoActionConfigForm" fxLayout="column">\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.latitude-key-name</mat-label>\n <input matInput formControlName="latitudeKeyName" required>\n <mat-error *ngIf="geoActionConfigForm.get(\'latitudeKeyName\').hasError(\'required\')">\n {{ \'tb.rulenode.latitude-key-name-required\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.longitude-key-name</mat-label>\n <input matInput formControlName="longitudeKeyName" required>\n <mat-error *ngIf="geoActionConfigForm.get(\'longitudeKeyName\').hasError(\'required\')">\n {{ \'tb.rulenode.longitude-key-name-required\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-checkbox fxFlex formControlName="fetchPerimeterInfoFromMessageMetadata" style="padding-bottom: 16px;">\n {{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}\n </mat-checkbox>\n <div fxLayout="row" *ngIf="!geoActionConfigForm.get(\'fetchPerimeterInfoFromMessageMetadata\').value">\n <mat-form-field fxFlex class="mat-block">\n <mat-label translate>tb.rulenode.perimeter-type</mat-label>\n <mat-select formControlName="perimeterType" required>\n <mat-option *ngFor="let type of perimeterTypes" [value]="type">\n {{ perimeterTypeTranslationMap.get(type) | translate }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div fxLayout="column"\n *ngIf="geoActionConfigForm.get(\'perimeterType\').value === perimeterType.CIRCLE &&\n !geoActionConfigForm.get(\'fetchPerimeterInfoFromMessageMetadata\').value">\n <div fxLayout="row" fxLayoutGap="8px">\n <mat-form-field fxFlex>\n <mat-label translate>tb.rulenode.circle-center-latitude</mat-label>\n <input type="number" min="-90" max="90" step="0.1" matInput formControlName="centerLatitude" required>\n <mat-error *ngIf="geoActionConfigForm.get(\'centerLatitude\').hasError(\'required\')">\n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field fxFlex>\n <mat-label translate>tb.rulenode.circle-center-longitude</mat-label>\n <input type="number" min="-180" max="180" step="0.1" matInput formControlName="centerLongitude" required>\n <mat-error *ngIf="geoActionConfigForm.get(\'centerLongitude\').hasError(\'required\')">\n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n </mat-error>\n </mat-form-field>\n </div>\n <div fxLayout="row" fxLayoutGap="8px">\n <mat-form-field fxFlex>\n <mat-label translate>tb.rulenode.range</mat-label>\n <input type="number" min="0" step="0.1" matInput formControlName="range" required>\n <mat-error *ngIf="geoActionConfigForm.get(\'range\').hasError(\'required\')">\n {{ \'tb.rulenode.range-required\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field fxFlex>\n <mat-label translate>tb.rulenode.range-units</mat-label>\n <mat-select formControlName="rangeUnit" required>\n <mat-option *ngFor="let type of rangeUnits" [value]="type">\n {{ rangeUnitTranslationMap.get(type) | translate }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div fxLayout="column" *ngIf="geoActionConfigForm.get(\'perimeterType\').value === perimeterType.POLYGON &&\n !geoActionConfigForm.get(\'fetchPerimeterInfoFromMessageMetadata\').value">\n <mat-form-field class="mat-block" hintLabel="{{\'tb.rulenode.polygon-definition-hint\' | translate}}" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.polygon-definition</mat-label>\n <input matInput formControlName="polygonsDefinition" required>\n <mat-error *ngIf="geoActionConfigForm.get(\'polygonsDefinition\').hasError(\'required\')">\n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n </mat-error>\n </mat-form-field>\n </div>\n <div fxLayout="column" fxLayout.gt-sm="row" fxLayoutGap.gt-sm="8px">\n <mat-form-field fxFlex class="mat-block">\n <mat-label translate>tb.rulenode.min-inside-duration</mat-label>\n <input type="number" step="1" min="1" max="2147483647" matInput formControlName="minInsideDuration" required>\n <mat-error *ngIf="geoActionConfigForm.get(\'minInsideDuration\').hasError(\'required\')">\n {{ \'tb.rulenode.min-inside-duration-value-required\' | translate }}\n </mat-error>\n <mat-error *ngIf="geoActionConfigForm.get(\'minInsideDuration\').hasError(\'min\')">\n {{ \'tb.rulenode.time-value-range\' | translate }}\n </mat-error>\n <mat-error *ngIf="geoActionConfigForm.get(\'minInsideDuration\').hasError(\'max\')">\n {{ \'tb.rulenode.time-value-range\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field fxFlex class="mat-block">\n <mat-label translate>tb.rulenode.min-inside-duration-time-unit</mat-label>\n <mat-select formControlName="minInsideDurationTimeUnit" required>\n <mat-option *ngFor="let timeUnit of timeUnits" [value]="timeUnit">\n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div fxLayout="column" fxLayout.gt-sm="row" fxLayoutGap.gt-sm="8px">\n <mat-form-field fxFlex class="mat-block">\n <mat-label translate>tb.rulenode.min-outside-duration</mat-label>\n <input type="number" step="1" min="1" max="2147483647" matInput formControlName="minOutsideDuration" required>\n <mat-error *ngIf="geoActionConfigForm.get(\'minOutsideDuration\').hasError(\'required\')">\n {{ \'tb.rulenode.min-outside-duration-value-required\' | translate }}\n </mat-error>\n <mat-error *ngIf="geoActionConfigForm.get(\'minOutsideDuration\').hasError(\'min\')">\n {{ \'tb.rulenode.time-value-range\' | translate }}\n </mat-error>\n <mat-error *ngIf="geoActionConfigForm.get(\'minOutsideDuration\').hasError(\'max\')">\n {{ \'tb.rulenode.time-value-range\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field fxFlex class="mat-block">\n <mat-label translate>tb.rulenode.min-outside-duration-time-unit</mat-label>\n <mat-select formControlName="minOutsideDurationTimeUnit" required>\n <mat-option *ngFor="let timeUnit of timeUnits" [value]="timeUnit">\n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n</section>\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:'<section [formGroup]="msgCountConfigForm" fxLayout="column">\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.interval-seconds</mat-label>\n <input required type="number" min="1" step="1" matInput formControlName="interval">\n <mat-error *ngIf="msgCountConfigForm.get(\'interval\').hasError(\'required\')">\n {{ \'tb.rulenode.interval-seconds-required\' | translate }}\n </mat-error>\n <mat-error *ngIf="msgCountConfigForm.get(\'interval\').hasError(\'min\')">\n {{ \'tb.rulenode.min-interval-seconds-message\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.output-timeseries-key-prefix</mat-label>\n <input required matInput formControlName="telemetryPrefix">\n <mat-error *ngIf="msgCountConfigForm.get(\'telemetryPrefix\').hasError(\'required\')">\n {{ \'tb.rulenode.output-timeseries-key-prefix-required\' | translate }}\n </mat-error>\n </mat-form-field>\n</section>\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:'<section [formGroup]="rpcReplyConfigForm" fxLayout="column">\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.request-id-metadata-attribute</mat-label>\n <input matInput formControlName="requestIdMetaDataAttribute">\n </mat-form-field>\n</section>\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:'<section [formGroup]="saveToCustomTableConfigForm" fxLayout="column">\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.custom-table-name</mat-label>\n <input required matInput formControlName="tableName">\n <mat-error *ngIf="saveToCustomTableConfigForm.get(\'tableName\').hasError(\'required\')">\n {{ \'tb.rulenode.custom-table-name-required\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.custom-table-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n <label translate class="tb-title tb-required">tb.rulenode.fields-mapping</label>\n <tb-kv-map-config\n required\n formControlName="fieldsMapping"\n requiredText="tb.rulenode.fields-mapping-required"\n keyText="tb.rulenode.message-field"\n keyRequiredText="tb.rulenode.message-field-required"\n valText="tb.rulenode.table-col"\n valRequiredText="tb.rulenode.table-col-required">\n </tb-kv-map-config>\n</section>\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:'<section fxLayout="column" class="tb-kv-map-config" [formGroup]="kvListFormGroup">\n <div class="header" fxFlex fxLayout="row" fxLayoutGap="8px">\n <span class="cell" fxFlex translate>{{ keyText }}</span>\n <span class="cell" fxFlex translate>{{ valText }}</span>\n <span [fxShow]="!disabled" style="width: 52px;" innerHTML="&nbsp"></span>\n </div>\n <div class="body">\n <div class="row" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"\n formArrayName="keyVals"\n *ngFor="let keyValControl of keyValsFormArray().controls; let $index = index">\n <mat-form-field fxFlex floatLabel="always" hideRequiredMarker class="cell mat-block">\n <mat-label></mat-label>\n <input [formControl]="keyValControl.get(\'key\')" matInput required\n placeholder="{{ keyText | translate }}"/>\n <mat-error *ngIf="keyValControl.get(\'key\').hasError(\'required\')">\n {{ keyRequiredText | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field fxFlex floatLabel="always" hideRequiredMarker class="cell mat-block">\n <mat-label></mat-label>\n <input [formControl]="keyValControl.get(\'value\')" matInput required\n placeholder="{{ valText | translate }}"/>\n <mat-error *ngIf="keyValControl.get(\'value\').hasError(\'required\')">\n {{ valRequiredText | translate }}\n </mat-error>\n </mat-form-field>\n <button mat-button mat-icon-button color="primary"\n [fxShow]="!disabled"\n type="button"\n (click)="removeKeyVal($index)"\n [disabled]="isLoading$ | async"\n matTooltip="{{ \'tb.key-val.remove-entry\' | translate }}"\n matTooltipPosition="above">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n <tb-error [error]="ngControl.hasError(\'kvMapRequired\')\n ? translate.instant(requiredText) : \'\'"></tb-error>\n <div style="margin-top: 8px;">\n <button mat-button mat-raised-button color="primary"\n [fxShow]="!disabled"\n [disabled]="isLoading$ | async"\n (click)="addKeyVal()"\n type="button"\n matTooltip="{{ \'tb.key-val.add-entry\' | translate }}"\n matTooltipPosition="above">\n <mat-icon>add</mat-icon>\n {{ \'action.add\' | translate }}\n </button>\n </div>\n</section>\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:'<section fxLayout="column" [formGroup]="deviceRelationsQueryFormGroup">\n <mat-checkbox formControlName="fetchLastLevelOnly" style="padding-bottom: 16px;">\n {{ \'relation.last-level-relation\' | translate }}\n </mat-checkbox>\n <div fxLayoutGap="8px" fxLayout="row">\n <mat-form-field class="mat-block" style="min-width: 100px;">\n <mat-label translate>relation.direction</mat-label>\n <mat-select required matInput formControlName="direction">\n <mat-option *ngFor="let type of directionTypes" [value]="type">\n {{ directionTypeTranslations.get(type) | translate }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field fxFlex floatLabel="always" class="mat-block">\n <mat-label translate>tb.rulenode.max-relation-level</mat-label>\n <input matInput\n type="number"\n min="1"\n step="1"\n placeholder="{{ \'tb.rulenode.unlimited-level\' | translate }}"\n formControlName="maxLevel">\n </mat-form-field>\n </div>\n <div class="mat-caption" style="color: rgba(0,0,0,0.57);" translate>relation.relation-type</div>\n <tb-relation-type-autocomplete\n fxFlex\n formControlName="relationType">\n </tb-relation-type-autocomplete>\n <div class="mat-caption tb-required" style="color: rgba(0,0,0,0.57);" translate>device.device-types</div>\n <tb-entity-subtype-list\n required\n [entityType]="entityType.DEVICE"\n formControlName="deviceTypes">\n </tb-entity-subtype-list>\n</section>\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:'<section fxLayout="column" [formGroup]="relationsQueryFormGroup">\n <mat-checkbox formControlName="fetchLastLevelOnly" style="padding-bottom: 16px;">\n {{ \'relation.last-level-relation\' | translate }}\n </mat-checkbox>\n <div fxLayoutGap="8px" fxLayout="row">\n <mat-form-field class="mat-block" style="min-width: 100px;">\n <mat-label translate>relation.direction</mat-label>\n <mat-select required matInput formControlName="direction">\n <mat-option *ngFor="let type of directionTypes" [value]="type">\n {{ directionTypeTranslations.get(type) | translate }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field fxFlex floatLabel="always" class="mat-block">\n <mat-label translate>tb.rulenode.max-relation-level</mat-label>\n <input matInput\n type="number"\n min="1"\n step="1"\n placeholder="{{ \'tb.rulenode.unlimited-level\' | translate }}"\n formControlName="maxLevel">\n </mat-form-field>\n </div>\n <div class="mat-caption" style="color: rgba(0,0,0,0.57);" translate>relation.relation-filters</div>\n <tb-relation-filters\n formControlName="filters"\n ></tb-relation-filters>\n</section>\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:'<mat-form-field [formGroup]="messageTypeConfigForm" style="width: 100%;">\n <mat-label *ngIf="label" translate>{{ label }}</mat-label>\n <mat-chip-list #chipList [required]="required">\n <mat-chip\n *ngFor="let messageType of messageTypes"\n [selectable]="true"\n [removable]="true"\n (removed)="remove(messageType)">\n {{messageType.name}}\n <mat-icon matChipRemove>close</mat-icon>\n </mat-chip>\n <input matInput type="text" placeholder="{{ placeholder | translate }}"\n style="max-width: 200px;"\n #messageTypeInput\n (focusin)="onFocus()"\n formControlName="messageType"\n matAutocompleteOrigin\n #origin="matAutocompleteOrigin"\n [matAutocompleteConnectedTo]="origin"\n [matAutocomplete]="messageTypeAutocomplete"\n [matChipInputFor]="chipList"\n [matChipInputSeparatorKeyCodes]="separatorKeysCodes"\n (matChipInputTokenEnd)="add($event)">\n </mat-chip-list>\n <mat-autocomplete #messageTypeAutocomplete="matAutocomplete"\n class="tb-autocomplete"\n (optionSelected)="selected($event)"\n [displayWith]="displayMessageTypeFn">\n <mat-option *ngFor="let messageType of filteredMessageTypes | async" [value]="messageType">\n <span [innerHTML]="messageType.name | highlight:searchText"></span>\n </mat-option>\n <mat-option *ngIf="(filteredMessageTypes | async)?.length === 0" [value]="null" class="tb-not-found">\n <div class="tb-not-found-content" (click)="$event.stopPropagation()">\n <div *ngIf="!textIsNotEmpty(searchText); else searchNotEmpty">\n <span translate>tb.rulenode.no-message-types-found</span>\n </div>\n <ng-template #searchNotEmpty>\n <span>\n {{ translate.get(\'tb.rulenode.no-message-type-matching\',\n {messageType: truncate.transform(searchText, true, 6, &apos;...&apos;)}) | async }}\n </span>\n </ng-template>\n <span>\n <a translate (click)="createMessageType($event, searchText)">tb.rulenode.create-new-message-type</a>\n </span>\n </div>\n </mat-option>\n </mat-autocomplete>\n <mat-error *ngIf="chipList.errorState">\n {{ \'tb.rulenode.message-types-required\' | translate }}\n </mat-error>\n</mat-form-field>\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:'<section [formGroup]="unassignCustomerConfigForm" fxLayout="column">\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.customer-name-pattern</mat-label>\n <input required matInput formControlName="customerNamePattern">\n <mat-error *ngIf="unassignCustomerConfigForm.get(\'customerNamePattern\').hasError(\'required\')">\n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.customer-name-pattern-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.customer-cache-expiration</mat-label>\n <input required type="number" min="0" step="1" matInput formControlName="customerCacheExpiration">\n <mat-error *ngIf="unassignCustomerConfigForm.get(\'customerCacheExpiration\').hasError(\'required\')">\n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n </mat-error>\n <mat-error *ngIf="unassignCustomerConfigForm.get(\'customerCacheExpiration\').hasError(\'min\')">\n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.customer-cache-expiration-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n</section>\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:'<section [formGroup]="snsConfigForm" fxLayout="column">\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.topic-arn-pattern</mat-label>\n <input required matInput formControlName="topicArnPattern">\n <mat-error *ngIf="snsConfigForm.get(\'topicArnPattern\').hasError(\'required\')">\n {{ \'tb.rulenode.topic-arn-pattern-required\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.topic-arn-pattern-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.aws-access-key-id</mat-label>\n <input required matInput formControlName="accessKeyId">\n <mat-error *ngIf="snsConfigForm.get(\'accessKeyId\').hasError(\'required\')">\n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.aws-secret-access-key</mat-label>\n <input required matInput formControlName="secretAccessKey">\n <mat-error *ngIf="snsConfigForm.get(\'secretAccessKey\').hasError(\'required\')">\n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.aws-region</mat-label>\n <input required matInput formControlName="region">\n <mat-error *ngIf="snsConfigForm.get(\'region\').hasError(\'required\')">\n {{ \'tb.rulenode.aws-region-required\' | translate }}\n </mat-error>\n </mat-form-field>\n</section>\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:'<section [formGroup]="sqsConfigForm" fxLayout="column">\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.queue-type</mat-label>\n <mat-select formControlName="queueType" required>\n <mat-option *ngFor="let type of sqsQueueTypes" [value]="type">\n {{ sqsQueueTypeTranslationsMap.get(type) | translate }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.queue-url-pattern</mat-label>\n <input required matInput formControlName="queueUrlPattern">\n <mat-error *ngIf="sqsConfigForm.get(\'queueUrlPattern\').hasError(\'required\')">\n {{ \'tb.rulenode.queue-url-pattern-required\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.queue-url-pattern-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n <mat-form-field *ngIf="sqsConfigForm.get(\'queueType\').value === sqsQueueType.STANDARD" class="mat-block">\n <mat-label translate>tb.rulenode.delay-seconds</mat-label>\n <input required type="number" min="0" max="900" step="1" matInput formControlName="delaySeconds">\n <mat-error *ngIf="sqsConfigForm.get(\'delaySeconds\').hasError(\'min\')">\n {{ \'tb.rulenode.min-delay-seconds-message\' | translate }}\n </mat-error>\n <mat-error *ngIf="sqsConfigForm.get(\'delaySeconds\').hasError(\'max\')">\n {{ \'tb.rulenode.max-delay-seconds-message\' | translate }}\n </mat-error>\n </mat-form-field>\n <label translate class="tb-title">tb.rulenode.message-attributes</label>\n <div class="tb-hint" translate>tb.rulenode.message-attributes-hint</div>\n <tb-kv-map-config\n required="false"\n formControlName="messageAttributes"\n keyText="tb.rulenode.name"\n keyRequiredText="tb.rulenode.name-required"\n valText="tb.rulenode.value"\n valRequiredText="tb.rulenode.value-required">\n </tb-kv-map-config>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.aws-access-key-id</mat-label>\n <input required matInput formControlName="accessKeyId">\n <mat-error *ngIf="sqsConfigForm.get(\'accessKeyId\').hasError(\'required\')">\n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.aws-secret-access-key</mat-label>\n <input required matInput formControlName="secretAccessKey">\n <mat-error *ngIf="sqsConfigForm.get(\'secretAccessKey\').hasError(\'required\')">\n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.aws-region</mat-label>\n <input required matInput formControlName="region">\n <mat-error *ngIf="sqsConfigForm.get(\'region\').hasError(\'required\')">\n {{ \'tb.rulenode.aws-region-required\' | translate }}\n </mat-error>\n </mat-form-field>\n</section>\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:'<section [formGroup]="pubSubConfigForm" fxLayout="column">\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.gcp-project-id</mat-label>\n <input required matInput formControlName="projectId">\n <mat-error *ngIf="pubSubConfigForm.get(\'projectId\').hasError(\'required\')">\n {{ \'tb.rulenode.gcp-project-id-required\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.pubsub-topic-name</mat-label>\n <input required matInput formControlName="topicName">\n <mat-error *ngIf="pubSubConfigForm.get(\'topicName\').hasError(\'required\')">\n {{ \'tb.rulenode.pubsub-topic-name-required\' | translate }}\n </mat-error>\n </mat-form-field>\n <tb-file-input style="padding-bottom: 24px;"\n formControlName="serviceAccountKey"\n [existingFileName]="pubSubConfigForm.get(\'serviceAccountKeyFileName\').value"\n (fileNameChanged)="pubSubConfigForm.get(\'serviceAccountKeyFileName\').setValue($event)"\n required\n requiredAsError\n label="{{\'tb.rulenode.gcp-service-account-key\' | translate}}"\n noFileText="tb.rulenode.no-file"\n dropLabel="{{\'tb.rulenode.drop-file\' | translate}}">\n </tb-file-input>\n <label translate class="tb-title">tb.rulenode.message-attributes</label>\n <div class="tb-hint" translate>tb.rulenode.message-attributes-hint</div>\n <tb-kv-map-config\n required="false"\n formControlName="messageAttributes"\n keyText="tb.rulenode.name"\n keyRequiredText="tb.rulenode.name-required"\n valText="tb.rulenode.value"\n valRequiredText="tb.rulenode.value-required">\n </tb-kv-map-config>\n</section>\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:'<section [formGroup]="kafkaConfigForm" fxLayout="column">\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.topic-pattern</mat-label>\n <input required matInput formControlName="topicPattern">\n <mat-error *ngIf="kafkaConfigForm.get(\'topicPattern\').hasError(\'required\')">\n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.bootstrap-servers</mat-label>\n <input required matInput formControlName="bootstrapServers">\n <mat-error *ngIf="kafkaConfigForm.get(\'bootstrapServers\').hasError(\'required\')">\n {{ \'tb.rulenode.bootstrap-servers-required\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.retries</mat-label>\n <input type="number" step="1" min="0" matInput formControlName="retries">\n <mat-error *ngIf="kafkaConfigForm.get(\'retries\').hasError(\'min\')">\n {{ \'tb.rulenode.min-retries-message\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.batch-size-bytes</mat-label>\n <input type="number" step="1" min="0" matInput formControlName="batchSize">\n <mat-error *ngIf="kafkaConfigForm.get(\'batchSize\').hasError(\'min\')">\n {{ \'tb.rulenode.min-batch-size-bytes-message\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.linger-ms</mat-label>\n <input type="number" step="1" min="0" matInput formControlName="linger">\n <mat-error *ngIf="kafkaConfigForm.get(\'linger\').hasError(\'min\')">\n {{ \'tb.rulenode.min-linger-ms-message\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.buffer-memory-bytes</mat-label>\n <input type="number" step="1" min="0" matInput formControlName="bufferMemory">\n <mat-error *ngIf="kafkaConfigForm.get(\'bufferMemory\').hasError(\'min\')">\n {{ \'tb.rulenode.min-buffer-memory-bytes-message\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.acks</mat-label>\n <mat-select formControlName="acks" required>\n <mat-option *ngFor="let ackValue of ackValues" [value]="ackValue">\n {{ ackValue }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.key-serializer</mat-label>\n <input required matInput formControlName="keySerializer">\n <mat-error *ngIf="kafkaConfigForm.get(\'keySerializer\').hasError(\'required\')">\n {{ \'tb.rulenode.key-serializer-required\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.value-serializer</mat-label>\n <input required matInput formControlName="valueSerializer">\n <mat-error *ngIf="kafkaConfigForm.get(\'valueSerializer\').hasError(\'required\')">\n {{ \'tb.rulenode.value-serializer-required\' | translate }}\n </mat-error>\n </mat-form-field>\n <label translate class="tb-title">tb.rulenode.other-properties</label>\n <tb-kv-map-config\n required="false"\n formControlName="otherProperties"\n keyText="tb.rulenode.key"\n keyRequiredText="tb.rulenode.key-required"\n valText="tb.rulenode.value"\n valRequiredText="tb.rulenode.value-required">\n </tb-kv-map-config>\n</section>\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:'<section [formGroup]="mqttConfigForm" fxLayout="column">\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.topic-pattern</mat-label>\n <input required matInput formControlName="topicPattern">\n <mat-error *ngIf="mqttConfigForm.get(\'topicPattern\').hasError(\'required\')">\n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.mqtt-topic-pattern-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n <div fxFlex fxLayout="column" fxLayout.gt-sm="row" fxLayoutGap.gt-sm="8px">\n <mat-form-field fxFlex="60" class="mat-block">\n <mat-label translate>tb.rulenode.host</mat-label>\n <input required matInput formControlName="host">\n <mat-error *ngIf="mqttConfigForm.get(\'host\').hasError(\'required\')">\n {{ \'tb.rulenode.host-required\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field fxFlex="40" class="mat-block">\n <mat-label translate>tb.rulenode.port</mat-label>\n <input required type="number" step="1" min="1" max="65535" matInput formControlName="port">\n <mat-error *ngIf="mqttConfigForm.get(\'port\').hasError(\'required\')">\n {{ \'tb.rulenode.port-required\' | translate }}\n </mat-error>\n <mat-error *ngIf="mqttConfigForm.get(\'port\').hasError(\'min\')">\n {{ \'tb.rulenode.port-range\' | translate }}\n </mat-error>\n <mat-error *ngIf="mqttConfigForm.get(\'port\').hasError(\'max\')">\n {{ \'tb.rulenode.port-range\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field fxFlex="40" class="mat-block">\n <mat-label translate>tb.rulenode.connect-timeout</mat-label>\n <input required type="number" step="1" min="1" max="200" matInput formControlName="connectTimeoutSec">\n <mat-error *ngIf="mqttConfigForm.get(\'connectTimeoutSec\').hasError(\'required\')">\n {{ \'tb.rulenode.connect-timeout-required\' | translate }}\n </mat-error>\n <mat-error *ngIf="mqttConfigForm.get(\'connectTimeoutSec\').hasError(\'min\')">\n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n </mat-error>\n <mat-error *ngIf="mqttConfigForm.get(\'connectTimeoutSec\').hasError(\'max\')">\n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n </mat-error>\n </mat-form-field>\n </div>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.client-id</mat-label>\n <input matInput formControlName="clientId">\n </mat-form-field>\n <mat-checkbox formControlName="cleanSession" style="padding-bottom: 16px;">\n {{ \'tb.rulenode.clean-session\' | translate }}\n </mat-checkbox>\n <mat-checkbox formControlName="ssl" style="padding-bottom: 16px;">\n {{ \'tb.rulenode.enable-ssl\' | translate }}\n </mat-checkbox>\n <mat-expansion-panel class="tb-mqtt-credentials-panel-group">\n <mat-expansion-panel-header>\n <mat-panel-title translate>tb.rulenode.credentials</mat-panel-title>\n <mat-panel-description>\n {{ mqttCredentialsTypeTranslationsMap.get(mqttConfigForm.get(\'credentials\').get(\'type\').value) | translate }}\n </mat-panel-description>\n </mat-expansion-panel-header>\n <section formGroupName="credentials" fxLayout="column">\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.credentials-type</mat-label>\n <mat-select formControlName="type" required>\n <mat-option *ngFor="let credentialsType of allMqttCredentialsTypes" [value]="credentialsType">\n {{ mqttCredentialsTypeTranslationsMap.get(credentialsType) | translate }}\n </mat-option>\n </mat-select>\n <mat-error *ngIf="mqttConfigForm.get(\'credentials\').get(\'type\').hasError(\'required\')">\n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n </mat-error>\n </mat-form-field>\n <section fxLayout="column" [ngSwitch]="mqttConfigForm.get(\'credentials\').get(\'type\').value">\n <ng-template ngSwitchCase="anonymous">\n </ng-template>\n <ng-template ngSwitchCase="basic">\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.username</mat-label>\n <input required matInput formControlName="username">\n <mat-error *ngIf="mqttConfigForm.get(\'credentials\').get(\'username\').hasError(\'required\')">\n {{ \'tb.rulenode.username-required\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.password</mat-label>\n <input type="password" required matInput formControlName="password">\n <mat-error *ngIf="mqttConfigForm.get(\'credentials\').get(\'password\').hasError(\'required\')">\n {{ \'tb.rulenode.password-required\' | translate }}\n </mat-error>\n </mat-form-field>\n </ng-template>\n <ng-template ngSwitchCase="cert.PEM">\n <tb-file-input formControlName="caCert"\n inputId="caCertSelect"\n [existingFileName]="mqttConfigForm.get(\'credentials\').get(\'caCertFileName\').value"\n (fileNameChanged)="mqttConfigForm.get(\'credentials\').get(\'caCertFileName\').setValue($event)"\n required\n requiredAsError\n label="{{\'tb.rulenode.ca-cert\' | translate}}"\n noFileText="tb.rulenode.no-file"\n dropLabel="{{\'tb.rulenode.drop-file\' | translate}}">\n </tb-file-input>\n <tb-file-input formControlName="cert"\n inputId="CertSelect"\n [existingFileName]="mqttConfigForm.get(\'credentials\').get(\'certFileName\').value"\n (fileNameChanged)="mqttConfigForm.get(\'credentials\').get(\'certFileName\').setValue($event)"\n required\n requiredAsError\n label="{{\'tb.rulenode.cert\' | translate}}"\n noFileText="tb.rulenode.no-file"\n dropLabel="{{\'tb.rulenode.drop-file\' | translate}}">\n </tb-file-input>\n <tb-file-input style="padding-bottom: 8px;"\n formControlName="privateKey"\n inputId="privateKeySelect"\n [existingFileName]="mqttConfigForm.get(\'credentials\').get(\'privateKeyFileName\').value"\n (fileNameChanged)="mqttConfigForm.get(\'credentials\').get(\'privateKeyFileName\').setValue($event)"\n required\n requiredAsError\n label="{{\'tb.rulenode.private-key\' | translate}}"\n noFileText="tb.rulenode.no-file"\n dropLabel="{{\'tb.rulenode.drop-file\' | translate}}">\n </tb-file-input>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.private-key-password</mat-label>\n <input type="password" matInput formControlName="password">\n </mat-form-field>\n </ng-template>\n </section>\n </section>\n </mat-expansion-panel>\n</section>\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:'<section [formGroup]="rabbitMqConfigForm" fxLayout="column">\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.exchange-name-pattern</mat-label>\n <input matInput formControlName="exchangeNamePattern">\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.routing-key-pattern</mat-label>\n <input matInput formControlName="routingKeyPattern">\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.message-properties</mat-label>\n <mat-select formControlName="messageProperties">\n <mat-option *ngFor="let property of messageProperties" [value]="property">\n {{ property }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <div fxLayout.gt-sm="row" fxLayoutGap.gt-sm="8px">\n <mat-form-field fxFlex="100" fxFlex.gt-sm="60" class="mat-block">\n <mat-label translate>tb.rulenode.host</mat-label>\n <input required matInput formControlName="host">\n <mat-error *ngIf="rabbitMqConfigForm.get(\'host\').hasError(\'required\')">\n {{ \'tb.rulenode.host-required\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field fxFlex="100" fxFlex.gt-sm="40" class="mat-block">\n <mat-label translate>tb.rulenode.port</mat-label>\n <input required type="number" step="1" min="1" max="65535" matInput formControlName="port">\n <mat-error *ngIf="rabbitMqConfigForm.get(\'port\').hasError(\'required\')">\n {{ \'tb.rulenode.port-required\' | translate }}\n </mat-error>\n <mat-error *ngIf="rabbitMqConfigForm.get(\'port\').hasError(\'min\')">\n {{ \'tb.rulenode.port-range\' | translate }}\n </mat-error>\n <mat-error *ngIf="rabbitMqConfigForm.get(\'port\').hasError(\'max\')">\n {{ \'tb.rulenode.port-range\' | translate }}\n </mat-error>\n </mat-form-field>\n </div>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.virtual-host</mat-label>\n <input matInput formControlName="virtualHost">\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.username</mat-label>\n <input matInput formControlName="username">\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.password</mat-label>\n <input type="password" matInput formControlName="password">\n </mat-form-field>\n <mat-checkbox formControlName="automaticRecoveryEnabled" style="padding-bottom: 16px;">\n {{ \'tb.rulenode.automatic-recovery\' | translate }}\n </mat-checkbox>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.connection-timeout-ms</mat-label>\n <input type="number" step="1" min="0" matInput formControlName="connectionTimeout">\n <mat-error *ngIf="rabbitMqConfigForm.get(\'connectionTimeout\').hasError(\'min\')">\n {{ \'tb.rulenode.min-connection-timeout-ms-message\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.handshake-timeout-ms</mat-label>\n <input type="number" step="1" min="0" matInput formControlName="handshakeTimeout">\n <mat-error *ngIf="rabbitMqConfigForm.get(\'handshakeTimeout\').hasError(\'min\')">\n {{ \'tb.rulenode.min-handshake-timeout-ms-message\' | translate }}\n </mat-error>\n </mat-form-field>\n <label translate class="tb-title">tb.rulenode.client-properties</label>\n <tb-kv-map-config\n required="false"\n formControlName="clientProperties"\n keyText="tb.rulenode.key"\n keyRequiredText="tb.rulenode.key-required"\n valText="tb.rulenode.value"\n valRequiredText="tb.rulenode.value-required">\n </tb-kv-map-config>\n</section>\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:'<section [formGroup]="restApiCallConfigForm" fxLayout="column">\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.endpoint-url-pattern</mat-label>\n <input required matInput formControlName="restEndpointUrlPattern">\n <mat-error *ngIf="restApiCallConfigForm.get(\'restEndpointUrlPattern\').hasError(\'required\')">\n {{ \'tb.rulenode.endpoint-url-pattern-required\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.endpoint-url-pattern-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.request-method</mat-label>\n <mat-select formControlName="requestMethod">\n <mat-option *ngFor="let requestType of httpRequestTypes" [value]="requestType">\n {{ requestType }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <mat-checkbox formControlName="useSimpleClientHttpFactory" style="padding-bottom: 16px;">\n {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}\n </mat-checkbox>\n <mat-form-field *ngIf="restApiCallConfigForm.get(\'useSimpleClientHttpFactory\').value === false" class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.read-timeout</mat-label>\n <input type="number" step="1" min="0" matInput formControlName="readTimeoutMs">\n <mat-hint innerHTML="{{ \'tb.rulenode.read-timeout-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.max-parallel-requests-count</mat-label>\n <input type="number" step="1" min="0" matInput formControlName="maxParallelRequestsCount">\n <mat-hint innerHTML="{{ \'tb.rulenode.max-parallel-requests-count-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n <label translate class="tb-title">tb.rulenode.headers</label>\n <div class="tb-hint" translate>tb.rulenode.headers-hint</div>\n <tb-kv-map-config\n required="false"\n formControlName="headers"\n keyText="tb.rulenode.header"\n keyRequiredText="tb.rulenode.header-required"\n valText="tb.rulenode.value"\n valRequiredText="tb.rulenode.value-required">\n </tb-kv-map-config>\n <mat-checkbox formControlName="useRedisQueueForMsgPersistence" style="padding-bottom: 16px;">\n {{ \'tb.rulenode.use-redis-queue\' | translate }}\n </mat-checkbox>\n <div fxLayout="column" *ngIf="restApiCallConfigForm.get(\'useRedisQueueForMsgPersistence\').value === true">\n <mat-checkbox formControlName="trimQueue" style="padding-bottom: 16px;">\n {{ \'tb.rulenode.trim-redis-queue\' | translate }}\n </mat-checkbox>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.redis-queue-max-size</mat-label>\n <input type="number" step="1" min="0" matInput formControlName="maxQueueSize">\n </mat-form-field>\n </div>\n</section>\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:'<section [formGroup]="sendEmailConfigForm" fxLayout="column">\n <mat-checkbox formControlName="useSystemSmtpSettings" style="padding-bottom: 16px;">\n {{ \'tb.rulenode.use-system-smtp-settings\' | translate }}\n </mat-checkbox>\n <section fxLayout="column" *ngIf="sendEmailConfigForm.get(\'useSystemSmtpSettings\').value === false">\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.smtp-protocol</mat-label>\n <mat-select formControlName="smtpProtocol">\n <mat-option *ngFor="let smtpProtocol of smtpProtocols" [value]="smtpProtocol">\n {{ smtpProtocol.toUpperCase() }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <div fxLayout.gt-sm="row" fxLayoutGap.gt-sm="8px">\n <mat-form-field fxFlex="100" fxFlex.gt-sm="60" class="mat-block">\n <mat-label translate>tb.rulenode.smtp-host</mat-label>\n <input required matInput formControlName="smtpHost">\n <mat-error *ngIf="sendEmailConfigForm.get(\'smtpHost\').hasError(\'required\')">\n {{ \'tb.rulenode.smtp-host-required\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field fxFlex="100" fxFlex.gt-sm="40" class="mat-block">\n <mat-label translate>tb.rulenode.smtp-port</mat-label>\n <input required type="number" step="1" min="1" max="65535" matInput formControlName="smtpPort">\n <mat-error *ngIf="sendEmailConfigForm.get(\'smtpPort\').hasError(\'required\')">\n {{ \'tb.rulenode.smtp-port-required\' | translate }}\n </mat-error>\n <mat-error *ngIf="sendEmailConfigForm.get(\'smtpPort\').hasError(\'min\')">\n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n </mat-error>\n <mat-error *ngIf="sendEmailConfigForm.get(\'smtpPort\').hasError(\'max\')">\n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n </mat-error>\n </mat-form-field>\n </div>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.timeout-msec</mat-label>\n <input required type="number" step="1" min="0" matInput formControlName="timeout">\n <mat-error *ngIf="sendEmailConfigForm.get(\'timeout\').hasError(\'required\')">\n {{ \'tb.rulenode.timeout-required\' | translate }}\n </mat-error>\n <mat-error *ngIf="sendEmailConfigForm.get(\'timeout\').hasError(\'min\')">\n {{ \'tb.rulenode.min-timeout-msec-message\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-checkbox formControlName="enableTls" style="padding-bottom: 16px;">\n {{ \'tb.rulenode.enable-tls\' | translate }}\n </mat-checkbox>\n <mat-form-field class="mat-block" *ngIf="sendEmailConfigForm.get(\'enableTls\').value === true">\n <mat-label translate>tb.rulenode.tls-version</mat-label>\n <mat-select formControlName="tlsVersion">\n <mat-option *ngFor="let tlsVersion of tlsVersions" [value]="tlsVersion">\n {{ tlsVersion }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field class="mat-block" floatLabel="always">\n <mat-label translate>tb.rulenode.username</mat-label>\n <input matInput placeholder="{{ \'tb.rulenode.enter-username\' | translate }}" formControlName="username">\n </mat-form-field>\n <mat-form-field class="mat-block" floatLabel="always">\n <mat-label translate>tb.rulenode.password</mat-label>\n <input matInput type="password" placeholder="{{ \'tb.rulenode.enter-password\' | translate }}" formControlName="password">\n </mat-form-field>\n </section>\n</section>\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:'<section [formGroup]="checkMessageConfigForm" fxLayout="column">\n <label translate class="tb-title no-padding tb-required">tb.rulenode.data-keys</label>\n <mat-form-field floatLabel="always" class="mat-block">\n <mat-label></mat-label>\n <mat-chip-list #messageNamesChipList>\n <mat-chip\n *ngFor="let messageName of checkMessageConfigForm.get(\'messageNames\').value;"\n (removed)="removeMessageName(messageName)">\n {{messageName}}\n <mat-icon matChipRemove>close</mat-icon>\n </mat-chip>\n <input matInput type="text" placeholder="{{\'tb.rulenode.data-keys\' | translate}}"\n style="max-width: 200px;"\n [matChipInputFor]="messageNamesChipList"\n [matChipInputSeparatorKeyCodes]="separatorKeysCodes"\n (matChipInputTokenEnd)="addMessageName($event)"\n [matChipInputAddOnBlur]="true">\n </mat-chip-list>\n </mat-form-field>\n <div class="tb-hint" translate>tb.rulenode.separator-hint</div>\n <label translate class="tb-title no-padding tb-required">tb.rulenode.metadata-keys</label>\n <mat-form-field floatLabel="always" class="mat-block">\n <mat-label></mat-label>\n <mat-chip-list #metadataNamesChipList>\n <mat-chip\n *ngFor="let metadataName of checkMessageConfigForm.get(\'metadataNames\').value;"\n (removed)="removeMetadataName(metadataName)">\n {{metadataName}}\n <mat-icon matChipRemove>close</mat-icon>\n </mat-chip>\n <input matInput type="text" placeholder="{{\'tb.rulenode.metadata-keys\' | translate}}"\n style="max-width: 200px;"\n [matChipInputFor]="metadataNamesChipList"\n [matChipInputSeparatorKeyCodes]="separatorKeysCodes"\n (matChipInputTokenEnd)="addMetadataName($event)"\n [matChipInputAddOnBlur]="true">\n </mat-chip-list>\n </mat-form-field>\n <div class="tb-hint" translate>tb.rulenode.separator-hint</div>\n <mat-checkbox fxFlex formControlName="checkAllKeys" style="padding-bottom: 16px;">\n {{ \'tb.rulenode.check-all-keys\' | translate }}\n </mat-checkbox>\n <div class="tb-hint" translate>tb.rulenode.check-all-keys-hint</div>\n</section>\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:'<section [formGroup]="checkRelationConfigForm" fxLayout="column">\n <mat-checkbox fxFlex formControlName="checkForSingleEntity" style="padding-bottom: 16px;">\n {{ \'tb.rulenode.check-relation-to-specific-entity\' | translate }}\n </mat-checkbox>\n <div class="tb-hint" translate>tb.rulenode.check-relation-hint</div>\n <mat-form-field class="mat-block" style="min-width: 100px;">\n <mat-label translate>relation.direction</mat-label>\n <mat-select formControlName="direction" required>\n <mat-option *ngFor="let direction of entitySearchDirection" [value]="direction">\n {{ entitySearchDirectionTranslationsMap.get(direction) | translate }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <div fxLayout="row" *ngIf="checkRelationConfigForm.get(\'checkForSingleEntity\').value" style="padding-top: 20px">\n <tb-entity-type-select\n style="min-width: 100px; padding-bottom: 20px; padding-right: 8px;"\n showLabel\n required\n formControlName="entityType">\n </tb-entity-type-select>\n <tb-entity-autocomplete\n fxFlex\n required\n *ngIf="checkRelationConfigForm.get(\'entityType\').value"\n [entityType]="checkRelationConfigForm.get(\'entityType\').value"\n formControlName="entityId">\n </tb-entity-autocomplete>\n </div>\n <tb-relation-type-autocomplete\n required\n formControlName="relationType">\n </tb-relation-type-autocomplete>\n</section>\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:'<section [formGroup]="geoFilterConfigForm" fxLayout="column">\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.latitude-key-name</mat-label>\n <input matInput formControlName="latitudeKeyName" required>\n <mat-error *ngIf="geoFilterConfigForm.get(\'latitudeKeyName\').hasError(\'required\')">\n {{ \'tb.rulenode.latitude-key-name-required\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.longitude-key-name</mat-label>\n <input matInput formControlName="longitudeKeyName" required>\n <mat-error *ngIf="geoFilterConfigForm.get(\'longitudeKeyName\').hasError(\'required\')">\n {{ \'tb.rulenode.longitude-key-name-required\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-checkbox fxFlex formControlName="fetchPerimeterInfoFromMessageMetadata" style="padding-bottom: 16px;">\n {{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}\n </mat-checkbox>\n <div fxLayout="row" *ngIf="!geoFilterConfigForm.get(\'fetchPerimeterInfoFromMessageMetadata\').value">\n <mat-form-field fxFlex class="mat-block">\n <mat-label translate>tb.rulenode.perimeter-type</mat-label>\n <mat-select formControlName="perimeterType" required>\n <mat-option *ngFor="let type of perimeterTypes" [value]="type">\n {{ perimeterTypeTranslationMap.get(type) | translate }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div fxLayout="column"\n *ngIf="geoFilterConfigForm.get(\'perimeterType\').value === perimeterType.CIRCLE &&\n !geoFilterConfigForm.get(\'fetchPerimeterInfoFromMessageMetadata\').value">\n <div fxLayout="row" fxLayoutGap="8px">\n <mat-form-field fxFlex>\n <mat-label translate>tb.rulenode.circle-center-latitude</mat-label>\n <input type="number" min="-90" max="90" step="0.1" matInput formControlName="centerLatitude" required>\n <mat-error *ngIf="geoFilterConfigForm.get(\'centerLatitude\').hasError(\'required\')">\n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field fxFlex>\n <mat-label translate>tb.rulenode.circle-center-longitude</mat-label>\n <input type="number" min="-180" max="180" step="0.1" matInput formControlName="centerLongitude" required>\n <mat-error *ngIf="geoFilterConfigForm.get(\'centerLongitude\').hasError(\'required\')">\n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n </mat-error>\n </mat-form-field>\n </div>\n <div fxLayout="row" fxLayoutGap="8px">\n <mat-form-field fxFlex>\n <mat-label translate>tb.rulenode.range</mat-label>\n <input type="number" min="0" step="0.1" matInput formControlName="range" required>\n <mat-error *ngIf="geoFilterConfigForm.get(\'range\').hasError(\'required\')">\n {{ \'tb.rulenode.range-required\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field fxFlex>\n <mat-label translate>tb.rulenode.range-units</mat-label>\n <mat-select formControlName="rangeUnit" required>\n <mat-option *ngFor="let type of rangeUnits" [value]="type">\n {{ rangeUnitTranslationMap.get(type) | translate }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div fxLayout="row" *ngIf="geoFilterConfigForm.get(\'perimeterType\').value === perimeterType.POLYGON &&\n !geoFilterConfigForm.get(\'fetchPerimeterInfoFromMessageMetadata\').value">\n <div fxLayout="column" fxFlex="100">\n <mat-form-field class="mat-block" hintLabel="{{\'tb.rulenode.polygon-definition-hint\' | translate}}">\n <mat-label translate>tb.rulenode.polygon-definition</mat-label>\n <input matInput formControlName="polygonsDefinition" required>\n <mat-error *ngIf="geoFilterConfigForm.get(\'polygonsDefinition\').hasError(\'required\')">\n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n </mat-error>\n </mat-form-field>\n </div>\n </div>\n</section>\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:'<section [formGroup]="messageTypeConfigForm" fxLayout="column">\n <tb-message-types-config\n required\n label="tb.rulenode.message-types-filter"\n formControlName="messageTypes"\n ></tb-message-types-config>\n</section>\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:'<section [formGroup]="originatorTypeConfigForm" fxLayout="column">\n <label translate class="tb-title no-padding tb-required">tb.rulenode.originator-types-filter</label>\n <tb-entity-type-list fxFlex\n formControlName="originatorTypes"\n [allowedEntityTypes]="allowedEntityTypes"\n [ignoreAuthorityFilter]="true"\n required>\n </tb-entity-type-list>\n</section>\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:'<section [formGroup]="scriptConfigForm" fxLayout="column">\n <label translate class="tb-title no-padding">tb.rulenode.filter</label>\n <tb-js-func #jsFuncComponent\n formControlName="jsScript"\n functionName="Filter"\n [functionArgs]="[\'msg\', \'metadata\', \'msgType\']"\n noValidate="true">\n </tb-js-func>\n <div fxLayout="row">\n <button mat-button mat-raised-button color="primary" (click)="testScript()">\n {{ \'tb.rulenode.test-filter-function\' | translate }}\n </button>\n </div>\n</section>\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:'<section [formGroup]="switchConfigForm" fxLayout="column">\n <label translate class="tb-title no-padding">tb.rulenode.switch</label>\n <tb-js-func #jsFuncComponent\n formControlName="jsScript"\n functionName="Switch"\n [functionArgs]="[\'msg\', \'metadata\', \'msgType\']"\n noValidate="true">\n </tb-js-func>\n <div fxLayout="row">\n <button mat-button mat-raised-button color="primary" (click)="testScript()">\n {{ \'tb.rulenode.test-switch-function\' | translate }}\n </button>\n </div>\n</section>\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:'<section [formGroup]="alarmStatusConfigForm" fxLayout="column">\n <mat-form-field class="mat-block" class="alarm-status-list">\n <mat-label translate>tb.rulenode.alarm-status-filter</mat-label>\n <mat-chip-list #alarmStatusChipList required>\n <mat-chip\n *ngFor="let alarmStatus of alarmStatusConfigForm.get(\'alarmStatusList\').value;"\n (removed)="removeAlarmStatus(alarmStatus)">\n <span>\n <strong>{{alarmStatusTranslationsMap.get(alarmStatus) | translate}}</strong>\n </span>\n <mat-icon matChipRemove>close</mat-icon>\n </mat-chip>\n <input matInput type="text"\n style="max-width: 200px;"\n #alarmStatusInput\n (focusin)="onAlarmStatusInputFocus()"\n [formControl]="statusFormControl"\n matAutocompleteOrigin\n #origin="matAutocompleteOrigin"\n [matAutocompleteConnectedTo]="origin"\n [matAutocomplete]="alarmStatusAutocomplete"\n [matChipInputFor]="alarmStatusChipList">\n </mat-chip-list>\n <mat-autocomplete #alarmStatusAutocomplete="matAutocomplete"\n class="tb-autocomplete"\n (optionSelected)="alarmStatusSelected($event)"\n [displayWith]="displayStatusFn">\n <mat-option *ngFor="let status of filteredAlarmStatus | async" [value]="status">\n <span [innerHTML]="alarmStatusTranslationsMap.get(status) | translate | highlight:searchText"></span>\n </mat-option>\n <mat-option *ngIf="(filteredAlarmStatus | async)?.length === 0" [value]="null" class="tb-not-found">\n <div class="tb-not-found-content" (click)="$event.stopPropagation()">\n <div>\n <span translate>tb.rulenode.no-alarm-status-matching</span>\n </div>\n </div>\n </mat-option>\n </mat-autocomplete>\n </mat-form-field>\n <tb-error [error]="(statusFormControl.touched &&\n alarmStatusConfigForm.get(\'alarmStatusList\').hasError(\'required\'))\n ? translate.instant(\'tb.rulenode.alarm-status-list-empty\') : \'\'"></tb-error>\n </section>\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:'<section [formGroup]="customerAttributesConfigForm" fxLayout="column">\n <label translate class="tb-title tb-required">tb.rulenode.attr-mapping</label>\n <mat-checkbox fxFlex formControlName="telemetry" style="padding-bottom: 16px;">\n {{ \'tb.rulenode.latest-telemetry\' | translate }}\n </mat-checkbox>\n <tb-kv-map-config\n required\n formControlName="attrMapping"\n requiredText="tb.rulenode.attr-mapping-required"\n keyText="{{ customerAttributesConfigForm.get(\'telemetry\').value ? \'tb.rulenode.source-telemetry\' : \'tb.rulenode.source-attribute\' }}"\n keyRequiredText="{{ customerAttributesConfigForm.get(\'telemetry\').value ? \'tb.rulenode.source-telemetry-required\' : \'tb.rulenode.source-attribute-required\' }}"\n valText="tb.rulenode.target-attribute"\n valRequiredText="tb.rulenode.target-attribute-required">\n </tb-kv-map-config>\n</section>\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:'<section [formGroup]="entityDetailsConfigForm" fxLayout="column">\n <mat-form-field class="mat-block" class="entity-fields-list">\n <mat-label translate>tb.rulenode.entity-details</mat-label>\n <mat-chip-list #detailsChipList required>\n <mat-chip\n *ngFor="let details of entityDetailsConfigForm.get(\'detailsList\').value;"\n (removed)="removeDetailsField(details)">\n <span>\n <strong>{{entityDetailsTranslationsMap.get(details) | translate}}</strong>\n </span>\n <mat-icon matChipRemove>close</mat-icon>\n </mat-chip>\n <input matInput type="text"\n style="max-width: 200px;"\n #detailsInput\n (focusin)="onEntityDetailsInputFocus()"\n [formControl]="detailsFormControl"\n matAutocompleteOrigin\n #origin="matAutocompleteOrigin"\n [matAutocompleteConnectedTo]="origin"\n [matAutocomplete]="detailsAutocomplete"\n [matChipInputFor]="detailsChipList">\n </mat-chip-list>\n <mat-autocomplete #detailsAutocomplete="matAutocomplete"\n class="tb-autocomplete"\n (optionSelected)="detailsFieldSelected($event)"\n [displayWith]="displayDetailsFn">\n <mat-option *ngFor="let details of filteredEntityDetails | async" [value]="details">\n <span [innerHTML]="entityDetailsTranslationsMap.get(details) | translate | highlight:searchText"></span>\n </mat-option>\n <mat-option *ngIf="(filteredEntityDetails | async)?.length === 0" [value]="null" class="tb-not-found">\n <div class="tb-not-found-content" (click)="$event.stopPropagation()">\n <div>\n <span translate>tb.rulenode.no-entity-details-matching</span>\n </div>\n </div>\n </mat-option>\n </mat-autocomplete>\n </mat-form-field>\n <tb-error [error]="(detailsFormControl.touched &&\n entityDetailsConfigForm.get(\'detailsList\').hasError(\'required\'))\n ? translate.instant(\'tb.rulenode.entity-details-list-empty\') : \'\'"></tb-error>\n <mat-checkbox fxFlex formControlName="addToMetadata" style="padding-bottom: 16px;">\n {{ \'tb.rulenode.add-to-metadata\' | translate }}\n </mat-checkbox>\n <div class="tb-hint" translate>tb.rulenode.add-to-metadata-hint</div>\n</section>\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:'<section [formGroup]="deviceAttributesConfigForm" fxLayout="column">\n <label translate class="tb-title tb-required">tb.rulenode.device-relations-query</label>\n <tb-device-relations-query-config\n required\n formControlName="deviceRelationsQuery"\n style="padding-bottom: 15px;">\n </tb-device-relations-query-config>\n <mat-checkbox fxFlex formControlName="tellFailureIfAbsent" style="padding-bottom: 16px;">\n {{ \'tb.rulenode.tell-failure-if-absent\' | translate }}\n </mat-checkbox>\n <div class="tb-hint" translate>tb.rulenode.tell-failure-if-absent-hint</div>\n <label translate class="tb-title no-padding">tb.rulenode.client-attributes</label>\n <mat-form-field floatLabel="always" class="mat-block">\n <mat-label></mat-label>\n <mat-chip-list #clientAttributesChipList>\n <mat-chip\n *ngFor="let key of deviceAttributesConfigForm.get(\'clientAttributeNames\').value;"\n (removed)="removeKey(key, \'clientAttributeNames\')">\n {{key}}\n <mat-icon matChipRemove>close</mat-icon>\n </mat-chip>\n <input matInput type="text" placeholder="{{\'tb.rulenode.client-attributes\' | translate}}"\n style="max-width: 200px;"\n [matChipInputFor]="clientAttributesChipList"\n [matChipInputSeparatorKeyCodes]="separatorKeysCodes"\n (matChipInputTokenEnd)="addKey($event, \'clientAttributeNames\')"\n [matChipInputAddOnBlur]="true">\n </mat-chip-list>\n </mat-form-field>\n <label translate class="tb-title no-padding">tb.rulenode.shared-attributes</label>\n <mat-form-field floatLabel="always" class="mat-block">\n <mat-label></mat-label>\n <mat-chip-list #sharedAttributesChipList>\n <mat-chip\n *ngFor="let key of deviceAttributesConfigForm.get(\'sharedAttributeNames\').value;"\n (removed)="removeKey(key, \'sharedAttributeNames\')">\n {{key}}\n <mat-icon matChipRemove>close</mat-icon>\n </mat-chip>\n <input matInput type="text" placeholder="{{\'tb.rulenode.shared-attributes\' | translate}}"\n style="max-width: 200px;"\n [matChipInputFor]="sharedAttributesChipList"\n [matChipInputSeparatorKeyCodes]="separatorKeysCodes"\n (matChipInputTokenEnd)="addKey($event, \'sharedAttributeNames\')"\n [matChipInputAddOnBlur]="true">\n </mat-chip-list>\n </mat-form-field>\n <label translate class="tb-title no-padding">tb.rulenode.server-attributes</label>\n <mat-form-field floatLabel="always" class="mat-block">\n <mat-label></mat-label>\n <mat-chip-list #serverAttributesChipList>\n <mat-chip\n *ngFor="let key of deviceAttributesConfigForm.get(\'serverAttributeNames\').value;"\n (removed)="removeKey(key, \'serverAttributeNames\')">\n {{key}}\n <mat-icon matChipRemove>close</mat-icon>\n </mat-chip>\n <input matInput type="text" placeholder="{{\'tb.rulenode.server-attributes\' | translate}}"\n style="max-width: 200px;"\n [matChipInputFor]="serverAttributesChipList"\n [matChipInputSeparatorKeyCodes]="separatorKeysCodes"\n (matChipInputTokenEnd)="addKey($event, \'serverAttributeNames\')"\n [matChipInputAddOnBlur]="true">\n </mat-chip-list>\n </mat-form-field>\n <label translate class="tb-title no-padding">tb.rulenode.latest-timeseries</label>\n <mat-form-field floatLabel="always" class="mat-block">\n <mat-label></mat-label>\n <mat-chip-list #latestTimeseriesChipList>\n <mat-chip\n *ngFor="let key of deviceAttributesConfigForm.get(\'latestTsKeyNames\').value;"\n (removed)="removeKey(key, \'latestTsKeyNames\')">\n {{key}}\n <mat-icon matChipRemove>close</mat-icon>\n </mat-chip>\n <input matInput type="text" placeholder="{{\'tb.rulenode.latest-timeseries\' | translate}}"\n style="max-width: 200px;"\n [matChipInputFor]="latestTimeseriesChipList"\n [matChipInputSeparatorKeyCodes]="separatorKeysCodes"\n (matChipInputTokenEnd)="addKey($event, \'latestTsKeyNames\')"\n [matChipInputAddOnBlur]="true">\n </mat-chip-list>\n </mat-form-field>\n <mat-checkbox formControlName="getLatestValueWithTs" style="padding-bottom: 16px;">\n {{ \'tb.rulenode.get-latest-value-with-ts\' | translate }}\n </mat-checkbox>\n <div class="tb-hint" innerHTML="{{ \'tb.rulenode.get-latest-value-with-ts-hint\' | translate }}"></div>\n</section>\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:'<section [formGroup]="originatorAttributesConfigForm" fxLayout="column">\n <mat-checkbox fxFlex formControlName="tellFailureIfAbsent" style="padding-bottom: 16px;">\n {{ \'tb.rulenode.tell-failure-if-absent\' | translate }}\n </mat-checkbox>\n <div class="tb-hint" translate>tb.rulenode.tell-failure-if-absent-hint</div>\n <label translate class="tb-title no-padding">tb.rulenode.client-attributes</label>\n <mat-form-field floatLabel="always" class="mat-block">\n <mat-label></mat-label>\n <mat-chip-list #clientAttributesChipList>\n <mat-chip\n *ngFor="let key of originatorAttributesConfigForm.get(\'clientAttributeNames\').value;"\n (removed)="removeKey(key, \'clientAttributeNames\')">\n {{key}}\n <mat-icon matChipRemove>close</mat-icon>\n </mat-chip>\n <input matInput type="text" placeholder="{{\'tb.rulenode.client-attributes\' | translate}}"\n style="max-width: 200px;"\n [matChipInputFor]="clientAttributesChipList"\n [matChipInputSeparatorKeyCodes]="separatorKeysCodes"\n (matChipInputTokenEnd)="addKey($event, \'clientAttributeNames\')"\n [matChipInputAddOnBlur]="true">\n </mat-chip-list>\n </mat-form-field>\n <label translate class="tb-title no-padding">tb.rulenode.shared-attributes</label>\n <mat-form-field floatLabel="always" class="mat-block">\n <mat-label></mat-label>\n <mat-chip-list #sharedAttributesChipList>\n <mat-chip\n *ngFor="let key of originatorAttributesConfigForm.get(\'sharedAttributeNames\').value;"\n (removed)="removeKey(key, \'sharedAttributeNames\')">\n {{key}}\n <mat-icon matChipRemove>close</mat-icon>\n </mat-chip>\n <input matInput type="text" placeholder="{{\'tb.rulenode.shared-attributes\' | translate}}"\n style="max-width: 200px;"\n [matChipInputFor]="sharedAttributesChipList"\n [matChipInputSeparatorKeyCodes]="separatorKeysCodes"\n (matChipInputTokenEnd)="addKey($event, \'sharedAttributeNames\')"\n [matChipInputAddOnBlur]="true">\n </mat-chip-list>\n </mat-form-field>\n <label translate class="tb-title no-padding">tb.rulenode.server-attributes</label>\n <mat-form-field floatLabel="always" class="mat-block">\n <mat-label></mat-label>\n <mat-chip-list #serverAttributesChipList>\n <mat-chip\n *ngFor="let key of originatorAttributesConfigForm.get(\'serverAttributeNames\').value;"\n (removed)="removeKey(key, \'serverAttributeNames\')">\n {{key}}\n <mat-icon matChipRemove>close</mat-icon>\n </mat-chip>\n <input matInput type="text" placeholder="{{\'tb.rulenode.server-attributes\' | translate}}"\n style="max-width: 200px;"\n [matChipInputFor]="serverAttributesChipList"\n [matChipInputSeparatorKeyCodes]="separatorKeysCodes"\n (matChipInputTokenEnd)="addKey($event, \'serverAttributeNames\')"\n [matChipInputAddOnBlur]="true">\n </mat-chip-list>\n </mat-form-field>\n <label translate class="tb-title no-padding">tb.rulenode.latest-timeseries</label>\n <mat-form-field floatLabel="always" class="mat-block">\n <mat-label></mat-label>\n <mat-chip-list #latestTimeseriesChipList>\n <mat-chip\n *ngFor="let key of originatorAttributesConfigForm.get(\'latestTsKeyNames\').value;"\n (removed)="removeKey(key, \'latestTsKeyNames\')">\n {{key}}\n <mat-icon matChipRemove>close</mat-icon>\n </mat-chip>\n <input matInput type="text" placeholder="{{\'tb.rulenode.latest-timeseries\' | translate}}"\n style="max-width: 200px;"\n [matChipInputFor]="latestTimeseriesChipList"\n [matChipInputSeparatorKeyCodes]="separatorKeysCodes"\n (matChipInputTokenEnd)="addKey($event, \'latestTsKeyNames\')"\n [matChipInputAddOnBlur]="true">\n </mat-chip-list>\n </mat-form-field>\n <mat-checkbox formControlName="getLatestValueWithTs" style="padding-bottom: 16px;">\n {{ \'tb.rulenode.get-latest-value-with-ts\' | translate }}\n </mat-checkbox>\n <div class="tb-hint" innerHTML="{{ \'tb.rulenode.get-latest-value-with-ts-hint\' | translate }}"></div>\n</section>\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:'<section [formGroup]="originatorFieldsConfigForm" fxLayout="column">\n <label translate class="tb-title tb-required">tb.rulenode.fields-mapping</label>\n <tb-kv-map-config\n required\n formControlName="fieldsMapping"\n requiredText="tb.rulenode.fields-mapping-required"\n keyText="tb.rulenode.source-field"\n keyRequiredText="tb.rulenode.source-field-required"\n valText="tb.rulenode.target-attribute"\n valRequiredText="tb.rulenode.target-attribute-required">\n </tb-kv-map-config>\n</section>\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:'<section [formGroup]="getTelemetryFromDatabaseConfigForm" fxLayout="column">\n <label translate class="tb-title no-padding">tb.rulenode.latest-timeseries</label>\n <mat-form-field floatLabel="always" class="mat-block">\n <mat-label></mat-label>\n <mat-chip-list #latestTimeseriesChipList>\n <mat-chip\n *ngFor="let key of getTelemetryFromDatabaseConfigForm.get(\'latestTsKeyNames\').value;"\n (removed)="removeKey(key, \'latestTsKeyNames\')">\n {{key}}\n <mat-icon matChipRemove>close</mat-icon>\n </mat-chip>\n <input matInput type="text" placeholder="{{\'tb.rulenode.latest-timeseries\' | translate}}"\n style="max-width: 200px;"\n [matChipInputFor]="latestTimeseriesChipList"\n [matChipInputSeparatorKeyCodes]="separatorKeysCodes"\n (matChipInputTokenEnd)="addKey($event, \'latestTsKeyNames\')"\n [matChipInputAddOnBlur]="true">\n </mat-chip-list>\n </mat-form-field>\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.fetch-mode</mat-label>\n <mat-select formControlName="fetchMode" required>\n <mat-option *ngFor="let mode of fetchModes" [value]="mode">\n {{ mode }}\n </mat-option>\n </mat-select>\n <mat-hint translate>tb.rulenode.fetch-mode-hint</mat-hint>\n </mat-form-field>\n <div fxLayout="column" *ngIf="getTelemetryFromDatabaseConfigForm.get(\'fetchMode\').value === fetchMode.ALL">\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.order-by</mat-label>\n <mat-select formControlName="orderBy" required>\n <mat-option *ngFor="let order of samplingOrders" [value]="order">\n {{ order }}\n </mat-option>\n </mat-select>\n <mat-hint translate>tb.rulenode.order-by-hint</mat-hint>\n </mat-form-field>\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.limit</mat-label>\n <input type="number" min="2" max="1000" step="1" matInput formControlName="limit" required>\n <mat-hint translate>tb.rulenode.limit-hint</mat-hint>\n </mat-form-field>\n </div>\n <mat-checkbox formControlName="useMetadataIntervalPatterns">\n {{ \'tb.rulenode.use-metadata-interval-patterns\' | translate }}\n </mat-checkbox>\n <div class="tb-hint" style="padding-bottom: 16px;" translate>tb.rulenode.use-metadata-interval-patterns-hint</div>\n <div fxLayout="column" *ngIf="getTelemetryFromDatabaseConfigForm.get(\'useMetadataIntervalPatterns\').value === false; else intervalPattern">\n <div fxLayout="column" fxLayout.gt-sm="row" fxLayoutGap.gt-sm="8px">\n <mat-form-field fxFlex class="mat-block">\n <mat-label translate>tb.rulenode.start-interval</mat-label>\n <input type="number" step="1" min="1" max="2147483647" matInput formControlName="startInterval" required>\n <mat-error *ngIf="getTelemetryFromDatabaseConfigForm.get(\'startInterval\').hasError(\'required\')">\n {{ \'tb.rulenode.start-interval-value-required\' | translate }}\n </mat-error>\n <mat-error *ngIf="getTelemetryFromDatabaseConfigForm.get(\'startInterval\').hasError(\'min\')">\n {{ \'tb.rulenode.time-value-range\' | translate }}\n </mat-error>\n <mat-error *ngIf="getTelemetryFromDatabaseConfigForm.get(\'startInterval\').hasError(\'max\')">\n {{ \'tb.rulenode.time-value-range\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field fxFlex class="mat-block">\n <mat-label translate>tb.rulenode.start-interval-time-unit</mat-label>\n <mat-select formControlName="startIntervalTimeUnit" required>\n <mat-option *ngFor="let timeUnit of timeUnits" [value]="timeUnit">\n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div fxLayout="column" fxLayout.gt-sm="row" fxLayoutGap.gt-sm="8px">\n <mat-form-field fxFlex class="mat-block">\n <mat-label translate>tb.rulenode.end-interval</mat-label>\n <input type="number" step="1" min="1" max="2147483647" matInput formControlName="endInterval" required>\n <mat-error *ngIf="getTelemetryFromDatabaseConfigForm.get(\'endInterval\').hasError(\'required\')">\n {{ \'tb.rulenode.end-interval-value-required\' | translate }}\n </mat-error>\n <mat-error *ngIf="getTelemetryFromDatabaseConfigForm.get(\'endInterval\').hasError(\'min\')">\n {{ \'tb.rulenode.time-value-range\' | translate }}\n </mat-error>\n <mat-error *ngIf="getTelemetryFromDatabaseConfigForm.get(\'endInterval\').hasError(\'max\')">\n {{ \'tb.rulenode.time-value-range\' | translate }}\n </mat-error>\n </mat-form-field>\n <mat-form-field fxFlex class="mat-block">\n <mat-label translate>tb.rulenode.end-interval-time-unit</mat-label>\n <mat-select formControlName="endIntervalTimeUnit" required>\n <mat-option *ngFor="let timeUnit of timeUnits" [value]="timeUnit">\n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <ng-template #intervalPattern>\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.start-interval-pattern</mat-label>\n <input matInput formControlName="startIntervalPattern" required>\n <mat-error *ngIf="getTelemetryFromDatabaseConfigForm.get(\'startIntervalPattern\').hasError(\'required\')">\n {{ \'tb.rulenode.start-interval-pattern-required\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.start-interval-pattern-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.end-interval-pattern</mat-label>\n <input matInput formControlName="endIntervalPattern" required>\n <mat-error *ngIf="getTelemetryFromDatabaseConfigForm.get(\'endIntervalPattern\').hasError(\'required\')">\n {{ \'tb.rulenode.end-interval-pattern-required\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.end-interval-pattern-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n </ng-template>\n</section>\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:'<section [formGroup]="relatedAttributesConfigForm" fxLayout="column">\n <label translate class="tb-title tb-required">tb.rulenode.relations-query</label>\n <tb-relations-query-config\n required\n formControlName="relationsQuery"\n style="padding-bottom: 15px;">\n </tb-relations-query-config>\n <label translate class="tb-title tb-required">tb.rulenode.attr-mapping</label>\n <mat-checkbox fxFlex formControlName="telemetry" style="padding-bottom: 16px;">\n {{ \'tb.rulenode.latest-telemetry\' | translate }}\n </mat-checkbox>\n <tb-kv-map-config\n required\n formControlName="attrMapping"\n requiredText="tb.rulenode.attr-mapping-required"\n keyText="{{ relatedAttributesConfigForm.get(\'telemetry\').value ? \'tb.rulenode.source-telemetry\' : \'tb.rulenode.source-attribute\' }}"\n keyRequiredText="{{ relatedAttributesConfigForm.get(\'telemetry\').value ? \'tb.rulenode.source-telemetry-required\' : \'tb.rulenode.source-attribute-required\' }}"\n valText="tb.rulenode.target-attribute"\n valRequiredText="tb.rulenode.target-attribute-required">\n </tb-kv-map-config>\n</section>\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:'<section [formGroup]="tenantAttributesConfigForm" fxLayout="column">\n <label translate class="tb-title tb-required">tb.rulenode.attr-mapping</label>\n <mat-checkbox fxFlex formControlName="telemetry" style="padding-bottom: 16px;">\n {{ \'tb.rulenode.latest-telemetry\' | translate }}\n </mat-checkbox>\n <tb-kv-map-config\n required\n formControlName="attrMapping"\n requiredText="tb.rulenode.attr-mapping-required"\n keyText="{{ tenantAttributesConfigForm.get(\'telemetry\').value ? \'tb.rulenode.source-telemetry\' : \'tb.rulenode.source-attribute\' }}"\n keyRequiredText="{{ tenantAttributesConfigForm.get(\'telemetry\').value ? \'tb.rulenode.source-telemetry-required\' : \'tb.rulenode.source-attribute-required\' }}"\n valText="tb.rulenode.target-attribute"\n valRequiredText="tb.rulenode.target-attribute-required">\n </tb-kv-map-config>\n</section>\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:'<section [formGroup]="changeOriginatorConfigForm" fxLayout="column">\n <mat-form-field class="mat-block">\n <mat-label translate>tb.rulenode.originator-source</mat-label>\n <mat-select formControlName="originatorSource" required>\n <mat-option *ngFor="let source of originatorSources" [value]="source">\n {{ originatorSourceTranslationMap.get(source) | translate }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <section fxLayout="column" *ngIf="changeOriginatorConfigForm.get(\'originatorSource\').value === originatorSource.RELATED">\n <label translate class="tb-title tb-required">tb.rulenode.relations-query</label>\n <tb-relations-query-config\n required\n formControlName="relationsQuery"\n style="padding-bottom: 15px;">\n </tb-relations-query-config>\n </section>\n</section>\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:'<section [formGroup]="scriptConfigForm" fxLayout="column">\n <label translate class="tb-title no-padding">tb.rulenode.transform</label>\n <tb-js-func #jsFuncComponent\n formControlName="jsScript"\n functionName="Transform"\n [functionArgs]="[\'msg\', \'metadata\', \'msgType\']"\n noValidate="true">\n </tb-js-func>\n <div fxLayout="row">\n <button mat-button mat-raised-button color="primary" (click)="testScript()">\n {{ \'tb.rulenode.test-transformer-function\' | translate }}\n </button>\n </div>\n</section>\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:'<section [formGroup]="toEmailConfigForm" fxLayout="column">\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.from-template</mat-label>\n <textarea required matInput formControlName="fromTemplate" rows="2"></textarea>\n <mat-error *ngIf="toEmailConfigForm.get(\'fromTemplate\').hasError(\'required\')">\n {{ \'tb.rulenode.from-template-required\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.from-template-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.to-template</mat-label>\n <textarea required matInput formControlName="toTemplate" rows="2"></textarea>\n <mat-error *ngIf="toEmailConfigForm.get(\'toTemplate\').hasError(\'required\')">\n {{ \'tb.rulenode.to-template-required\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.mail-address-list-template-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.cc-template</mat-label>\n <textarea matInput formControlName="ccTemplate" rows="2"></textarea>\n <mat-hint innerHTML="{{ \'tb.rulenode.mail-address-list-template-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.bcc-template</mat-label>\n <textarea matInput formControlName="bccTemplate" rows="2"></textarea>\n <mat-hint innerHTML="{{ \'tb.rulenode.mail-address-list-template-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.subject-template</mat-label>\n <textarea required matInput formControlName="subjectTemplate" rows="2"></textarea>\n <mat-error *ngIf="toEmailConfigForm.get(\'subjectTemplate\').hasError(\'required\')">\n {{ \'tb.rulenode.subject-template-required\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.subject-template-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n <mat-form-field class="mat-block" style="padding-bottom: 16px;">\n <mat-label translate>tb.rulenode.body-template</mat-label>\n <textarea required matInput formControlName="bodyTemplate" rows="6"></textarea>\n <mat-error *ngIf="toEmailConfigForm.get(\'bodyTemplate\').hasError(\'required\')">\n {{ \'tb.rulenode.body-template-required\' | translate }}\n </mat-error>\n <mat-hint innerHTML="{{ \'tb.rulenode.body-template-hint\' | translate }}"></mat-hint>\n </mat-form-field>\n</section>\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 <code>${metaKeyName}</code> to substitute variables from metadata","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","entity-type-pattern-hint":"Type pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.","customer-name-pattern":"Customer name pattern","customer-name-pattern-required":"Customer name pattern is required","create-customer-if-not-exists":"Create new customer if not exists","customer-cache-expiration":"Customers cache expiration time (sec)","customer-name-pattern-hint":"Customer name pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.","customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","start-interval":"Start Interval","end-interval":"End Interval","start-interval-time-unit":"Start Interval Time Unit","end-interval-time-unit":"End Interval Time Unit","fetch-mode":"Fetch mode","fetch-mode-hint":"If selected fetch mode 'ALL' you able to choose telemetry sampling order.","order-by":"Order by","order-by-hint":"Select to choose telemetry sampling order.",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 <code>${metaKeyName}</code> 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 <code>${metaKeyName}</code> to substitute variables from metadata","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required","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 <code>${metaKeyName}</code> to substitute variables from metadata","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":"Comma separated address list, use <code>${metaKeyName}</code> to substitute variables from metadata","cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","subject-template-hint":"Mail subject template, use <code>${metaKeyName}</code> to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use <code>${metaKeyName}</code> to substitute variables from metadata","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","endpoint-url-pattern-hint":"HTTP URL address pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory","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 <code>${metaKeyName}</code> in header/value fields to substitute variables from metadata",header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required","mqtt-topic-pattern-hint":"MQTT topic pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","topic-arn-pattern-hint":"Topic ARN pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","queue-url-pattern-hint":"Queue URL pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","gcp-project-id":"GCP project ID","gcp-project-id-required":"GCP project ID is required","gcp-service-account-key":"GCP service account key file","gcp-service-account-key-required":"GCP service account key file is required","pubsub-topic-name":"Topic name","pubsub-topic-name-required":"Topic name is required","message-attributes":"Message attributes","message-attributes-hint":"Use <code>${metaKeyName}</code> in name/value fields to substitute variables from metadata","connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"CA certificate file *","private-key":"Private key file *",cert:"Certificate file *","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","use-metadata-interval-patterns":"Use metadata interval patterns","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","check-all-keys":"Check that all selected keys are present","check-all-keys-hint":"If selected, checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-to-specific-entity":"Delete relation to specific entity","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.","start-interval-pattern":"Start interval pattern","end-interval-pattern":"End interval pattern","start-interval-pattern-required":"Start interval pattern is required","end-interval-pattern-required":"End interval pattern is required","start-interval-pattern-hint":"Start interval pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","end-interval-pattern-hint":"End interval pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","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": "&lcub;\\"ts\\":1574329385897,\\"value\\":42&rcub;"',"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})}));
16   -//# sourceMappingURL=rulenode-core-config.umd.min.js.map
\ No newline at end of file
  16 +//# sourceMappingURL=rulenode-core-config.umd.min.js.map
... ...
  1 + Apache License
  2 + Version 2.0, January 2004
  3 + http://www.apache.org/licenses/
  4 +
  5 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
  6 +
  7 + 1. Definitions.
  8 +
  9 + "License" shall mean the terms and conditions for use, reproduction,
  10 + and distribution as defined by Sections 1 through 9 of this document.
  11 +
  12 + "Licensor" shall mean the copyright owner or entity authorized by
  13 + the copyright owner that is granting the License.
  14 +
  15 + "Legal Entity" shall mean the union of the acting entity and all
  16 + other entities that control, are controlled by, or are under common
  17 + control with that entity. For the purposes of this definition,
  18 + "control" means (i) the power, direct or indirect, to cause the
  19 + direction or management of such entity, whether by contract or
  20 + otherwise, or (ii) ownership of fifty percent (50%) or more of the
  21 + outstanding shares, or (iii) beneficial ownership of such entity.
  22 +
  23 + "You" (or "Your") shall mean an individual or Legal Entity
  24 + exercising permissions granted by this License.
  25 +
  26 + "Source" form shall mean the preferred form for making modifications,
  27 + including but not limited to software source code, documentation
  28 + source, and configuration files.
  29 +
  30 + "Object" form shall mean any form resulting from mechanical
  31 + transformation or translation of a Source form, including but
  32 + not limited to compiled object code, generated documentation,
  33 + and conversions to other media types.
  34 +
  35 + "Work" shall mean the work of authorship, whether in Source or
  36 + Object form, made available under the License, as indicated by a
  37 + copyright notice that is included in or attached to the work
  38 + (an example is provided in the Appendix below).
  39 +
  40 + "Derivative Works" shall mean any work, whether in Source or Object
  41 + form, that is based on (or derived from) the Work and for which the
  42 + editorial revisions, annotations, elaborations, or other modifications
  43 + represent, as a whole, an original work of authorship. For the purposes
  44 + of this License, Derivative Works shall not include works that remain
  45 + separable from, or merely link (or bind by name) to the interfaces of,
  46 + the Work and Derivative Works thereof.
  47 +
  48 + "Contribution" shall mean any work of authorship, including
  49 + the original version of the Work and any modifications or additions
  50 + to that Work or Derivative Works thereof, that is intentionally
  51 + submitted to Licensor for inclusion in the Work by the copyright owner
  52 + or by an individual or Legal Entity authorized to submit on behalf of
  53 + the copyright owner. For the purposes of this definition, "submitted"
  54 + means any form of electronic, verbal, or written communication sent
  55 + to the Licensor or its representatives, including but not limited to
  56 + communication on electronic mailing lists, source code control systems,
  57 + and issue tracking systems that are managed by, or on behalf of, the
  58 + Licensor for the purpose of discussing and improving the Work, but
  59 + excluding communication that is conspicuously marked or otherwise
  60 + designated in writing by the copyright owner as "Not a Contribution."
  61 +
  62 + "Contributor" shall mean Licensor and any individual or Legal Entity
  63 + on behalf of whom a Contribution has been received by Licensor and
  64 + subsequently incorporated within the Work.
  65 +
  66 + 2. Grant of Copyright License. Subject to the terms and conditions of
  67 + this License, each Contributor hereby grants to You a perpetual,
  68 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable
  69 + copyright license to reproduce, prepare Derivative Works of,
  70 + publicly display, publicly perform, sublicense, and distribute the
  71 + Work and such Derivative Works in Source or Object form.
  72 +
  73 + 3. Grant of Patent License. Subject to the terms and conditions of
  74 + this License, each Contributor hereby grants to You a perpetual,
  75 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable
  76 + (except as stated in this section) patent license to make, have made,
  77 + use, offer to sell, sell, import, and otherwise transfer the Work,
  78 + where such license applies only to those patent claims licensable
  79 + by such Contributor that are necessarily infringed by their
  80 + Contribution(s) alone or by combination of their Contribution(s)
  81 + with the Work to which such Contribution(s) was submitted. If You
  82 + institute patent litigation against any entity (including a
  83 + cross-claim or counterclaim in a lawsuit) alleging that the Work
  84 + or a Contribution incorporated within the Work constitutes direct
  85 + or contributory patent infringement, then any patent licenses
  86 + granted to You under this License for that Work shall terminate
  87 + as of the date such litigation is filed.
  88 +
  89 + 4. Redistribution. You may reproduce and distribute copies of the
  90 + Work or Derivative Works thereof in any medium, with or without
  91 + modifications, and in Source or Object form, provided that You
  92 + meet the following conditions:
  93 +
  94 + (a) You must give any other recipients of the Work or
  95 + Derivative Works a copy of this License; and
  96 +
  97 + (b) You must cause any modified files to carry prominent notices
  98 + stating that You changed the files; and
  99 +
  100 + (c) You must retain, in the Source form of any Derivative Works
  101 + that You distribute, all copyright, patent, trademark, and
  102 + attribution notices from the Source form of the Work,
  103 + excluding those notices that do not pertain to any part of
  104 + the Derivative Works; and
  105 +
  106 + (d) If the Work includes a "NOTICE" text file as part of its
  107 + distribution, then any Derivative Works that You distribute must
  108 + include a readable copy of the attribution notices contained
  109 + within such NOTICE file, excluding those notices that do not
  110 + pertain to any part of the Derivative Works, in at least one
  111 + of the following places: within a NOTICE text file distributed
  112 + as part of the Derivative Works; within the Source form or
  113 + documentation, if provided along with the Derivative Works; or,
  114 + within a display generated by the Derivative Works, if and
  115 + wherever such third-party notices normally appear. The contents
  116 + of the NOTICE file are for informational purposes only and
  117 + do not modify the License. You may add Your own attribution
  118 + notices within Derivative Works that You distribute, alongside
  119 + or as an addendum to the NOTICE text from the Work, provided
  120 + that such additional attribution notices cannot be construed
  121 + as modifying the License.
  122 +
  123 + You may add Your own copyright statement to Your modifications and
  124 + may provide additional or different license terms and conditions
  125 + for use, reproduction, or distribution of Your modifications, or
  126 + for any such Derivative Works as a whole, provided Your use,
  127 + reproduction, and distribution of the Work otherwise complies with
  128 + the conditions stated in this License.
  129 +
  130 + 5. Submission of Contributions. Unless You explicitly state otherwise,
  131 + any Contribution intentionally submitted for inclusion in the Work
  132 + by You to the Licensor shall be under the terms and conditions of
  133 + this License, without any additional terms or conditions.
  134 + Notwithstanding the above, nothing herein shall supersede or modify
  135 + the terms of any separate license agreement you may have executed
  136 + with Licensor regarding such Contributions.
  137 +
  138 + 6. Trademarks. This License does not grant permission to use the trade
  139 + names, trademarks, service marks, or product names of the Licensor,
  140 + except as required for reasonable and customary use in describing the
  141 + origin of the Work and reproducing the content of the NOTICE file.
  142 +
  143 + 7. Disclaimer of Warranty. Unless required by applicable law or
  144 + agreed to in writing, Licensor provides the Work (and each
  145 + Contributor provides its Contributions) on an "AS IS" BASIS,
  146 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  147 + implied, including, without limitation, any warranties or conditions
  148 + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
  149 + PARTICULAR PURPOSE. You are solely responsible for determining the
  150 + appropriateness of using or redistributing the Work and assume any
  151 + risks associated with Your exercise of permissions under this License.
  152 +
  153 + 8. Limitation of Liability. In no event and under no legal theory,
  154 + whether in tort (including negligence), contract, or otherwise,
  155 + unless required by applicable law (such as deliberate and grossly
  156 + negligent acts) or agreed to in writing, shall any Contributor be
  157 + liable to You for damages, including any direct, indirect, special,
  158 + incidental, or consequential damages of any character arising as a
  159 + result of this License or out of the use or inability to use the
  160 + Work (including but not limited to damages for loss of goodwill,
  161 + work stoppage, computer failure or malfunction, or any and all
  162 + other commercial damages or losses), even if such Contributor
  163 + has been advised of the possibility of such damages.
  164 +
  165 + 9. Accepting Warranty or Additional Liability. While redistributing
  166 + the Work or Derivative Works thereof, You may choose to offer,
  167 + and charge a fee for, acceptance of support, warranty, indemnity,
  168 + or other liability obligations and/or rights consistent with this
  169 + License. However, in accepting such obligations, You may act only
  170 + on Your own behalf and on Your sole responsibility, not on behalf
  171 + of any other Contributor, and only if You agree to indemnify,
  172 + defend, and hold each Contributor harmless for any liability
  173 + incurred by, or claims asserted against, such Contributor by reason
  174 + of your accepting any such warranty or additional liability.
  175 +
  176 + END OF TERMS AND CONDITIONS
  177 +
  178 + APPENDIX: How to apply the Apache License to your work.
  179 +
  180 + To apply the Apache License to your work, attach the following
  181 + boilerplate notice, with the fields enclosed by brackets "[]"
  182 + replaced with your own identifying information. (Don't include
  183 + the brackets!) The text should be enclosed in the appropriate
  184 + comment syntax for the file format. We also recommend that a
  185 + file or class name and description of purpose be included on the
  186 + same "printed page" as the copyright notice for easier
  187 + identification within third-party archives.
  188 +
  189 + Copyright [yyyy] [name of copyright owner]
  190 +
  191 + Licensed under the Apache License, Version 2.0 (the "License");
  192 + you may not use this file except in compliance with the License.
  193 + You may obtain a copy of the License at
  194 +
  195 + http://www.apache.org/licenses/LICENSE-2.0
  196 +
  197 + Unless required by applicable law or agreed to in writing, software
  198 + distributed under the License is distributed on an "AS IS" BASIS,
  199 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  200 + See the License for the specific language governing permissions and
  201 + limitations under the License.
\ No newline at end of file
... ...
... ... @@ -37,7 +37,9 @@
37 37 "node_modules/tooltipster/dist/css/plugins/tooltipster/sideTip/themes/tooltipster-sideTip-shadow.min.css",
38 38 "src/app/shared/components/json-form/react/json-form.scss",
39 39 "node_modules/rc-select/assets/index.less",
40   - "node_modules/jstree-bootstrap-theme/dist/themes/proton/style.min.css"
  40 + "node_modules/jstree-bootstrap-theme/dist/themes/proton/style.min.css",
  41 + "node_modules/leaflet/dist/leaflet.css",
  42 + "src/app/modules/home/components/widget/lib/maps/markers.scss"
41 43 ],
42 44 "stylePreprocessorOptions": {
43 45 "includePaths": [
... ...
... ... @@ -1833,6 +1833,12 @@
1833 1833 "resolved": "https://registry.npmjs.org/@types/flowjs/-/flowjs-2.13.1.tgz",
1834 1834 "integrity": "sha512-cPuORQrWmJV7pmiSt1ApDOsQSooVka53Ugr3LB0MW/bsG/fDtOXSxsT5Aiej98VD3eCIZNyABfk3NBWU7CorsQ=="
1835 1835 },
  1836 + "@types/geojson": {
  1837 + "version": "7946.0.7",
  1838 + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.7.tgz",
  1839 + "integrity": "sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ==",
  1840 + "dev": true
  1841 + },
1836 1842 "@types/glob": {
1837 1843 "version": "7.1.1",
1838 1844 "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz",
... ... @@ -1882,6 +1888,30 @@
1882 1888 "@types/jquery": "*"
1883 1889 }
1884 1890 },
  1891 + "@types/leaflet": {
  1892 + "version": "1.5.9",
  1893 + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.5.9.tgz",
  1894 + "integrity": "sha512-vFj2Y764lC6PpmmoFTui3yEMAKf+Dy08eGIaFgM+efJoKNxDP/bsEr2UHloP6kkJqc+4VO9+KYea2ZhiZCnNrQ==",
  1895 + "dev": true,
  1896 + "requires": {
  1897 + "@types/geojson": "*"
  1898 + }
  1899 + },
  1900 + "@types/leaflet-polylinedecorator": {
  1901 + "version": "1.6.0",
  1902 + "resolved": "https://registry.npmjs.org/@types/leaflet-polylinedecorator/-/leaflet-polylinedecorator-1.6.0.tgz",
  1903 + "integrity": "sha512-Z2BXZDjKEqHclwrAmhYdF1RwyFfa/NFxsoF79sitzaj5D/4YWHp/zDRcUZar5cQFKRgK66AYEIF7nKVuMzUGdw==",
  1904 + "dev": true,
  1905 + "requires": {
  1906 + "@types/leaflet": "*"
  1907 + }
  1908 + },
  1909 + "@types/lodash": {
  1910 + "version": "4.14.149",
  1911 + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz",
  1912 + "integrity": "sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==",
  1913 + "dev": true
  1914 + },
1885 1915 "@types/minimatch": {
1886 1916 "version": "3.0.3",
1887 1917 "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
... ... @@ -1894,9 +1924,9 @@
1894 1924 "integrity": "sha512-13gmo3M2qVvjQrWNseqM3+cR6S2Ss3grbR2NZltgMq94wOwqJYQdgn8qzwDshzgXqMlSUtyPZjysImmktu22ew=="
1895 1925 },
1896 1926 "@types/node": {
1897   - "version": "13.7.2",
1898   - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.2.tgz",
1899   - "integrity": "sha512-uvilvAQbdJvnSBFcKJ2td4016urcGvsiR+N4dHGU87ml8O2Vl6l+ErOi9w0kXSPiwJ1AYlIW+0pDXDWWMOiWbw==",
  1927 + "version": "13.7.6",
  1928 + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.6.tgz",
  1929 + "integrity": "sha512-eyK7MWD0R1HqVTp+PtwRgFeIsemzuj4gBFSQxfPHY5iMjS7474e5wq+VFgTcdpyHeNxyKSaetYAjdMLJlKoWqA==",
1900 1930 "dev": true
1901 1931 },
1902 1932 "@types/prop-types": {
... ... @@ -8240,6 +8270,47 @@
8240 8270 "invert-kv": "^2.0.0"
8241 8271 }
8242 8272 },
  8273 + "leaflet": {
  8274 + "version": "1.6.0",
  8275 + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.6.0.tgz",
  8276 + "integrity": "sha512-CPkhyqWUKZKFJ6K8umN5/D2wrJ2+/8UIpXppY7QDnUZW5bZL5+SEI2J7GBpwh4LIupOKqbNSQXgqmrEJopHVNQ=="
  8277 + },
  8278 + "leaflet-geometryutil": {
  8279 + "version": "0.9.3",
  8280 + "resolved": "https://registry.npmjs.org/leaflet-geometryutil/-/leaflet-geometryutil-0.9.3.tgz",
  8281 + "integrity": "sha512-Wi6YvfNx/Xu9q35AEfXpsUXmIFLen/MO+C2qimxHRnjyeyOxBhdcZa6kSiReaOX0cGK7yQInqrzz0dkIqZ8Dpg==",
  8282 + "requires": {
  8283 + "leaflet": ">=0.7.0"
  8284 + }
  8285 + },
  8286 + "leaflet-polylinedecorator": {
  8287 + "version": "1.6.0",
  8288 + "resolved": "https://registry.npmjs.org/leaflet-polylinedecorator/-/leaflet-polylinedecorator-1.6.0.tgz",
  8289 + "integrity": "sha1-nvef0bUwLWe3Lv6Vmo7NJVPycmY=",
  8290 + "requires": {
  8291 + "leaflet-rotatedmarker": "^0.2.0"
  8292 + }
  8293 + },
  8294 + "leaflet-providers": {
  8295 + "version": "1.9.1",
  8296 + "resolved": "https://registry.npmjs.org/leaflet-providers/-/leaflet-providers-1.9.1.tgz",
  8297 + "integrity": "sha512-YpJB9y4/nT5NGicU9vuqlttJaCer6paD3J3b8Wrw+IIQvK9dtcdzE9CsTkDg7Dg9FeGp5NEr3hu17xcHbYI/2w=="
  8298 + },
  8299 + "leaflet-rotatedmarker": {
  8300 + "version": "0.2.0",
  8301 + "resolved": "https://registry.npmjs.org/leaflet-rotatedmarker/-/leaflet-rotatedmarker-0.2.0.tgz",
  8302 + "integrity": "sha1-RGf0n5jRv9VpWb2cZwUgPdJgEnc="
  8303 + },
  8304 + "leaflet.gridlayer.googlemutant": {
  8305 + "version": "0.8.0",
  8306 + "resolved": "https://registry.npmjs.org/leaflet.gridlayer.googlemutant/-/leaflet.gridlayer.googlemutant-0.8.0.tgz",
  8307 + "integrity": "sha512-Ain+jgDKRhlM6qNDDj2QFJa9vXUqV096N0PmpHO3DoNLS4I7EynTQCJXN+9qY4C51ZpV4Q4CI+apNv5XiP5aUA=="
  8308 + },
  8309 + "leaflet.markercluster": {
  8310 + "version": "1.4.1",
  8311 + "resolved": "https://registry.npmjs.org/leaflet.markercluster/-/leaflet.markercluster-1.4.1.tgz",
  8312 + "integrity": "sha512-ZSEpE/EFApR0bJ1w/dUGwTSUvWlpalKqIzkaYdYB7jaftQA/Y2Jav+eT4CMtEYFj+ZK4mswP13Q2acnPBnhGOw=="
  8313 + },
8243 8314 "less": {
8244 8315 "version": "3.10.3",
8245 8316 "resolved": "https://registry.npmjs.org/less/-/less-3.10.3.tgz",
... ...
... ... @@ -56,6 +56,12 @@
56 56 "json-schema-defaults": "^0.4.0",
57 57 "jstree": "^3.3.9",
58 58 "jstree-bootstrap-theme": "^1.0.1",
  59 + "leaflet": "^1.6.0",
  60 + "leaflet-geometryutil": "^0.9.3",
  61 + "leaflet-polylinedecorator": "^1.6.0",
  62 + "leaflet-providers": "^1.9.1",
  63 + "leaflet.gridlayer.googlemutant": "^0.8.0",
  64 + "leaflet.markercluster": "^1.4.1",
59 65 "material-design-icons": "^3.0.1",
60 66 "messageformat": "^2.3.0",
61 67 "moment": "^2.24.0",
... ... @@ -99,7 +105,9 @@
99 105 "@types/jquery": "^3.3.32",
100 106 "@types/js-beautify": "^1.8.1",
101 107 "@types/jstree": "^3.3.39",
102   - "@types/node": "^13.7.2",
  108 + "@types/leaflet": "^1.5.9",
  109 + "@types/leaflet-polylinedecorator": "^1.6.0",
  110 + "@types/lodash": "^4.14.149",
103 111 "@types/raphael": "^2.1.30",
104 112 "@types/react": "^16.9.20",
105 113 "@types/react-dom": "^16.9.5",
... ...
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import { JsonSchema, JsonSettingsSchema } from '@app/shared/public-api';
  18 +
  19 +
  20 +export function initSchema(): JsonSettingsSchema {
  21 + return {
  22 + schema: {
  23 + type: 'object',
  24 + properties: {},
  25 + required: []
  26 + },
  27 + form: [],
  28 + groupInfoes: []
  29 + };
  30 +}
  31 +
  32 +export function addGroupInfo(schema: JsonSettingsSchema, title: string) {
  33 + schema.groupInfoes.push({
  34 + formIndex: schema.groupInfoes?.length || 0,
  35 + GroupTitle: title
  36 + });
  37 +}
  38 +
  39 +export function addToSchema(schema: JsonSettingsSchema, newSchema: JsonSettingsSchema) {
  40 + Object.assign(schema.schema.properties, newSchema.schema.properties);
  41 + schema.schema.required = schema.schema.required.concat(newSchema.schema.required);
  42 + schema.form.push(newSchema.form);
  43 +}
  44 +
  45 +export function mergeSchemes(schemes: JsonSettingsSchema[]): JsonSettingsSchema {
  46 + return schemes.reduce((finalSchema: JsonSettingsSchema, schema: JsonSettingsSchema) => {
  47 + return {
  48 + schema: {
  49 + properties: {
  50 + ...finalSchema.schema.properties,
  51 + ...schema.schema.properties
  52 + },
  53 + required: [
  54 + ...finalSchema.schema.required,
  55 + ...schema.schema.required
  56 + ]
  57 + },
  58 + form: [
  59 + ...finalSchema.form,
  60 + ...schema.form
  61 + ]
  62 + } as JsonSettingsSchema;
  63 + }, initSchema());
  64 +}
  65 +
  66 +export function addCondition(schema: JsonSettingsSchema, condition: String): JsonSettingsSchema {
  67 + schema.form = schema.form.map(element => {
  68 + if (typeof element === 'string') {
  69 + return {
  70 + key: element,
  71 + condition
  72 + }
  73 + }
  74 + if (typeof element == 'object') {
  75 + if (element.condition) {
  76 + element.condition += ' && ' + condition
  77 + }
  78 + else element.condition = condition;
  79 + }
  80 + return element;
  81 + });
  82 + return schema;
  83 +}
\ No newline at end of file
... ...
... ... @@ -15,8 +15,8 @@
15 15 ///
16 16
17 17 import _ from 'lodash';
18   -import { Observable, Subject } from 'rxjs';
19   -import { finalize, share } from 'rxjs/operators';
  18 +import { Observable, Subject, from, fromEvent, of } from 'rxjs';
  19 +import { finalize, share, map } from 'rxjs/operators';
20 20 import base64js from 'base64-js';
21 21
22 22 export function onParentScrollOrWindowResize(el: Node): Observable<Event> {
... ... @@ -101,7 +101,7 @@ export function isNumber(value: any): boolean {
101 101 }
102 102
103 103 export function isNumeric(value: any): boolean {
104   - return (value - parseFloat( value ) + 1) >= 0;
  104 + return (value - parseFloat(value) + 1) >= 0;
105 105 }
106 106
107 107 export function isString(value: any): boolean {
... ... @@ -221,6 +221,18 @@ function scrollParents(node: Node): Node[] {
221 221 return scrollParentNodes;
222 222 }
223 223
  224 +function hashCode(str) {
  225 + let hash = 0;
  226 + let i, char;
  227 + if (str.length == 0) return hash;
  228 + for (i = 0; i < str.length; i++) {
  229 + char = str.charCodeAt(i);
  230 + hash = ((hash << 5) - hash) + char;
  231 + hash = hash & hash; // Convert to 32bit integer
  232 + }
  233 + return hash;
  234 +}
  235 +
224 236 function easeInOut(
225 237 currentTime: number,
226 238 startTime: number,
... ... @@ -411,3 +423,147 @@ export function snakeCase(name: string, separator: string): string {
411 423 export function getDescendantProp(obj: any, path: string): any {
412 424 return path.split('.').reduce((acc, part) => acc && acc[part], obj);
413 425 }
  426 +
  427 +export function imageLoader(imageUrl: string): Observable<HTMLImageElement> {
  428 + const image = new Image();
  429 + const imageLoad$ = fromEvent(image, 'load').pipe(map(event => image));
  430 + image.src = imageUrl;
  431 + return imageLoad$;
  432 +}
  433 +
  434 +const imageAspectMap = {};
  435 +
  436 +export function aspectCache(imageUrl: string): Observable<number> {
  437 + if (imageUrl?.length) {
  438 + const hash = hashCode(imageUrl);
  439 + let aspect = imageAspectMap[hash];
  440 + if (aspect) {
  441 + return of(aspect);
  442 + }
  443 + else return imageLoader(imageUrl).pipe(map(image => {
  444 + aspect = image.width / image.height;
  445 + imageAspectMap[hash] = aspect;
  446 + return aspect;
  447 + }))
  448 + }
  449 +}
  450 +
  451 +
  452 +export function parseArray(input: any[]): any[] {
  453 + const alliases: any = _(input).groupBy(el => el?.datasource?.aliasName).values().value();
  454 + return alliases.map((alliasArray, dsIndex) =>
  455 + alliasArray[0].data.map((el, i) => {
  456 + const obj = {
  457 + aliasName: alliasArray[0]?.datasource?.aliasName,
  458 + entityName: alliasArray[0]?.datasource?.entityName,
  459 + $datasource: alliasArray[0]?.datasource,
  460 + dsIndex,
  461 + time: el[0],
  462 + deviceType: null
  463 + };
  464 + alliasArray.forEach(el => {
  465 + obj[el?.dataKey?.label] = el?.data[i][1];
  466 + obj[el?.dataKey?.label + '|ts'] = el?.data[0][0];
  467 + if (el?.dataKey?.label === 'type') {
  468 + obj.deviceType = el?.data[0][1];
  469 + }
  470 + });
  471 + return obj;
  472 + })
  473 + );
  474 +}
  475 +
  476 +export function parseData(input: any[]): any[] {
  477 + return _(input).groupBy(el => el?.datasource?.aliasName).values().value().map((alliasArray, i) => {
  478 + const obj = {
  479 + aliasName: alliasArray[0]?.datasource?.aliasName,
  480 + entityName: alliasArray[0]?.datasource?.entityName,
  481 + $datasource: alliasArray[0]?.datasource,
  482 + dsIndex: i,
  483 + deviceType: null
  484 + };
  485 + alliasArray.forEach(el => {
  486 + obj[el?.dataKey?.label] = el?.data[0][1];
  487 + obj[el?.dataKey?.label + '|ts'] = el?.data[0][0];
  488 + if (el?.dataKey?.label === 'type') {
  489 + obj.deviceType = el?.data[0][1];
  490 + }
  491 + });
  492 + return obj;
  493 + });
  494 +}
  495 +
  496 +export function safeExecute(func: Function, params = []) {
  497 + let res = null;
  498 + if (func && typeof (func) === 'function') {
  499 + try {
  500 + res = func(...params);
  501 + }
  502 + catch (err) {
  503 + console.log('error in external function:', err);
  504 + res = null;
  505 + }
  506 + }
  507 + return res;
  508 +}
  509 +
  510 +export function parseFunction(source: any, params: string[] = []): Function {
  511 + let res = null;
  512 + if (source?.length) {
  513 + try {
  514 + res = new Function(...params, source);
  515 + }
  516 + catch (err) {
  517 + console.error(err);
  518 + res = null;
  519 + }
  520 + }
  521 + return res;
  522 +}
  523 +
  524 +export function parseTemplate(template: string, data: object) {
  525 + let res = '';
  526 + try {
  527 + let variables = '';
  528 + const expressions = template
  529 + .match(/\{(.*?)\}/g) // find expressions
  530 + .map(exp => exp.replace(/{|}/g, '')) // remove brackets
  531 + .map(exp => exp.split(':'))
  532 + .map(arr => {
  533 + variables += `let ${arr[0]} = ''; `;
  534 + return arr;
  535 + })
  536 + .filter(arr => !!arr[1]) // filter expressions without format
  537 + .reduce((res, current) => {
  538 + res[current[0]] = current[1];
  539 + return res;
  540 + }, {});
  541 +
  542 + for (const key in data) {
  543 + if (!key.includes('|'))
  544 + variables += `${key} = '${expressions[key] ? padValue(data[key], +expressions[key]) : data[key]}';`;
  545 + }
  546 + template = template.replace(/:\d+}/g, '}');
  547 + res = safeExecute(parseFunction(variables + 'return' + '`' + template + '`'));
  548 + }
  549 + catch (ex) {
  550 + }
  551 + return res;
  552 +}
  553 +
  554 +export function padValue(val: any, dec: number): string {
  555 + let strVal;
  556 + let n;
  557 +
  558 + val = parseFloat(val);
  559 + n = (val < 0);
  560 + val = Math.abs(val);
  561 +
  562 + if (dec > 0) {
  563 + strVal = val.toFixed(dec).toString()
  564 + } else {
  565 + strVal = Math.round(val).toString();
  566 + }
  567 + strVal = (n ? '-' : '') + strVal;
  568 + return strVal;
  569 +}
\ No newline at end of file
... ...
... ... @@ -18,7 +18,7 @@ import * as CanvasGauges from 'canvas-gauges';
18 18 import { FontStyle, FontWeight } from '@home/components/widget/lib/settings.models';
19 19 import * as tinycolor_ from 'tinycolor2';
20 20 import { ColorFormats } from 'tinycolor2';
21   -import { isDefined, isString, isUndefined } from '@core/utils';
  21 +import { isDefined, isString, isUndefined, padValue } from '@core/utils';
22 22 import GenericOptions = CanvasGauges.GenericOptions;
23 23 import BaseGauge = CanvasGauges.BaseGauge;
24 24
... ... @@ -786,24 +786,6 @@ function drawDigitalMinMax(context: DigitalGaugeCanvasRenderingContext2D, option
786 786 drawText(context, options, 'MinMax', options.maxValue+'', maxX, maxY);
787 787 }
788 788
789   -function padValue(val: any, options: CanvasDigitalGaugeOptions): string {
790   - const dec = options.valueDec;
791   - let strVal;
792   - let n;
793   -
794   - val = parseFloat(val);
795   - n = (val < 0);
796   - val = Math.abs(val);
797   -
798   - if (dec > 0) {
799   - strVal = val.toFixed(dec).toString()
800   - } else {
801   - strVal = Math.round(val).toString();
802   - }
803   - strVal = (n ? '-' : '') + strVal;
804   - return strVal;
805   -}
806   -
807 789 function drawDigitalValue(context: DigitalGaugeCanvasRenderingContext2D, options: CanvasDigitalGaugeOptions, value: any) {
808 790 if (options.hideValue) return;
809 791
... ... @@ -813,7 +795,7 @@ function drawDigitalValue(context: DigitalGaugeCanvasRenderingContext2D, options
813 795 const textX = Math.round(baseX + width / 2);
814 796 const textY = valueY;
815 797
816   - let text = options.valueText || padValue(value, options);
  798 + let text = options.valueText || padValue(value, options.valueDec);
817 799 text += options.symbol;
818 800
819 801 context.save();
... ...
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import L from 'leaflet';
  18 +
  19 +import 'leaflet-providers';
  20 +import 'leaflet.markercluster/dist/MarkerCluster.css'
  21 +import 'leaflet.markercluster/dist/MarkerCluster.Default.css'
  22 +import 'leaflet.markercluster/dist/leaflet.markercluster'
  23 +
  24 +import { MapSettings, MarkerSettings, FormattedData, UnitedMapSettings, PolygonSettings } from './map-models';
  25 +import { Marker } from './markers';
  26 +import { Observable, of, BehaviorSubject, Subject } from 'rxjs';
  27 +import { filter } from 'rxjs/operators';
  28 +import { Polyline } from './polyline';
  29 +import { Polygon } from './polygon';
  30 +
  31 +export default abstract class LeafletMap {
  32 +
  33 + markers: Map<string, Marker> = new Map();
  34 + dragMode = true;
  35 + poly: Polyline;
  36 + polygon: Polygon;
  37 + map: L.Map;
  38 + map$: BehaviorSubject<L.Map> = new BehaviorSubject(null);
  39 + ready$: Observable<L.Map> = this.map$.pipe(filter(map => !!map));
  40 + options: UnitedMapSettings;
  41 + isMarketCluster: boolean;
  42 + bounds: L.LatLngBounds;
  43 + newMarker: L.Marker;
  44 + datasources: FormattedData[];
  45 +
  46 + constructor(public $container: HTMLElement, options: UnitedMapSettings) {
  47 + this.options = options;
  48 + }
  49 +
  50 + public initSettings(options: MapSettings) {
  51 + const { initCallback,
  52 + disableScrollZooming, }: MapSettings = options;
  53 + if (disableScrollZooming) {
  54 + this.map.scrollWheelZoom.disable();
  55 + }
  56 + if (initCallback) {
  57 + setTimeout(options.initCallback, 0);
  58 + }
  59 + }
  60 +
  61 + addMarkerControl() {
  62 + if (this.options.draggableMarker) {
  63 + let mousePositionOnMap: L.LatLng;
  64 + let addMarker: L.Control;
  65 + this.map.on('mouseup', (e: L.LeafletMouseEvent) => {
  66 + mousePositionOnMap = e.latlng
  67 + })
  68 + const dragListener = (e: L.DragEndEvent) => {
  69 + if (e.type === 'dragend' && mousePositionOnMap) {
  70 + const newMarker = L.marker(mousePositionOnMap).addTo(this.map);
  71 + const datasourcesList = document.createElement('div');
  72 + const customLatLng = this.convertToCustomFormat(mousePositionOnMap);
  73 + this.datasources.forEach(ds => {
  74 + const dsItem = document.createElement('p');
  75 + dsItem.appendChild(document.createTextNode(ds.entityName));
  76 + dsItem.setAttribute('style', 'font-size: 14px');
  77 + dsItem.onclick = () => {
  78 + const updatedEnttity = { ...ds, ...customLatLng };
  79 + this.saveMarkerLocation(updatedEnttity);
  80 + this.map.removeLayer(newMarker);
  81 + this.deleteMarker(ds.aliasName);
  82 + this.createMarker(ds.aliasName, updatedEnttity, this.datasources, this.options, false);
  83 + }
  84 + datasourcesList.append(dsItem);
  85 + })
  86 + const popup = L.popup();
  87 + popup.setContent(datasourcesList);
  88 + newMarker.bindPopup(popup).openPopup();
  89 +
  90 + }
  91 + addMarker.setPosition('topright')
  92 + }
  93 + L.Control.AddMarker = L.Control.extend({
  94 + onAdd(map) {
  95 + const img = L.DomUtil.create('img') as any;
  96 + img.src = `assets/add_location.svg`;
  97 + img.style.width = '32px';
  98 + img.style.height = '32px';
  99 + img.onclick = this.dragMarker;
  100 + img.draggable = true;
  101 + const draggableImg = new L.Draggable(img);
  102 + draggableImg.enable();
  103 + draggableImg.on('dragend', dragListener)
  104 + return img;
  105 + },
  106 + onRemove(map) {
  107 + },
  108 + dragMarker: this.dragMarker
  109 +
  110 + } as any);
  111 +
  112 + L.control.addMarker = (opts) => {
  113 + return new L.Control.AddMarker(opts);
  114 + }
  115 +
  116 + addMarker = L.control.addMarker({ position: 'topright' }).addTo(this.map);
  117 + }
  118 + }
  119 +
  120 + public setMap(map: L.Map) {
  121 + this.map = map;
  122 + if (this.options.useDefaultCenterPosition) {
  123 + this.map.panTo(this.options.defaultCenterPosition);
  124 + this.bounds = map.getBounds();
  125 + }
  126 + else this.bounds = new L.LatLngBounds(null, null);
  127 + if (this.options.draggableMarker) {
  128 + this.addMarkerControl();
  129 + }
  130 + this.map$.next(this.map);
  131 + }
  132 +
  133 + public setDataSources(dataSources) {
  134 + this.datasources = dataSources;
  135 + }
  136 +
  137 + public saveMarkerLocation(e) {
  138 +
  139 + }
  140 +
  141 + createLatLng(lat: number, lng: number): L.LatLng {
  142 + return L.latLng(lat, lng);
  143 + }
  144 +
  145 + createBounds(): L.LatLngBounds {
  146 + return this.map.getBounds();
  147 + }
  148 +
  149 + extendBounds(bounds: L.LatLngBounds, polyline: L.Polyline) {
  150 + if (polyline && polyline.getLatLngs() && polyline.getBounds()) {
  151 + bounds.extend(polyline.getBounds());
  152 + }
  153 + }
  154 +
  155 + invalidateSize() {
  156 + this.map?.invalidateSize(true);
  157 + }
  158 +
  159 + onResize() {
  160 +
  161 + }
  162 +
  163 + getCenter() {
  164 + return this.map.getCenter();
  165 + }
  166 +
  167 + convertPosition(expression: object): L.LatLng {
  168 + const lat = expression[this.options.latKeyName];
  169 + const lng = expression[this.options.lngKeyName];
  170 + if (isNaN(lat) || isNaN(lng))
  171 + return null;
  172 + else
  173 + return L.latLng(lat, lng) as L.LatLng;
  174 + }
  175 +
  176 + convertToCustomFormat(position: L.LatLng): object {
  177 + return {
  178 + [this.options.latKeyName]: position.lat,
  179 + [this.options.lngKeyName]: position.lng
  180 + }
  181 + }
  182 +
  183 + // Markers
  184 + updateMarkers(markersData) {
  185 + markersData.forEach(data => {
  186 + if (this.convertPosition(data)) {
  187 + if (data.rotationAngle) {
  188 + this.options.icon = L.divIcon({
  189 + html: `<div class="arrow" style="transform: rotate(${data.rotationAngle}deg)"><div>`
  190 + })
  191 + }
  192 + else {
  193 + this.options.icon = null;
  194 + }
  195 + if (this.markers.get(data.aliasName)) {
  196 + this.updateMarker(data.aliasName, data, markersData, this.options)
  197 + }
  198 + else {
  199 + this.createMarker(data.aliasName, data, markersData, this.options as MarkerSettings);
  200 + }
  201 + }
  202 + });
  203 + }
  204 +
  205 + dragMarker = (e, data?) => {
  206 + if (e.type !== 'dragend') return;
  207 + this.saveMarkerLocation({ ...data, ...this.convertToCustomFormat(e.target._latlng) });
  208 + }
  209 +
  210 + private createMarker(key, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings, setFocus = true) {
  211 + this.ready$.subscribe(() => {
  212 + const newMarker = new Marker(this.map, this.convertPosition(data), settings, data, dataSources, () => { }, this.dragMarker);
  213 + if (setFocus)
  214 + this.map.fitBounds(this.bounds.extend(newMarker.leafletMarker.getLatLng()));
  215 + this.markers.set(key, newMarker);
  216 + });
  217 + }
  218 +
  219 + private updateMarker(key, data, dataSources, settings: MarkerSettings) {
  220 + const marker: Marker = this.markers.get(key);
  221 + const location = this.convertPosition(data)
  222 + if (!location.equals(marker.location)) {
  223 + marker.updateMarkerPosition(location);
  224 + }
  225 + if (settings.showTooltip) {
  226 + marker.updateMarkerTooltip(data);
  227 + }
  228 + marker.setDataSources(data, dataSources);
  229 + marker.updateMarkerIcon(settings);
  230 + }
  231 +
  232 + deleteMarker(key) {
  233 + let marker = this.markers.get(key)?.leafletMarker;
  234 + if (marker) {
  235 + this.map.removeLayer(marker);
  236 + this.markers.delete(key);
  237 + marker = null;
  238 + }
  239 + }
  240 +
  241 + // Polyline
  242 +
  243 + updatePolylines(polyData: Array<Array<any>>) {
  244 + polyData.forEach(data => {
  245 + if (data.length) {
  246 + const dataSource = polyData.map(arr => arr[0]);
  247 + if (this.poly) {
  248 + this.updatePolyline(data, dataSource, this.options);
  249 + }
  250 + else {
  251 + this.createPolyline(data, dataSource, this.options);
  252 + }
  253 + }
  254 + })
  255 + }
  256 +
  257 + createPolyline(data: any[], dataSources, settings) {
  258 + if (data.length)
  259 + this.ready$.subscribe(() => {
  260 + this.poly = new Polyline(this.map,
  261 + data.map(el => this.convertPosition(el)).filter(el => !!el), data, dataSources, settings);
  262 + const bounds = this.bounds.extend(this.poly.leafletPoly.getBounds());
  263 + if (bounds.isValid()) {
  264 + this.map.fitBounds(bounds);
  265 + this.bounds = bounds;
  266 + }
  267 + });
  268 + }
  269 +
  270 + updatePolyline(data, dataSources, settings) {
  271 + this.ready$.subscribe(() => {
  272 + this.poly.updatePolyline(settings, data, dataSources);
  273 + });
  274 + }
  275 +
  276 + // Polygon
  277 +
  278 + updatePolygons(polyData: any[]) {
  279 + polyData.forEach((data: any) => {
  280 + if (data.data.length && data.dataKey.name === this.options.polygonKeyName) {
  281 + if (typeof (data?.data[0][1]) === 'string') {
  282 + data.data = JSON.parse(data.data[0][1]);
  283 + }
  284 + if (this.polygon) {
  285 + this.updatePolygon(data.data, polyData, this.options);
  286 + }
  287 + else {
  288 + this.createPolygon(data.data, polyData, this.options);
  289 + }
  290 + }
  291 + });
  292 + }
  293 +
  294 + createPolygon(data: FormattedData, dataSources: FormattedData[], settings: PolygonSettings) {
  295 + this.ready$.subscribe(() => {
  296 + this.polygon = new Polygon(this.map, data, dataSources, settings);
  297 + const bounds = this.bounds.extend(this.polygon.leafletPoly.getBounds());
  298 + if (bounds.isValid()) {
  299 + this.map.fitBounds(bounds);
  300 + this.bounds = bounds;
  301 + }
  302 + });
  303 + }
  304 +
  305 + updatePolygon(data, dataSources, settings) {
  306 + this.ready$.subscribe(() => {
  307 + // this.polygon.updatePolygon(settings, data, dataSources);
  308 + });
  309 + }
  310 +}
\ No newline at end of file
... ...
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License; Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing; software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS;
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND; either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import { LatLngTuple } from 'leaflet';
  18 +
  19 +export type GenericFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string;
  20 +export type MarkerImageFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string;
  21 +
  22 +export type MapSettings = {
  23 + polygonKeyName: any;
  24 + draggableMarker: boolean;
  25 + initCallback?: () => any;
  26 + defaultZoomLevel?: number;
  27 + dontFitMapBounds?: boolean;
  28 + disableScrollZooming?: boolean;
  29 + minZoomLevel?: number;
  30 + latKeyName?: string;
  31 + lngKeyName?: string;
  32 + xPosKeyName?: string;
  33 + yPosKeyName?: string;
  34 + mapProvider: MapProviders;
  35 + mapUrl?: string;
  36 + mapImageUrl?: string;
  37 + provider?: MapProviders;
  38 + credentials?: any; // declare credentials format
  39 + defaultCenterPosition?: LatLngTuple;
  40 + markerClusteringSetting?;
  41 + useDefaultCenterPosition?: boolean;
  42 + gmDefaultMapType?: string;
  43 + useLabelFunction: string;
  44 + icon?: any;
  45 +}
  46 +
  47 +export enum MapProviders {
  48 + google = 'google-map',
  49 + openstreet = 'openstreet-map',
  50 + here = 'here',
  51 + image = 'image-map',
  52 + tencent = 'tencent-map'
  53 +}
  54 +
  55 +export type MarkerSettings = {
  56 + tooltipPattern?: any;
  57 + icon?: any;
  58 + showLabel?: boolean;
  59 + label: string;
  60 + labelColor: string;
  61 + labelText: string;
  62 + useLabelFunction: boolean;
  63 + draggableMarker: boolean;
  64 + showTooltip?: boolean;
  65 + color?: string;
  66 + autocloseTooltip: boolean;
  67 + displayTooltipAction: string;
  68 + currentImage?: string;
  69 + useMarkerImageFunction?: boolean;
  70 + markerImages?: string[];
  71 + useMarkerImage: boolean;
  72 + markerImageSize: number;
  73 + markerImage: {
  74 + length: number
  75 + }
  76 +
  77 + colorFunction: GenericFunction;
  78 + tooltipFunction: GenericFunction;
  79 + labelFunction: GenericFunction;
  80 + markerImageFunction?: MarkerImageFunction;
  81 +}
  82 +
  83 +export interface FormattedData {
  84 + aliasName: string;
  85 + entityName: string;
  86 + $datasource: string;
  87 + dsIndex: number;
  88 + deviceType: string
  89 +}
  90 +
  91 +export type PolygonSettings = {
  92 + showPolygon: boolean;
  93 + showTooltip: any;
  94 + polygonStrokeOpacity: number;
  95 + polygonOpacity: number;
  96 + polygonStrokeWeight: number;
  97 + polygonStrokeColor: string;
  98 + polygonColor: string;
  99 + autocloseTooltip: boolean;
  100 + displayTooltipAction: string;
  101 +
  102 + polygonColorFunction?: GenericFunction;
  103 +}
  104 +
  105 +export type PolylineSettings = {
  106 + usePolylineDecorator: any;
  107 + autocloseTooltip: boolean;
  108 + displayTooltipAction: string;
  109 + useColorFunction: any;
  110 + color: string;
  111 + useStrokeOpacityFunction: any;
  112 + strokeOpacity: number;
  113 + useStrokeWeightFunction: any;
  114 + strokeWeight: number;
  115 + decoratorOffset: string | number;
  116 + endDecoratorOffset: string | number;
  117 + decoratorRepeat: string | number;
  118 + decoratorSymbol: any;
  119 + decoratorSymbolSize: any;
  120 + useDecoratorCustomColor: any;
  121 + decoratorCustomColor: any;
  122 +
  123 +
  124 + colorFunction: GenericFunction;
  125 + strokeOpacityFunction: GenericFunction;
  126 + strokeWeightFunction: GenericFunction;
  127 +}
  128 +
  129 +export interface HistorySelectSettings {
  130 + buttonColor: string;
  131 +}
  132 +
  133 +export type UnitedMapSettings = MapSettings & PolygonSettings & MarkerSettings & PolygonSettings;
\ No newline at end of file
... ...
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import { JsonSettingsSchema } from '@app/shared/public-api';
  18 +
  19 +export interface MapWidgetInterface {
  20 + resize(),
  21 + update(),
  22 + onInit(),
  23 + onDestroy();
  24 +}
  25 +
  26 +export interface MapWidgetStaticInterface {
  27 + settingsSchema(mapProvider?, drawRoutes?): JsonSettingsSchema;
  28 + getProvidersSchema():JsonSettingsSchema
  29 + dataKeySettingsSchema(): object;
  30 + actionSources(): object;
  31 +}
... ...
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import { MapProviders, UnitedMapSettings } from './map-models';
  18 +import LeafletMap from './leaflet-map';
  19 +import {
  20 + openstreetMapSettingsSchema,
  21 + googleMapSettingsSchema,
  22 + imageMapSettingsSchema,
  23 + tencentMapSettingsSchema,
  24 + commonMapSettingsSchema,
  25 + routeMapSettingsSchema,
  26 + markerClusteringSettingsSchema,
  27 + markerClusteringSettingsSchemaLeaflet,
  28 + hereMapSettingsSchema,
  29 + mapProviderSchema
  30 +} from './schemes';
  31 +import { MapWidgetStaticInterface, MapWidgetInterface } from './map-widget.interface';
  32 +import { OpenStreetMap, TencentMap, GoogleMap, HEREMap, ImageMap } from './providers';
  33 +import { parseFunction, parseArray, parseData } from '@app/core/utils';
  34 +import { initSchema, addToSchema, mergeSchemes, addCondition, addGroupInfo } from '@app/core/schema-utils';
  35 +import { AttributeScope, EntityId, JsonSettingsSchema } from '@app/shared/public-api';
  36 +import { forkJoin } from 'rxjs';
  37 +import { WidgetContext } from '@app/modules/home/models/widget-component.models';
  38 +import { AttributeService } from '@app/core/public-api';
  39 +import { getDefCenterPosition } from './maps-utils';
  40 +
  41 +let providerSets;
  42 +let defaultSettings;
  43 +
  44 +export class MapWidgetController implements MapWidgetInterface {
  45 +
  46 + constructor(public mapProvider: MapProviders, private drawRoutes: boolean, public ctx: WidgetContext, $element: HTMLElement) {
  47 + if (this.map) {
  48 + this.map.map.remove();
  49 + delete this.map;
  50 + }
  51 +
  52 + this.data = ctx.data;
  53 + if (!$element) {
  54 + $element = ctx.$container[0];
  55 + }
  56 + this.settings = this.initSettings(ctx.settings);
  57 +
  58 + const MapClass = providerSets[this.provider]?.MapClass;
  59 + if (!MapClass) {
  60 + return;
  61 + }
  62 + this.map = new MapClass($element, this.settings);
  63 + this.map.saveMarkerLocation = this.setMarkerLocation;
  64 + }
  65 +
  66 + map: LeafletMap;
  67 + provider: MapProviders;
  68 + schema: JsonSettingsSchema;
  69 + data;
  70 + settings: UnitedMapSettings;
  71 +
  72 + public static dataKeySettingsSchema(): object {
  73 + return {};
  74 + }
  75 +
  76 + public static getProvidersSchema() {
  77 + return mergeSchemes([mapProviderSchema,
  78 + ...Object.values(providerSets)?.map(
  79 + (setting: IProvider) => addCondition(setting?.schema, `model.provider === '${setting.name}'`))]);
  80 + }
  81 +
  82 + public static settingsSchema(mapProvider: MapProviders, drawRoutes: boolean): JsonSettingsSchema {
  83 + const schema = initSchema();
  84 + addToSchema(schema, this.getProvidersSchema());
  85 + addGroupInfo(schema, 'Map Provider Settings');
  86 + addToSchema(schema, commonMapSettingsSchema);
  87 + addGroupInfo(schema, 'Common Map Settings');
  88 +
  89 + if (drawRoutes) {
  90 + addToSchema(schema, routeMapSettingsSchema);
  91 + addGroupInfo(schema, 'Route Map Settings');
  92 + } else if (mapProvider !== 'image-map') {
  93 + const clusteringSchema = mergeSchemes([markerClusteringSettingsSchemaLeaflet, markerClusteringSettingsSchema])
  94 + addToSchema(schema, clusteringSchema);
  95 + addGroupInfo(schema, 'Markers Clustering Settings');
  96 + }
  97 + return schema;
  98 + }
  99 +
  100 + public static actionSources(): object {
  101 + return {
  102 + markerClick: {
  103 + name: 'widget-action.marker-click',
  104 + multiple: false
  105 + },
  106 + polygonClick: {
  107 + name: 'widget-action.polygon-click',
  108 + multiple: false
  109 + },
  110 + tooltipAction: {
  111 + name: 'widget-action.tooltip-tag-action',
  112 + multiple: true
  113 + }
  114 + };
  115 + }
  116 +
  117 + onInit() {
  118 + }
  119 +
  120 + setMarkerLocation = (e) => {
  121 + const attributeService = this.ctx.$injector.get(AttributeService);
  122 + forkJoin(
  123 + this.data.filter(data => !!e[data.dataKey.name])
  124 + .map(data => {
  125 + const entityId: EntityId = {
  126 + entityType: data.datasource.entityType,
  127 + id: data.datasource.entityId
  128 + };
  129 + return attributeService.saveEntityAttributes(
  130 + entityId,
  131 + AttributeScope.SHARED_SCOPE,
  132 + [{
  133 + key: data.dataKey.name,
  134 + value: e[data.dataKey.name]
  135 + }]
  136 + );
  137 + })).subscribe(res => {
  138 + console.log('MapWidgetController -> setMarkerLocation -> res', res)
  139 + });
  140 + }
  141 +
  142 + initSettings(settings: UnitedMapSettings) {
  143 + const functionParams = ['data', 'dsData', 'dsIndex'];
  144 + this.provider = settings.provider || this.mapProvider;
  145 + const customOptions = {
  146 + provider: this.provider,
  147 + mapUrl: settings?.mapImageUrl,
  148 + labelFunction: parseFunction(settings.labelFunction, functionParams),
  149 + tooltipFunction: parseFunction(settings.tooltipFunction, functionParams),
  150 + colorFunction: parseFunction(settings.colorFunction, functionParams),
  151 + polygonColorFunction: parseFunction(settings.polygonColorFunction, functionParams),
  152 + markerImageFunction: parseFunction(settings.markerImageFunction, ['data', 'images', 'dsData', 'dsIndex']),
  153 + labelColor: this.ctx.widgetConfig.color,
  154 + tooltipPattern: settings.tooltipPattern ||
  155 + '<b>${entityName}</b><br/><br/><b>Latitude:</b> ${' +
  156 + settings.latKeyName + ':7}<br/><b>Longitude:</b> ${' + settings.lngKeyName + ':7}',
  157 + defaultCenterPosition: getDefCenterPosition(settings?.defaultCenterPosition),
  158 + currentImage: (settings.useMarkerImage && settings.markerImage?.length) ? {
  159 + url: settings.markerImage,
  160 + size: settings.markerImageSize || 34
  161 + } : null
  162 + }
  163 + return { ...defaultSettings, ...settings, ...customOptions, }
  164 + }
  165 +
  166 + update() {
  167 + if (this.drawRoutes)
  168 + this.map.updatePolylines(parseArray(this.data));
  169 + if (this.settings.showPolygon) {
  170 + this.map.updatePolygons(this.data);
  171 + }
  172 + if (this.settings.draggableMarker) {
  173 + this.map.setDataSources(parseData(this.data));
  174 + }
  175 + else
  176 + this.map.updateMarkers(parseData(this.data));
  177 + }
  178 +
  179 + resize() {
  180 + this.map?.invalidateSize();
  181 + this.map.onResize();
  182 + }
  183 +
  184 + onDestroy() {
  185 + }
  186 +}
  187 +
  188 +export let TbMapWidgetV2: MapWidgetStaticInterface = MapWidgetController;
  189 +
  190 +interface IProvider {
  191 + MapClass: LeafletMap,
  192 + schema: JsonSettingsSchema,
  193 + name: string
  194 +}
  195 +
  196 +providerSets = {
  197 + 'openstreet-map': {
  198 + MapClass: OpenStreetMap,
  199 + schema: openstreetMapSettingsSchema,
  200 + name: 'openstreet-map',
  201 + },
  202 + 'tencent-map': {
  203 + MapClass: TencentMap,
  204 + schema: tencentMapSettingsSchema,
  205 + name: 'tencent-map'
  206 + },
  207 + 'google-map': {
  208 + MapClass: GoogleMap,
  209 + schema: googleMapSettingsSchema,
  210 + name: 'google-map'
  211 + },
  212 + here: {
  213 + MapClass: HEREMap,
  214 + schema: hereMapSettingsSchema,
  215 + name: 'here'
  216 + },
  217 + 'image-map': {
  218 + MapClass: ImageMap,
  219 + schema: imageMapSettingsSchema,
  220 + name: 'image-map'
  221 + }
  222 +}
  223 +
  224 +defaultSettings = {
  225 + xPosKeyName: 'xPos',
  226 + yPosKeyName: 'yPos',
  227 + markerOffsetX: 0.5,
  228 + markerOffsetY: 1,
  229 + latKeyName: 'latitude',
  230 + lngKeyName: 'longitude',
  231 + polygonKeyName: 'coordinates',
  232 + showLabel: false,
  233 + label: '${entityName}',
  234 + showTooltip: false,
  235 + useDefaultCenterPosition: false,
  236 + showTooltipAction: 'click',
  237 + autocloseTooltip: false,
  238 + showPolygon: true,
  239 + labelColor: '#000000',
  240 + color: '#FE7569',
  241 + polygonColor: '#0000ff',
  242 + polygonStrokeColor: '#fe0001',
  243 + polygonOpacity: 0.5,
  244 + polygonStrokeOpacity: 1,
  245 + polygonStrokeWeight: 1,
  246 + useLabelFunction: false,
  247 + markerImages: [],
  248 + strokeWeight: 2,
  249 + strokeOpacity: 1.0,
  250 + initCallback: () => { },
  251 + defaultZoomLevel: 8,
  252 + dontFitMapBounds: false,
  253 + disableScrollZooming: false,
  254 + minZoomLevel: 16,
  255 + credentials: '',
  256 + markerClusteringSetting: null,
  257 + draggableMarker: false
  258 +}
\ No newline at end of file
... ...
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import L from 'leaflet';
  18 +import _ from 'lodash';
  19 +import { MarkerSettings, PolylineSettings, PolygonSettings } from './map-models';
  20 +
  21 +export function createTooltip(target: L.Layer, settings: MarkerSettings | PolylineSettings | PolygonSettings): L.Popup {
  22 + const popup = L.popup();
  23 + popup.setContent('');
  24 + target.bindPopup(popup, { autoClose: settings.autocloseTooltip, closeOnClick: false });
  25 + if (settings.displayTooltipAction === 'hover') {
  26 + target.off('click');
  27 + target.on('mouseover', function () {
  28 + this.openPopup();
  29 + });
  30 + target.on('mouseout', function () {
  31 + this.closePopup();
  32 + });
  33 + }
  34 + return popup;
  35 +}
  36 +
  37 +export function getRatio(firsMoment: number, secondMoment: number, intermediateMoment: number): number {
  38 + return (intermediateMoment - firsMoment) / (secondMoment - firsMoment);
  39 +};
  40 +
  41 +export function findAngle(startPoint, endPoint) {
  42 + let angle = -Math.atan2(endPoint.latitude - startPoint.latitude, endPoint.longitude - startPoint.longitude);
  43 + angle = angle * 180 / Math.PI;
  44 + return parseInt(angle.toFixed(2), 10);
  45 +}
  46 +
  47 +
  48 +export function getDefCenterPosition(position) {
  49 + if (typeof (position) === 'string')
  50 + return position.split(',');
  51 + if (typeof (position) === 'object')
  52 + return position;
  53 + return [0, 0];
  54 +}
  55 +
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +.arrow {
  17 + height: 30px;
  18 + width: 30px;
  19 + 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");
  20 + background-size: cover;
  21 + background-repeat: no-repeat;
  22 + background-position: center center;
  23 +}
  24 +
  25 +.leaflet-div-icon,
  26 +.tb-marker-label,
  27 +.tb-marker-label:before {
  28 + border: none;
  29 + background: none;
  30 + box-shadow: none;
  31 +}
... ...
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import L from 'leaflet';
  18 +import { MarkerSettings, FormattedData } from './map-models';
  19 +import { aspectCache, safeExecute, parseTemplate } from '@app/core/utils';
  20 +import { createTooltip } from './maps-utils';
  21 +
  22 +export class Marker {
  23 +
  24 + leafletMarker: L.Marker;
  25 + tooltipOffset: [number, number];
  26 + tooltip: L.Popup;
  27 + location: L.LatLngExpression;
  28 + data: FormattedData;
  29 + dataSources: FormattedData[];
  30 +
  31 + constructor(private map: L.Map, location: L.LatLngExpression, public settings: MarkerSettings,
  32 + data?, dataSources?, onClickListener?, onDragendListener?) {
  33 + this.setDataSources(data, dataSources);
  34 + this.leafletMarker = L.marker(location, {
  35 + draggable: settings.draggableMarker
  36 + });
  37 +
  38 + this.createMarkerIcon((iconInfo) => {
  39 + this.leafletMarker.setIcon(iconInfo.icon);
  40 + this.tooltipOffset = [0, -iconInfo.size[1] + 10];
  41 + this.updateMarkerLabel(settings);
  42 + this.leafletMarker.addTo(map);
  43 + });
  44 +
  45 + if (settings.showTooltip) {
  46 + this.tooltip = createTooltip(this.leafletMarker, settings);
  47 + this.tooltip.setContent(parseTemplate(this.settings.tooltipPattern, data));
  48 + }
  49 +
  50 + if (onClickListener) {
  51 + this.leafletMarker.on('click', onClickListener);
  52 + }
  53 +
  54 + if (onDragendListener) {
  55 + this.leafletMarker.on('dragend', (e) => onDragendListener(e, this.data));
  56 + }
  57 + }
  58 +
  59 + setDataSources(data: FormattedData, dataSources: FormattedData[]) {
  60 + this.data = data;
  61 + this.dataSources = dataSources;
  62 + }
  63 +
  64 + updateMarkerTooltip(data: FormattedData) {
  65 + this.tooltip.setContent(parseTemplate(this.settings.tooltipPattern, data));
  66 + }
  67 +
  68 + updateMarkerPosition(position: L.LatLngExpression) {
  69 + this.leafletMarker.setLatLng(position);
  70 + }
  71 +
  72 + updateMarkerLabel(settings: MarkerSettings) {
  73 + this.leafletMarker.unbindTooltip();
  74 +
  75 + if (settings.showLabel) {
  76 + if (settings.useLabelFunction) {
  77 + settings.labelText = safeExecute(settings.labelFunction, [this.data, this.dataSources, this.data.dsIndex])
  78 + }
  79 + else settings.labelText = parseTemplate(settings.label, this.data);
  80 + this.leafletMarker.bindTooltip(`<div style="color: ${settings.labelColor};"><b>${settings.labelText}</b></div>`,
  81 + { className: 'tb-marker-label', permanent: true, direction: 'top', offset: this.tooltipOffset });
  82 + }
  83 + }
  84 +
  85 + updateMarkerColor(color) {
  86 + this.createDefaultMarkerIcon(color, (iconInfo) => {
  87 + this.leafletMarker.setIcon(iconInfo.icon);
  88 + });
  89 + }
  90 +
  91 + updateMarkerIcon(settings: MarkerSettings) {
  92 + this.createMarkerIcon((iconInfo) => {
  93 + this.leafletMarker.setIcon(iconInfo.icon);
  94 + this.tooltipOffset = [0, -iconInfo.size[1] + 10];
  95 + this.updateMarkerLabel(settings);
  96 + });
  97 + }
  98 +
  99 + createMarkerIcon(onMarkerIconReady) {
  100 +
  101 + if (this.settings.icon) {
  102 + onMarkerIconReady({
  103 + size: [30, 30],
  104 + icon: this.settings.icon,
  105 + });
  106 + return;
  107 + }
  108 +
  109 + const currentImage = this.settings.useMarkerImageFunction ?
  110 + safeExecute(this.settings.markerImageFunction,
  111 + [this.data, this.settings.markerImages, this.dataSources, this.data.dsIndex]) : this.settings.currentImage;
  112 +
  113 +
  114 + if (currentImage && currentImage.url) {
  115 + aspectCache(currentImage.url).subscribe(
  116 + (aspect) => {
  117 + if (aspect) {
  118 + let width;
  119 + let height;
  120 + if (aspect > 1) {
  121 + width = currentImage.size;
  122 + height = currentImage.size / aspect;
  123 + } else {
  124 + width = currentImage.size * aspect;
  125 + height = currentImage.size;
  126 + }
  127 + const icon = L.icon({
  128 + iconUrl: currentImage.url,
  129 + iconSize: [width, height],
  130 + iconAnchor: [width / 2, height],
  131 + popupAnchor: [0, -height]
  132 + });
  133 + const iconInfo = {
  134 + size: [width, height],
  135 + icon
  136 + };
  137 + onMarkerIconReady(iconInfo);
  138 + } else {
  139 + this.createDefaultMarkerIcon(this.settings.color, onMarkerIconReady);
  140 + }
  141 + }
  142 + );
  143 + } else {
  144 + this.createDefaultMarkerIcon(this.settings.color, onMarkerIconReady);
  145 + }
  146 + }
  147 +
  148 + createDefaultMarkerIcon(color, onMarkerIconReady) {
  149 + const pinColor = color.substr(1);
  150 + const icon = L.icon({
  151 + iconUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|' + pinColor,
  152 + iconSize: [21, 34],
  153 + iconAnchor: [10, 34],
  154 + popupAnchor: [0, -34],
  155 + shadowUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_shadow',
  156 + shadowSize: [40, 37],
  157 + shadowAnchor: [12, 35]
  158 + });
  159 + const iconInfo = {
  160 + size: [21, 34],
  161 + icon
  162 + };
  163 + onMarkerIconReady(iconInfo);
  164 + }
  165 +
  166 +
  167 +
  168 + removeMarker() {
  169 + /* this.map$.subscribe(map =>
  170 + this.leafletMarker.addTo(map))*/
  171 + }
  172 +
  173 + extendBoundsWithMarker(bounds) {
  174 + bounds.extend(this.leafletMarker.getLatLng());
  175 + }
  176 +
  177 + getMarkerPosition() {
  178 + return this.leafletMarker.getLatLng();
  179 + }
  180 +
  181 + setMarkerPosition(latLng) {
  182 + this.leafletMarker.setLatLng(latLng);
  183 + }
  184 +}
\ No newline at end of file
... ...
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import L, { LatLngExpression } from 'leaflet';
  18 +import { createTooltip } from './maps-utils';
  19 +import { PolygonSettings } from './map-models';
  20 +
  21 +export class Polygon {
  22 +
  23 + leafletPoly: L.Polygon;
  24 + tooltip;
  25 +
  26 + constructor(public map, coordinates, dataSources, settings: PolygonSettings, onClickListener?) {
  27 + this.leafletPoly = L.polygon(coordinates, {
  28 + fill: true,
  29 + fillColor: settings.polygonColor,
  30 + color: settings.polygonStrokeColor,
  31 + weight: settings.polygonStrokeWeight,
  32 + fillOpacity: settings.polygonOpacity,
  33 + opacity: settings.polygonStrokeOpacity
  34 + }).addTo(this.map);
  35 +
  36 + if (settings.showTooltip) {
  37 + this.tooltip = createTooltip(this.leafletPoly, settings);
  38 + }
  39 + if (onClickListener) {
  40 + this.leafletPoly.on('click', onClickListener);
  41 + }
  42 + }
  43 +
  44 + removePolygon() {
  45 + this.map.removeLayer(this.leafletPoly);
  46 + }
  47 +
  48 + updatePolygonColor(settings) {
  49 + console.log('Polygon -> updatePolygonColor -> settings', settings)
  50 + const style: L.PathOptions = {
  51 +
  52 + fill: true,
  53 + fillColor: settings.color,
  54 + color: settings.color,
  55 + weight: settings.polygonStrokeWeight,
  56 + fillOpacity: settings.polygonOpacity,
  57 + opacity: settings.polygonStrokeOpacity
  58 + };
  59 + this.leafletPoly.setStyle(style);
  60 + }
  61 +
  62 + getPolygonLatLngs() {
  63 + return this.leafletPoly.getLatLngs();
  64 + }
  65 +
  66 + setPolygonLatLngs(latLngs: LatLngExpression[]) {
  67 + this.leafletPoly.setLatLngs(latLngs);
  68 + this.leafletPoly.redraw();
  69 + }
  70 +}
\ No newline at end of file
... ...
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import L, { PolylineDecoratorOptions } from 'leaflet';
  18 +import 'leaflet-polylinedecorator';
  19 +
  20 +import { safeExecute } from '@app/core/utils';
  21 +import { PolylineSettings } from './map-models';
  22 +
  23 +export class Polyline {
  24 +
  25 + leafletPoly: L.Polyline;
  26 + polylineDecorator: L.PolylineDecorator;
  27 + dataSources;
  28 + data;
  29 +
  30 + constructor(private map: L.Map, locations, data, dataSources, settings: PolylineSettings) {
  31 + this.dataSources = dataSources;
  32 + this.data = data;
  33 +
  34 + this.leafletPoly = L.polyline(locations,
  35 + this.getPolyStyle(settings)
  36 + ).addTo(this.map);
  37 + if (settings.usePolylineDecorator) {
  38 + this.polylineDecorator = L.polylineDecorator(this.leafletPoly, this.getDecoratorSettings(settings)).addTo(this.map);
  39 + }
  40 + }
  41 +
  42 + getDecoratorSettings(settings: PolylineSettings): PolylineDecoratorOptions {
  43 + return {
  44 + patterns: [
  45 + {
  46 + offset: settings.decoratorOffset,
  47 + endOffset: settings.endDecoratorOffset,
  48 + repeat: settings.decoratorRepeat,
  49 + symbol: L.Symbol[settings.decoratorSymbol]({
  50 + pixelSize: settings.decoratorSymbolSize,
  51 + polygon: false,
  52 + pathOptions: {
  53 + color: settings.useDecoratorCustomColor ? settings.decoratorCustomColor : this.getPolyStyle(settings).color,
  54 + stroke: true
  55 + }
  56 + })
  57 + }
  58 + ],
  59 + interactive: false,
  60 + } as PolylineDecoratorOptions
  61 + }
  62 +
  63 + updatePolyline(settings, data, dataSources) {
  64 + this.leafletPoly.setStyle(this.getPolyStyle(settings));
  65 + // this.setPolylineLatLngs(data);
  66 + if(this.polylineDecorator)
  67 + this.polylineDecorator.setPaths(this.leafletPoly);
  68 + }
  69 +
  70 + getPolyStyle(settings: PolylineSettings): L.PolylineOptions {
  71 + return {
  72 + color: settings.useColorFunction ?
  73 + safeExecute(settings.colorFunction, [this.data, this.dataSources, this.data[0]?.dsIndex]) : settings.color,
  74 + opacity: settings.useStrokeOpacityFunction ?
  75 + safeExecute(settings.strokeOpacityFunction, [this.data, this.dataSources, this.data[0]?.dsIndex]) : settings.strokeOpacity,
  76 + weight: settings.useStrokeWeightFunction ?
  77 + safeExecute(settings.strokeWeightFunction, [this.data, this.dataSources, this.data[0]?.dsIndex]) : settings.strokeWeight,
  78 + }
  79 + }
  80 +
  81 + removePolyline() {
  82 + this.map.removeLayer(this.leafletPoly);
  83 + }
  84 +
  85 + getPolylineLatLngs() {
  86 + return this.leafletPoly.getLatLngs();
  87 + }
  88 +
  89 + setPolylineLatLngs(latLngs) {
  90 + this.leafletPoly.setLatLngs(latLngs);
  91 + }
  92 +}
\ No newline at end of file
... ...
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +
  18 +import L from 'leaflet';
  19 +import LeafletMap from '../leaflet-map';
  20 +import { MapSettings, UnitedMapSettings } from '../map-models';
  21 +import 'leaflet.gridlayer.googlemutant';
  22 +
  23 +let googleLoaded = false;
  24 +
  25 +
  26 +export class GoogleMap extends LeafletMap {
  27 + constructor($container, options: UnitedMapSettings) {
  28 +
  29 + super($container, options);
  30 + this.loadGoogle(() => {
  31 + const map = L.map($container).setView(options?.defaultCenterPosition, options?.defaultZoomLevel);
  32 + const roads = (L.gridLayer as any).googleMutant({
  33 + type: options?.gmDefaultMapType || 'roadmap'
  34 + }).addTo(map);
  35 + super.setMap(map);
  36 + }, options.credentials.apiKey);
  37 + super.initSettings(options);
  38 + }
  39 +
  40 + private loadGoogle(callback, apiKey = 'AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q') {
  41 + if (googleLoaded) {
  42 + callback()
  43 + }
  44 + else {
  45 + googleLoaded = true;
  46 + const script = document.createElement('script');
  47 + script.onload = () => {
  48 + callback();
  49 + }
  50 + script.onerror = () => {
  51 + googleLoaded = false;
  52 + }
  53 + script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}`;
  54 + document.getElementsByTagName('head')[0].appendChild(script);
  55 + }
  56 + }
  57 +}
\ No newline at end of file
... ...
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import L from 'leaflet';
  18 +import LeafletMap from '../leaflet-map';
  19 +import { MapSettings, UnitedMapSettings } from '../map-models';
  20 +
  21 +export class HEREMap extends LeafletMap {
  22 + constructor($container, options: UnitedMapSettings) {
  23 + const defaultCredentials =
  24 + {
  25 + app_id: 'AhM6TzD9ThyK78CT3ptx',
  26 + app_code: 'p6NPiITB3Vv0GMUFnkLOOg'
  27 + }
  28 + super($container, options);
  29 + const map = L.map($container).setView(options?.defaultCenterPosition, options?.defaultZoomLevel);
  30 + const tileLayer = (L.tileLayer as any).provider(options.mapProvider || 'HERE.normalDay', options.credentials || defaultCredentials);
  31 + tileLayer.addTo(map);
  32 + super.setMap(map);
  33 + super.initSettings(options);
  34 + }
  35 +}
\ No newline at end of file
... ...
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import L from 'leaflet';
  18 +import LeafletMap from '../leaflet-map';
  19 +import { MapSettings, UnitedMapSettings } from '../map-models';
  20 +import { aspectCache } from '@app/core/utils';
  21 +
  22 +const maxZoom = 4;// ?
  23 +
  24 +export class ImageMap extends LeafletMap {
  25 +
  26 + imageOverlay;
  27 + aspect = 0;
  28 + width = 0;
  29 + height = 0;
  30 +
  31 + constructor($container: HTMLElement, options: UnitedMapSettings) {
  32 + super($container, options);
  33 + aspectCache(options.mapUrl).subscribe(aspect => {
  34 + this.aspect = aspect;
  35 + this.onResize();
  36 + super.setMap(this.map);
  37 + super.initSettings(options);
  38 + });
  39 + }
  40 +
  41 + updateBounds(updateImage?, lastCenterPos?) {
  42 + const w = this.width;
  43 + const h = this.height;
  44 + let southWest = this.pointToLatLng(0, h);
  45 + let northEast = this.pointToLatLng(w, 0);
  46 + const bounds = new L.LatLngBounds(southWest, northEast);
  47 +
  48 + if (updateImage && this.imageOverlay) {
  49 + this.imageOverlay.remove();
  50 + this.imageOverlay = null;
  51 + }
  52 +
  53 + if (this.imageOverlay) {
  54 + this.imageOverlay.setBounds(bounds);
  55 + } else {
  56 + this.imageOverlay = L.imageOverlay(this.options.mapUrl, bounds).addTo(this.map);
  57 +
  58 + }
  59 + const padding = 200 * maxZoom;
  60 + southWest = this.pointToLatLng(-padding, h + padding);
  61 + northEast = this.pointToLatLng(w + padding, -padding);
  62 + const maxBounds = new L.LatLngBounds(southWest, northEast);
  63 + this.map.setMaxBounds(maxBounds);
  64 + if (lastCenterPos) {
  65 + lastCenterPos.x *= w;
  66 + lastCenterPos.y *= h;
  67 + /* this.ctx.$scope.$injector.get('$mdUtil').nextTick(() => {
  68 + this.map.panTo(center, { animate: false });
  69 + });*/
  70 + }
  71 + }
  72 +
  73 + onResize(updateImage?) {
  74 + let width = this.$container.clientWidth;
  75 + if (width > 0 && this.aspect) {
  76 + let height = width / this.aspect;
  77 + const imageMapHeight = this.$container.clientHeight;
  78 + if (imageMapHeight > 0 && height > imageMapHeight) {
  79 + height = imageMapHeight;
  80 + width = height * this.aspect;
  81 + }
  82 + width *= maxZoom;
  83 + const prevWidth = this.width;
  84 + const prevHeight = this.height;
  85 + if (this.width !== width) {
  86 + this.width = width;
  87 + this.height = width / this.aspect;
  88 + if (!this.map) {
  89 + this.initMap(updateImage);
  90 + } else {
  91 + const lastCenterPos = this.latLngToPoint(this.map.getCenter());
  92 + lastCenterPos.x /= prevWidth;
  93 + lastCenterPos.y /= prevHeight;
  94 + this.updateBounds(updateImage, lastCenterPos);
  95 + this.map.invalidateSize(true);
  96 + // this.updateMarkers();
  97 + }
  98 +
  99 + }
  100 + }
  101 + }
  102 +
  103 + initMap(updateImage?) {
  104 + if (!this.map && this.aspect > 0) {
  105 + const center = this.pointToLatLng(this.width / 2, this.height / 2);
  106 + this.map = L.map(this.$container, {
  107 + minZoom: 1,
  108 + maxZoom,
  109 + scrollWheelZoom: !this.options.disableScrollZooming,
  110 + center,
  111 + zoom: 1,
  112 + crs: L.CRS.Simple,
  113 + attributionControl: false
  114 + });
  115 + this.updateBounds(updateImage);
  116 + // this.updateMarkers();
  117 + }
  118 + }
  119 +
  120 + convertPosition(expression): L.LatLng {
  121 + return this.pointToLatLng(
  122 + expression[this.options.xPosKeyName] * this.width,
  123 + expression[this.options.yPosKeyName] * this.height);
  124 + }
  125 +
  126 + pointToLatLng(x, y): L.LatLng {
  127 + return L.CRS.Simple.pointToLatLng({ x, y } as L.PointExpression, maxZoom - 1);
  128 + }
  129 +
  130 + latLngToPoint(latLng) {
  131 + return L.CRS.Simple.latLngToPoint(latLng, maxZoom - 1);
  132 + }
  133 +}
\ No newline at end of file
... ...
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +export * from './tencent-map';
  18 +export * from './google-map';
  19 +export * from './here-map';
  20 +export * from './image-map';
  21 +export * from './openstreet-map';
... ...
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import L from 'leaflet';
  18 +import LeafletMap from '../leaflet-map';
  19 +import { MapSettings, UnitedMapSettings } from '../map-models';
  20 +
  21 +export class OpenStreetMap extends LeafletMap {
  22 + constructor($container, options: UnitedMapSettings) {
  23 + super($container, options);
  24 + const map = L.map($container).setView(options?.defaultCenterPosition, options?.defaultZoomLevel);
  25 + const tileLayer = (L.tileLayer as any).provider('OpenStreetMap.Mapnik');
  26 + tileLayer.addTo(map);
  27 + super.setMap(map);
  28 + super.initSettings(options);
  29 + }
  30 +}
\ No newline at end of file
... ...
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +
  18 +import L from 'leaflet';
  19 +import LeafletMap from '../leaflet-map';
  20 +import { MapSettings, UnitedMapSettings } from '../map-models';
  21 +
  22 +export class TencentMap extends LeafletMap {
  23 + constructor($container, options: UnitedMapSettings) {
  24 + super($container, options);
  25 + const txUrl = 'http://rt{s}.map.gtimg.com/realtimerender?z={z}&x={x}&y={y}&type=vector&style=0';
  26 + const map = L.map($container).setView(options?.defaultCenterPosition, options?.defaultZoomLevel);
  27 + const txLayer = L.tileLayer(txUrl, { subdomains: '0123', tms: true }).addTo(map);
  28 + txLayer.addTo(map);
  29 + super.setMap(map);
  30 + super.initSettings(options);
  31 + }
  32 +}
\ No newline at end of file
... ...
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +export const googleMapSettingsSchema =
  18 +{
  19 + schema: {
  20 + title: 'Google Map Configuration',
  21 + type: 'object',
  22 + properties: {
  23 + gmApiKey: {
  24 + title: 'Google Maps API Key',
  25 + type: 'string'
  26 + },
  27 + gmDefaultMapType: {
  28 + title: 'Default map type',
  29 + type: 'string',
  30 + default: 'roadmap'
  31 + }
  32 + },
  33 + required: [
  34 + ]
  35 + },
  36 + form: [
  37 + 'gmApiKey',
  38 + {
  39 + key: 'gmDefaultMapType',
  40 + type: 'rc-select',
  41 + multiple: false,
  42 + items: [
  43 + {
  44 + value: 'roadmap',
  45 + label: 'Roadmap'
  46 + },
  47 + {
  48 + value: 'satellite',
  49 + label: 'Satellite'
  50 + },
  51 + {
  52 + value: 'hybrid',
  53 + label: 'Hybrid'
  54 + },
  55 + {
  56 + value: 'terrain',
  57 + label: 'Terrain'
  58 + }
  59 + ]
  60 + }
  61 + ]
  62 +};
  63 +
  64 +export const tencentMapSettingsSchema =
  65 +{
  66 + schema: {
  67 + title: 'Tencent Map Configuration',
  68 + type: 'object',
  69 + properties: {
  70 + tmApiKey: {
  71 + title: 'Tencent Maps API Key',
  72 + type: 'string'
  73 + },
  74 + tmDefaultMapType: {
  75 + title: 'Default map type',
  76 + type: 'string',
  77 + default: 'roadmap'
  78 + }
  79 + },
  80 + required: [
  81 + ]
  82 + },
  83 + form: [
  84 + 'tmApiKey',
  85 + {
  86 + key: 'tmDefaultMapType',
  87 + type: 'rc-select',
  88 + multiple: false,
  89 + items: [
  90 + {
  91 + value: 'roadmap',
  92 + label: 'Roadmap'
  93 + },
  94 + {
  95 + value: 'satellite',
  96 + label: 'Satellite'
  97 + },
  98 + {
  99 + value: 'hybrid',
  100 + label: 'Hybrid'
  101 + },
  102 + ]
  103 + }
  104 + ]
  105 +};
  106 +
  107 +export const hereMapSettingsSchema =
  108 +{
  109 + schema: {
  110 + title: 'HERE Map Configuration',
  111 + type: 'object',
  112 + properties: {
  113 + mapProvider: {
  114 + title: 'Map layer',
  115 + type: 'string',
  116 + default: 'HERE.normalDay'
  117 + },
  118 + credentials: {
  119 + type: 'object',
  120 + properties: {
  121 + app_id: {
  122 + title: 'HERE app id',
  123 + type: 'string'
  124 + },
  125 + app_code: {
  126 + title: 'HERE app code',
  127 + type: 'string'
  128 + }
  129 + },
  130 + required: ['app_id', 'app_code']
  131 + }
  132 + },
  133 + required: []
  134 + },
  135 + form: [
  136 + {
  137 + key: 'mapProvider',
  138 + type: 'rc-select',
  139 + multiple: false,
  140 + items: [
  141 + {
  142 + value: 'HERE.normalDay',
  143 + label: 'HERE.normalDay (Default)'
  144 + },
  145 + {
  146 + value: 'HERE.normalNight',
  147 + label: 'HERE.normalNight'
  148 + },
  149 + {
  150 + value: 'HERE.hybridDay',
  151 + label: 'HERE.hybridDay'
  152 + },
  153 + {
  154 + value: 'HERE.terrainDay',
  155 + label: 'HERE.terrainDay'
  156 + }
  157 + ]
  158 + },
  159 + 'credentials'
  160 + ]
  161 +};
  162 +
  163 +export const openstreetMapSettingsSchema =
  164 +{
  165 + schema: {
  166 + title: 'Openstreet Map Configuration',
  167 + type: 'object',
  168 + properties: {
  169 + mapProvider: {
  170 + title: 'Map provider',
  171 + type: 'string',
  172 + default: 'OpenStreetMap.Mapnik'
  173 + },
  174 + useCustomProvider: {
  175 + title: 'Use custom provider',
  176 + type: 'boolean',
  177 + default: false
  178 + },
  179 + customProviderTileUrl: {
  180 + title: 'Custom provider tile URL',
  181 + type: 'string',
  182 + default: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
  183 + }
  184 + },
  185 + required: []
  186 + },
  187 + form: [
  188 + {
  189 + key: 'mapProvider',
  190 + type: 'rc-select',
  191 + multiple: false,
  192 + items: [
  193 + {
  194 + value: 'OpenStreetMap.Mapnik',
  195 + label: 'OpenStreetMap.Mapnik (Default)'
  196 + },
  197 + {
  198 + value: 'OpenStreetMap.BlackAndWhite',
  199 + label: 'OpenStreetMap.BlackAndWhite'
  200 + },
  201 + {
  202 + value: 'OpenStreetMap.HOT',
  203 + label: 'OpenStreetMap.HOT'
  204 + },
  205 + {
  206 + value: 'Esri.WorldStreetMap',
  207 + label: 'Esri.WorldStreetMap'
  208 + },
  209 + {
  210 + value: 'Esri.WorldTopoMap',
  211 + label: 'Esri.WorldTopoMap'
  212 + },
  213 + {
  214 + value: 'CartoDB.Positron',
  215 + label: 'CartoDB.Positron'
  216 + },
  217 + {
  218 + value: 'CartoDB.DarkMatter',
  219 + label: 'CartoDB.DarkMatter'
  220 + }
  221 + ]
  222 + },
  223 + 'useCustomProvider',
  224 + {
  225 + key: 'customProviderTileUrl',
  226 + condition: 'model.useCustomProvider === true',
  227 + }
  228 + ]
  229 +};
  230 +
  231 +export const commonMapSettingsSchema =
  232 +{
  233 + schema: {
  234 + title: 'Map Configuration',
  235 + type: 'object',
  236 + properties: {
  237 + defaultZoomLevel: {
  238 + title: 'Default map zoom level (0 - 20)',
  239 + type: 'number'
  240 + },
  241 + useDefaultCenterPosition: {
  242 + title: 'Use default map center position',
  243 + type: 'boolean',
  244 + default: false
  245 + },
  246 + defaultCenterPosition: {
  247 + title: 'Default map center position (0,0)',
  248 + type: 'string',
  249 + default: '0,0'
  250 + },
  251 + fitMapBounds: {
  252 + title: 'Fit map bounds to cover all markers',
  253 + type: 'boolean',
  254 + default: true
  255 + },
  256 + disableScrollZooming: {
  257 + title: 'Disable scroll zooming',
  258 + type: 'boolean',
  259 + default: false
  260 + },
  261 + latKeyName: {
  262 + title: 'Latitude key name',
  263 + type: 'string',
  264 + default: 'latitude'
  265 + },
  266 + lngKeyName: {
  267 + title: 'Longitude key name',
  268 + type: 'string',
  269 + default: 'longitude'
  270 + },
  271 + showLabel: {
  272 + title: 'Show label',
  273 + type: 'boolean',
  274 + default: true
  275 + },
  276 + label: {
  277 + title: 'Label (pattern examples: \'${entityName}\', \'${entityName}: (Text ${keyName} units.)\' )',
  278 + type: 'string',
  279 + default: '${entityName}'
  280 + },
  281 + useLabelFunction: {
  282 + title: 'Use label function',
  283 + type: 'boolean',
  284 + default: false
  285 + },
  286 + labelFunction: {
  287 + title: 'Label function: f(data, dsData, dsIndex)',
  288 + type: 'string'
  289 + },
  290 + showTooltip: {
  291 + title: 'Show tooltip',
  292 + type: 'boolean',
  293 + default: true
  294 + },
  295 + showTooltipAction: {
  296 + title: 'Action for displaying the tooltip',
  297 + type: 'string',
  298 + default: 'click'
  299 + },
  300 + autocloseTooltip: {
  301 + title: 'Auto-close tooltips',
  302 + type: 'boolean',
  303 + default: true
  304 + },
  305 + tooltipPattern: {
  306 + title: 'Tooltip (for ex. \'Text ${keyName} units.\' or <link-act name=\'my-action\'>Link text</link-act>\')',
  307 + type: 'string',
  308 + default: '<b>${entityName}</b><br/><br/><b>Latitude:</b> ${latitude:7}<br/><b>Longitude:</b> ${longitude:7}'
  309 + },
  310 + useTooltipFunction: {
  311 + title: 'Use tooltip function',
  312 + type: 'boolean',
  313 + default: false
  314 + },
  315 + tooltipFunction: {
  316 + title: 'Tooltip function: f(data, dsData, dsIndex)',
  317 + type: 'string'
  318 + },
  319 + color: {
  320 + title: 'Color',
  321 + type: 'string'
  322 + },
  323 + useColorFunction: {
  324 + title: 'Use color function',
  325 + type: 'boolean',
  326 + default: false
  327 + },
  328 + colorFunction: {
  329 + title: 'Color function: f(data, dsData, dsIndex)',
  330 + type: 'string'
  331 + },
  332 + showPolygon: {
  333 + title: 'Show polygon',
  334 + type: 'boolean',
  335 + default: false
  336 + },
  337 + polygonKeyName: {
  338 + title: 'Polygon key name',
  339 + type: 'string',
  340 + default: 'coordinates'
  341 + },
  342 + polygonColor: {
  343 + title: 'Polygon color',
  344 + type: 'string'
  345 + },
  346 + polygonOpacity: {
  347 + title: 'Polygon opacity',
  348 + type: 'number',
  349 + default: 0.5
  350 + },
  351 + polygonStrokeColor: {
  352 + title: 'Stroke color',
  353 + type: 'string'
  354 + },
  355 + polygonStrokeOpacity: {
  356 + title: 'Stroke opacity',
  357 + type: 'number',
  358 + default: 1
  359 + },
  360 + polygonStrokeWeight: {
  361 + title: 'Stroke weight',
  362 + type: 'number',
  363 + default: 1
  364 + },
  365 + usePolygonColorFunction: {
  366 + title: 'Use polygon color function',
  367 + type: 'boolean',
  368 + default: false
  369 + },
  370 + polygonColorFunction: {
  371 + title: 'Polygon Color function: f(data, dsData, dsIndex)',
  372 + type: 'string'
  373 + },
  374 + draggableMarker: {
  375 + title: 'Draggable Marker',
  376 + type: 'boolean',
  377 + default: false
  378 + },
  379 + markerImage: {
  380 + title: 'Custom marker image',
  381 + type: 'string'
  382 + },
  383 + markerImageSize: {
  384 + title: 'Custom marker image size (px)',
  385 + type: 'number',
  386 + default: 34
  387 + },
  388 + useMarkerImageFunction: {
  389 + title: 'Use marker image function',
  390 + type: 'boolean',
  391 + default: false
  392 + },
  393 + markerImageFunction: {
  394 + title: 'Marker image function: f(data, images, dsData, dsIndex)',
  395 + type: 'string'
  396 + },
  397 + markerImages: {
  398 + title: 'Marker images',
  399 + type: 'array',
  400 + items: {
  401 + title: 'Marker image',
  402 + type: 'string'
  403 + }
  404 + }
  405 + },
  406 + required: []
  407 + },
  408 + form: [
  409 + 'defaultZoomLevel',
  410 + 'useDefaultCenterPosition',
  411 + 'defaultCenterPosition',
  412 + 'fitMapBounds',
  413 + 'disableScrollZooming',
  414 + 'latKeyName',
  415 + 'lngKeyName',
  416 + 'showLabel',
  417 + 'label',
  418 + 'useLabelFunction',
  419 + 'draggableMarker',
  420 + {
  421 + key: 'labelFunction',
  422 + type: 'javascript'
  423 + },
  424 + 'showTooltip',
  425 + {
  426 + key: 'showTooltipAction',
  427 + type: 'rc-select',
  428 + multiple: false,
  429 + items: [
  430 + {
  431 + value: 'click',
  432 + label: 'Show tooltip on click (Default)'
  433 + },
  434 + {
  435 + value: 'hover',
  436 + label: 'Show tooltip on hover'
  437 + }
  438 + ]
  439 + },
  440 + 'autocloseTooltip',
  441 + {
  442 + key: 'tooltipPattern',
  443 + type: 'textarea'
  444 + },
  445 + 'useTooltipFunction',
  446 + {
  447 + key: 'tooltipFunction',
  448 + type: 'javascript'
  449 + },
  450 + {
  451 + key: 'color',
  452 + type: 'color'
  453 + },
  454 + 'useColorFunction',
  455 + {
  456 + key: 'colorFunction',
  457 + type: 'javascript'
  458 + }, 'showPolygon', 'polygonKeyName',
  459 + {
  460 + key: 'polygonColor',
  461 + type: 'color'
  462 + },
  463 + 'polygonOpacity',
  464 + {
  465 + key: 'polygonStrokeColor',
  466 + type: 'color'
  467 + },
  468 + 'polygonStrokeOpacity', 'polygonStrokeWeight', 'usePolygonColorFunction',
  469 + {
  470 + key: 'polygonColorFunction',
  471 + type: 'javascript'
  472 + },
  473 + {
  474 + key: 'markerImage',
  475 + type: 'image'
  476 + },
  477 + 'markerImageSize',
  478 + 'useMarkerImageFunction',
  479 + {
  480 + key: 'markerImageFunction',
  481 + type: 'javascript'
  482 + },
  483 + {
  484 + key: 'markerImages',
  485 + items: [
  486 + {
  487 + key: 'markerImages[]',
  488 + type: 'image'
  489 + }
  490 + ]
  491 + }
  492 + ]
  493 +};
  494 +
  495 +export const routeMapSettingsSchema =
  496 +{
  497 + schema: {
  498 + title: 'Route Map Configuration',
  499 + type: 'object',
  500 + properties: {
  501 + strokeWeight: {
  502 + title: 'Stroke weight',
  503 + type: 'number',
  504 + default: 2
  505 + },
  506 + strokeOpacity: {
  507 + title: 'Stroke opacity',
  508 + type: 'number',
  509 + default: 1.0
  510 + }
  511 + },
  512 + required: []
  513 + },
  514 + form: [
  515 + 'strokeWeight',
  516 + 'strokeOpacity'
  517 + ]
  518 +};
  519 +
  520 +export const markerClusteringSettingsSchema =
  521 +{
  522 + schema: {
  523 + title: 'Markers Clustering Configuration',
  524 + type: 'object',
  525 + properties: {
  526 + useClusterMarkers: {
  527 + title: 'Use map markers clustering',
  528 + type: 'boolean',
  529 + default: false
  530 + },
  531 + zoomOnClick: {
  532 + title: 'Zoom when clicking on a cluster',
  533 + type: 'boolean',
  534 + default: true
  535 + },
  536 + maxZoom: {
  537 + title: 'The maximum zoom level when a marker can be part of a cluster (0 - 18)',
  538 + type: 'number'
  539 + }
  540 + },
  541 + required: []
  542 + },
  543 + form: [
  544 + 'useClusterMarkers',
  545 + 'zoomOnClick',
  546 + 'maxZoom'
  547 + ]
  548 +};
  549 +
  550 +export const markerClusteringSettingsSchemaGoogle =
  551 +{
  552 + schema: {
  553 + title: 'Marker Clustering Configuration Google',
  554 + type: 'object',
  555 + properties: {
  556 + gridSize: {
  557 + title: 'Maximum radius that a cluster will cover in pixels',
  558 + type: 'number',
  559 + default: 60
  560 + },
  561 + minimumClusterSize: {
  562 + title: 'The minimum number of markers in a cluster',
  563 + type: 'number'
  564 + }
  565 + },
  566 + required: []
  567 + },
  568 + form: [
  569 + 'gridSize',
  570 + 'minimumClusterSize'
  571 + ]
  572 +};
  573 +
  574 +export const markerClusteringSettingsSchemaLeaflet =
  575 +{
  576 + schema: {
  577 + title: 'Markers Clustering Configuration Leaflet',
  578 + type: 'object',
  579 + properties: {
  580 + showCoverageOnHover: {
  581 + title: 'Show the bounds of markers when mouse over a cluster',
  582 + type: 'boolean',
  583 + default: true
  584 + },
  585 + animate: {
  586 + title: 'Show animation on markers when zooming',
  587 + type: 'boolean',
  588 + default: true
  589 + },
  590 + maxClusterRadius: {
  591 + title: 'Maximum radius that a cluster will cover in pixels',
  592 + type: 'number',
  593 + default: 80
  594 + },
  595 + chunkedLoading: {
  596 + title: 'Use chunks for adding markers so that the page does not freeze',
  597 + type: 'boolean',
  598 + default: false
  599 + },
  600 + removeOutsideVisibleBounds: {
  601 + title: 'Use lazy load for adding markers',
  602 + type: 'boolean',
  603 + default: true
  604 + }
  605 + },
  606 + required: []
  607 + },
  608 + form: [
  609 + 'showCoverageOnHover',
  610 + 'animate',
  611 + 'maxClusterRadius',
  612 + 'chunkedLoading',
  613 + 'removeOutsideVisibleBounds'
  614 + ]
  615 +};
  616 +
  617 +export const imageMapSettingsSchema =
  618 +{
  619 + schema: {
  620 + title: 'Image Map Configuration',
  621 + type: 'object',
  622 + properties: {
  623 + mapImageUrl: {
  624 + title: 'Image map background',
  625 + type: 'string',
  626 + default: 'data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg=='
  627 + },
  628 + imageEntityAlias: {
  629 + title: 'Image URL source entity alias',
  630 + type: 'string',
  631 + default: ''
  632 + },
  633 + imageUrlAttribute: {
  634 + title: 'Image URL source entity attribute',
  635 + type: 'string',
  636 + default: ''
  637 + },
  638 + disableScrollZooming: {
  639 + title: 'Disable scroll zooming',
  640 + type: 'boolean',
  641 + default: false
  642 + },
  643 + xPosKeyName: {
  644 + title: 'X position key name',
  645 + type: 'string',
  646 + default: 'xPos'
  647 + },
  648 + yPosKeyName: {
  649 + title: 'Y position key name',
  650 + type: 'string',
  651 + default: 'yPos'
  652 + },
  653 + showLabel: {
  654 + title: 'Show label',
  655 + type: 'boolean',
  656 + default: true
  657 + },
  658 + label: {
  659 + title: 'Label (pattern examples: \'${entityName}\', \'${entityName}: (Text ${keyName} units.)\' )',
  660 + type: 'string',
  661 + default: '${entityName}'
  662 + },
  663 + useLabelFunction: {
  664 + title: 'Use label function',
  665 + type: 'boolean',
  666 + default: false
  667 + },
  668 + labelFunction: {
  669 + title: 'Label function: f(data, dsData, dsIndex)',
  670 + type: 'string'
  671 + },
  672 + showTooltip: {
  673 + title: 'Show tooltip',
  674 + type: 'boolean',
  675 + default: true
  676 + },
  677 + showTooltipAction: {
  678 + title: 'Action for displaying the tooltip',
  679 + type: 'string',
  680 + default: 'click'
  681 + },
  682 + autocloseTooltip: {
  683 + title: 'Auto-close tooltips',
  684 + type: 'boolean',
  685 + default: true
  686 + },
  687 + tooltipPattern: {
  688 + title: 'Tooltip (for ex. \'Text ${keyName} units.\' or <link-act name=\'my-action\'>Link text</link-act>\')',
  689 + type: 'string',
  690 + default: '<b>${entityName}</b><br/><br/><b>X Pos:</b> ${xPos:2}<br/><b>Y Pos:</b> ${yPos:2}'
  691 + },
  692 + useTooltipFunction: {
  693 + title: 'Use tooltip function',
  694 + type: 'boolean',
  695 + default: false
  696 + },
  697 + tooltipFunction: {
  698 + title: 'Tooltip function: f(data, dsData, dsIndex)',
  699 + type: 'string'
  700 + },
  701 + color: {
  702 + title: 'Color',
  703 + type: 'string'
  704 + },
  705 + posFunction: {
  706 + title: 'Position conversion function: f(origXPos, origYPos), should return x,y coordinates as double from 0 to 1 each',
  707 + type: 'string',
  708 + default: 'return {x: origXPos, y: origYPos};'
  709 + },
  710 + markerOffsetX: {
  711 + title: 'Marker X offset relative to position',
  712 + type: 'number',
  713 + default: 0.5
  714 + },
  715 + markerOffsetY: {
  716 + title: 'Marker Y offset relative to position',
  717 + type: 'number',
  718 + default: 1
  719 + },
  720 + useColorFunction: {
  721 + title: 'Use color function',
  722 + type: 'boolean',
  723 + default: false
  724 + },
  725 + colorFunction: {
  726 + title: 'Color function: f(data, dsData, dsIndex)',
  727 + type: 'string'
  728 + },
  729 + markerImage: {
  730 + title: 'Custom marker image',
  731 + type: 'string'
  732 + },
  733 + markerImageSize: {
  734 + title: 'Custom marker image size (px)',
  735 + type: 'number',
  736 + default: 34
  737 + },
  738 + useMarkerImageFunction: {
  739 + title: 'Use marker image function',
  740 + type: 'boolean',
  741 + default: false
  742 + },
  743 + markerImageFunction: {
  744 + title: 'Marker image function: f(data, images, dsData, dsIndex)',
  745 + type: 'string'
  746 + },
  747 + markerImages: {
  748 + title: 'Marker images',
  749 + type: 'array',
  750 + items: {
  751 + title: 'Marker image',
  752 + type: 'string'
  753 + }
  754 + }
  755 + },
  756 + required: []
  757 + },
  758 + form: [
  759 + {
  760 + key: 'mapImageUrl',
  761 + type: 'image'
  762 + },
  763 + 'imageEntityAlias',
  764 + 'imageUrlAttribute',
  765 + 'disableScrollZooming',
  766 + 'xPosKeyName',
  767 + 'yPosKeyName',
  768 + 'showLabel',
  769 + 'label',
  770 + 'useLabelFunction',
  771 + {
  772 + key: 'labelFunction',
  773 + type: 'javascript'
  774 + },
  775 + 'showTooltip',
  776 + {
  777 + key: 'showTooltipAction',
  778 + type: 'rc-select',
  779 + multiple: false,
  780 + items: [
  781 + {
  782 + value: 'click',
  783 + label: 'Show tooltip on click (Default)'
  784 + },
  785 + {
  786 + value: 'hover',
  787 + label: 'Show tooltip on hover'
  788 + }
  789 + ]
  790 + },
  791 + 'autocloseTooltip',
  792 + {
  793 + key: 'tooltipPattern',
  794 + type: 'textarea'
  795 + },
  796 + 'useTooltipFunction',
  797 + {
  798 + key: 'tooltipFunction',
  799 + type: 'javascript'
  800 + },
  801 + {
  802 + key: 'color',
  803 + type: 'color'
  804 + },
  805 + {
  806 + key: 'posFunction',
  807 + type: 'javascript'
  808 + },
  809 + 'markerOffsetX',
  810 + 'markerOffsetY',
  811 + 'useColorFunction',
  812 + {
  813 + key: 'colorFunction',
  814 + type: 'javascript'
  815 + },
  816 + {
  817 + key: 'markerImage',
  818 + type: 'image'
  819 + },
  820 + 'markerImageSize',
  821 + 'useMarkerImageFunction',
  822 + {
  823 + key: 'markerImageFunction',
  824 + type: 'javascript'
  825 + },
  826 + {
  827 + key: 'markerImages',
  828 + items: [
  829 + {
  830 + key: 'markerImages[]',
  831 + type: 'image'
  832 + }
  833 + ]
  834 + }
  835 + ]
  836 +};
  837 +
  838 +export const mapProviderSchema =
  839 +{
  840 + schema: {
  841 + title: 'Map Provider Configuration',
  842 + type: 'object',
  843 + properties: {
  844 + provider: {
  845 + title: 'Map Provider',
  846 + type: 'string',
  847 + default: 'openstreet-map'
  848 + }
  849 + },
  850 + required: []
  851 + },
  852 + form: [
  853 + {
  854 + key: 'provider',
  855 + type: 'rc-select',
  856 + multiple: false,
  857 + items: [
  858 + {
  859 + value: 'google-map',
  860 + label: 'Google maps'
  861 + },
  862 + {
  863 + value: 'openstreet-map',
  864 + label: 'Openstreet maps'
  865 + },
  866 + {
  867 + value: 'here',
  868 + label: 'HERE maps'
  869 + },
  870 + {
  871 + value: 'image-map',
  872 + label: 'Image map'
  873 + },
  874 + {
  875 + value: 'tencent-map',
  876 + label: 'Tencent maps'
  877 + }
  878 + ]
  879 + }
  880 + ]
  881 +};
  882 +
  883 +
  884 +export const tripAnimationSchema = {
  885 + schema: {
  886 + title: 'Openstreet Map Configuration',
  887 + type: 'object',
  888 + properties: {
  889 + normalizationStep: {
  890 + title: 'Normalization data step (ms)',
  891 + type: 'number',
  892 + default: 1000
  893 + },
  894 + latKeyName: {
  895 + title: 'Latitude key name',
  896 + type: 'string',
  897 + default: 'latitude'
  898 + },
  899 + lngKeyName: {
  900 + title: 'Longitude key name',
  901 + type: 'string',
  902 + default: 'longitude'
  903 + },
  904 + polKeyName: {
  905 + title: 'Polygon key name',
  906 + type: 'string',
  907 + default: 'coordinates'
  908 + },
  909 + showLabel: {
  910 + title: 'Show label',
  911 + type: 'boolean',
  912 + default: true
  913 + },
  914 + label: {
  915 + title: 'Label (pattern examples: \'${entityName}\', \'${entityName}: (Text ${keyName} units.)\' )',
  916 + type: 'string',
  917 + default: '${entityName}'
  918 + },
  919 + useLabelFunction: {
  920 + title: 'Use label function',
  921 + type: 'boolean',
  922 + default: false
  923 + },
  924 + labelFunction: {
  925 + title: 'Label function: f(data, dsData, dsIndex)',
  926 + type: 'string'
  927 + },
  928 + showTooltip: {
  929 + title: 'Show tooltip',
  930 + type: 'boolean',
  931 + default: true
  932 + },
  933 + tooltipColor: {
  934 + title: 'Tooltip background color',
  935 + type: 'string',
  936 + default: '#fff'
  937 + },
  938 + tooltipFontColor: {
  939 + title: 'Tooltip font color',
  940 + type: 'string',
  941 + default: '#000'
  942 + },
  943 + tooltipOpacity: {
  944 + title: 'Tooltip opacity (0-1)',
  945 + type: 'number',
  946 + default: 1
  947 + },
  948 + tooltipPattern: {
  949 + title: 'Tooltip (for ex. \'Text ${keyName} units.\' or <link-act name=\'my-action\'>Link text</link-act>\')',
  950 + type: 'string',
  951 + default: '<b>${entityName}</b><br/><br/><b>Latitude:</b> ${latitude:7}<br/><b>Longitude:</b> ${longitude:7}'
  952 + },
  953 + useTooltipFunction: {
  954 + title: 'Use tooltip function',
  955 + type: 'boolean',
  956 + default: false
  957 + },
  958 + tooltipFunction: {
  959 + title: 'Tooltip function: f(data, dsData, dsIndex)',
  960 + type: 'string'
  961 + },
  962 + color: {
  963 + title: 'Path color',
  964 + type: 'string'
  965 + },
  966 + strokeWeight: {
  967 + title: 'Stroke weight',
  968 + type: 'number',
  969 + default: 2
  970 + },
  971 + strokeOpacity: {
  972 + title: 'Stroke opacity',
  973 + type: 'number',
  974 + default: 1
  975 + },
  976 + useColorFunction: {
  977 + title: 'Use path color function',
  978 + type: 'boolean',
  979 + default: false
  980 + },
  981 + colorFunction: {
  982 + title: 'Path color function: f(data, dsData, dsIndex)',
  983 + type: 'string'
  984 + },
  985 + usePolylineDecorator: {
  986 + title: 'Use path decorator',
  987 + type: 'boolean',
  988 + default: false
  989 + },
  990 + decoratorSymbol: {
  991 + title: 'Decorator symbol',
  992 + type: 'string',
  993 + default: 'arrowHead'
  994 + },
  995 + decoratorSymbolSize: {
  996 + title: 'Decorator symbol size (px)',
  997 + type: 'number',
  998 + default: 10
  999 + },
  1000 + useDecoratorCustomColor: {
  1001 + title: 'Use path decorator custom color',
  1002 + type: 'boolean',
  1003 + default: false
  1004 + },
  1005 + decoratorCustomColor: {
  1006 + title: 'Decorator custom color',
  1007 + type: 'string',
  1008 + default: '#000'
  1009 + },
  1010 + decoratorOffset: {
  1011 + title: 'Decorator offset',
  1012 + type: 'string',
  1013 + default: '20px'
  1014 + },
  1015 + endDecoratorOffset: {
  1016 + title: 'End decorator offset',
  1017 + type: 'string',
  1018 + default: '20px'
  1019 + },
  1020 + decoratorRepeat: {
  1021 + title: 'Decorator repeat',
  1022 + type: 'string',
  1023 + default: '20px'
  1024 + },
  1025 + showPolygon: {
  1026 + title: 'Show polygon',
  1027 + type: 'boolean',
  1028 + default: false
  1029 + },
  1030 + polygonTooltipPattern: {
  1031 + title: 'Tooltip (for ex. \'Text ${keyName} units.\' or <link-act name=\'my-action\'>Link text</link-act>\')',
  1032 + type: 'string',
  1033 + default: '<b>${entityName}</b><br/><br/><b>TimeStamp:</b> ${ts:7}'
  1034 + },
  1035 + usePolygonTooltipFunction: {
  1036 + title: 'Use polygon tooltip function',
  1037 + type: 'boolean',
  1038 + default: false
  1039 + },
  1040 + polygonTooltipFunction: {
  1041 + title: 'Polygon tooltip function: f(data, dsData, dsIndex)',
  1042 + type: 'string'
  1043 + },
  1044 + polygonColor: {
  1045 + title: 'Polygon color',
  1046 + type: 'string'
  1047 + },
  1048 + polygonOpacity: {
  1049 + title: 'Polygon opacity',
  1050 + type: 'number',
  1051 + default: 0.5
  1052 + },
  1053 + polygonStrokeColor: {
  1054 + title: 'Polygon border color',
  1055 + type: 'string'
  1056 + },
  1057 + polygonStrokeOpacity: {
  1058 + title: 'Polygon border opacity',
  1059 + type: 'number',
  1060 + default: 1
  1061 + },
  1062 + polygonStrokeWeight: {
  1063 + title: 'Polygon border weight',
  1064 + type: 'number',
  1065 + default: 1
  1066 + },
  1067 + usePolygonColorFunction: {
  1068 + title: 'Use polygon color function',
  1069 + type: 'boolean',
  1070 + default: false
  1071 + },
  1072 + polygonColorFunction: {
  1073 + title: 'Polygon Color function: f(data, dsData, dsIndex)',
  1074 + type: 'string'
  1075 + },
  1076 + showPoints: {
  1077 + title: 'Show points',
  1078 + type: 'boolean',
  1079 + default: false
  1080 + },
  1081 + pointColor: {
  1082 + title: 'Point color',
  1083 + type: 'string'
  1084 + },
  1085 + pointSize: {
  1086 + title: 'Point size (px)',
  1087 + type: 'number',
  1088 + default: 10
  1089 + },
  1090 + usePointAsAnchor: {
  1091 + title: 'Use point as anchor',
  1092 + type: 'boolean',
  1093 + default: false
  1094 + },
  1095 + pointAsAnchorFunction: {
  1096 + title: 'Point as anchor function: f(data, dsData, dsIndex)',
  1097 + type: 'string'
  1098 + },
  1099 + pointTooltipOnRightPanel: {
  1100 + title: 'Independant point tooltip',
  1101 + type: 'boolean',
  1102 + default: true
  1103 + },
  1104 + autocloseTooltip: {
  1105 + title: 'Auto-close point popup',
  1106 + type: 'boolean',
  1107 + default: true
  1108 + },
  1109 + markerImage: {
  1110 + title: 'Custom marker image',
  1111 + type: 'string'
  1112 + },
  1113 + markerImageSize: {
  1114 + title: 'Custom marker image size (px)',
  1115 + type: 'number',
  1116 + default: 34
  1117 + },
  1118 + rotationAngle: {
  1119 + title: 'Set additional rotation angle for marker (deg)',
  1120 + type: 'number',
  1121 + default: 180
  1122 + },
  1123 + useMarkerImageFunction: {
  1124 + title: 'Use marker image function',
  1125 + type: 'boolean',
  1126 + default: false
  1127 + },
  1128 + markerImageFunction: {
  1129 + title: 'Marker image function: f(data, images, dsData, dsIndex)',
  1130 + type: 'string'
  1131 + },
  1132 + markerImages: {
  1133 + title: 'Marker images',
  1134 + type: 'array',
  1135 + items: {
  1136 + title: 'Marker image',
  1137 + type: 'string'
  1138 + }
  1139 + }
  1140 + },
  1141 + required: []
  1142 + },
  1143 + form: [{
  1144 + key: 'mapProvider',
  1145 + type: 'rc-select',
  1146 + multiple: false,
  1147 + items: [{
  1148 + value: 'OpenStreetMap.Mapnik',
  1149 + label: 'OpenStreetMap.Mapnik (Default)'
  1150 + }, {
  1151 + value: 'OpenStreetMap.BlackAndWhite',
  1152 + label: 'OpenStreetMap.BlackAndWhite'
  1153 + }, {
  1154 + value: 'OpenStreetMap.HOT',
  1155 + label: 'OpenStreetMap.HOT'
  1156 + }, {
  1157 + value: 'Esri.WorldStreetMap',
  1158 + label: 'Esri.WorldStreetMap'
  1159 + }, {
  1160 + value: 'Esri.WorldTopoMap',
  1161 + label: 'Esri.WorldTopoMap'
  1162 + }, {
  1163 + value: 'CartoDB.Positron',
  1164 + label: 'CartoDB.Positron'
  1165 + }, {
  1166 + value: 'CartoDB.DarkMatter',
  1167 + label: 'CartoDB.DarkMatter'
  1168 + }]
  1169 + }, 'normalizationStep', 'latKeyName', 'lngKeyName', 'polKeyName', 'showLabel', 'label', 'useLabelFunction', {
  1170 + key: 'labelFunction',
  1171 + type: 'javascript'
  1172 + }, 'showTooltip', {
  1173 + key: 'tooltipColor',
  1174 + type: 'color'
  1175 + }, {
  1176 + key: 'tooltipFontColor',
  1177 + type: 'color'
  1178 + }, 'tooltipOpacity', {
  1179 + key: 'tooltipPattern',
  1180 + type: 'textarea'
  1181 + }, 'useTooltipFunction', {
  1182 + key: 'tooltipFunction',
  1183 + type: 'javascript'
  1184 + }, {
  1185 + key: 'color',
  1186 + type: 'color'
  1187 + }, 'useColorFunction', {
  1188 + key: 'colorFunction',
  1189 + type: 'javascript'
  1190 + }, 'usePolylineDecorator', {
  1191 + key: 'decoratorSymbol',
  1192 + type: 'rc-select',
  1193 + multiple: false,
  1194 + items: [{
  1195 + value: 'arrowHead',
  1196 + label: 'Arrow'
  1197 + }, {
  1198 + value: 'dash',
  1199 + label: 'Dash'
  1200 + }]
  1201 + }, 'decoratorSymbolSize', 'useDecoratorCustomColor', {
  1202 + key: 'decoratorCustomColor',
  1203 + type: 'color'
  1204 + }, {
  1205 + key: 'decoratorOffset',
  1206 + type: 'textarea'
  1207 + }, {
  1208 + key: 'endDecoratorOffset',
  1209 + type: 'textarea'
  1210 + }, {
  1211 + key: 'decoratorRepeat',
  1212 + type: 'textarea'
  1213 + }, 'strokeWeight', 'strokeOpacity', 'showPolygon', {
  1214 + key: 'polygonTooltipPattern',
  1215 + type: 'textarea'
  1216 + }, 'usePolygonTooltipFunction', {
  1217 + key: 'polygonTooltipFunction',
  1218 + type: 'javascript'
  1219 + }, {
  1220 + key: 'polygonColor',
  1221 + type: 'color'
  1222 + }, 'polygonOpacity', {
  1223 + key: 'polygonStrokeColor',
  1224 + type: 'color'
  1225 + }, 'polygonStrokeOpacity', 'polygonStrokeWeight', 'usePolygonColorFunction', {
  1226 + key: 'polygonColorFunction',
  1227 + type: 'javascript'
  1228 + }, 'showPoints', {
  1229 + key: 'pointColor',
  1230 + type: 'color'
  1231 + }, 'pointSize', 'usePointAsAnchor', {
  1232 + key: 'pointAsAnchorFunction',
  1233 + type: 'javascript'
  1234 + }, 'pointTooltipOnRightPanel', 'autocloseTooltip', {
  1235 + key: 'markerImage',
  1236 + type: 'image'
  1237 + }, 'markerImageSize', 'rotationAngle', 'useMarkerImageFunction',
  1238 + {
  1239 + key: 'markerImageFunction',
  1240 + type: 'javascript'
  1241 + }, {
  1242 + key: 'markerImages',
  1243 + items: [
  1244 + {
  1245 + key: 'markerImages[]',
  1246 + type: 'image'
  1247 + }
  1248 + ]
  1249 + }]
  1250 +}
\ No newline at end of file
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2020 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<div class="trip-animation-widget">
  19 + <div class="trip-animation-label-container" *ngIf="settings.showLabel">
  20 + {{settings.label | tbParseTemplate: activeTrip}}
  21 + </div>
  22 + <div class="trip-animation-container" layout="column">
  23 + <div class="map" #map></div>
  24 + <div class="trip-animation-info-panel" layout="row">
  25 + <button class="tooltip-button" mat-mini-fab color="primary" aria-label="tooltip"
  26 + *ngIf="settings.showTooltip" (click)="showHideTooltip()">
  27 + <mat-icon>info_outline</mat-icon>
  28 + </button>
  29 + </div>
  30 + <div class="trip-animation-tooltip md-whiteframe-z4" layout="column"
  31 + [ngClass]="{'trip-animation-tooltip-hidden':!visibleTooltip}" [innerHTML]="mainTooltip"
  32 + [ngStyle]="{'background-color': settings.tooltipColor, 'opacity': settings.tooltipOpacity, 'color': settings.tooltipFontColor}">
  33 + </div>
  34 + </div>
  35 + <tb-history-selector *ngIf="historicalData" [settings]="settings" [intervals]="intervals"
  36 + (onTimeUpdated)="timeUpdated($event)"></tb-history-selector>
  37 +</div>
\ No newline at end of file
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +.trip-animation-widget {
  18 + position: relative;
  19 + width: 100%;
  20 + height: 100%;
  21 + font-size: 16px;
  22 + line-height: 24px;
  23 + display: flex;
  24 + flex-direction: column;
  25 +
  26 + .trip-animation-label-container {
  27 + height: 24px;
  28 + }
  29 +
  30 + .trip-animation-container {
  31 + position: relative;
  32 + z-index: 1;
  33 + flex: 1;
  34 + width: 100%;
  35 +
  36 + .map {
  37 + width: 100%;
  38 + height: 100%;
  39 + }
  40 +
  41 + .trip-animation-info-panel {
  42 + position: absolute;
  43 + top: 0;
  44 + right: 0;
  45 + pointer-events: none;
  46 +
  47 + .tooltip-button {
  48 + top: 0;
  49 + left: 0;
  50 + width: 32px;
  51 + min-width: 32px;
  52 + height: 32px;
  53 + min-height: 32px;
  54 + padding: 0 0 2px;
  55 + margin: 2px;
  56 + line-height: 24px;
  57 + z-index: 999;
  58 +
  59 + &::ng-deep .mat-button-wrapper {
  60 + padding: 0;
  61 + }
  62 +
  63 + mat-icon {
  64 + width: 24px;
  65 + height: 24px;
  66 +
  67 + svg {
  68 + width: inherit;
  69 + height: inherit;
  70 + }
  71 + }
  72 + }
  73 + }
  74 +
  75 + .trip-animation-tooltip {
  76 + position: absolute;
  77 + top: 38px;
  78 + right: 0;
  79 + z-index: 400;
  80 + padding: 10px;
  81 + background-color: #fff;
  82 + transition: 0.3s ease-in-out;
  83 +
  84 + &-hidden {
  85 + transform: translateX(110%);
  86 + }
  87 + }
  88 + }
  89 +}
... ...
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import L from 'leaflet';
  18 +import _ from 'lodash';
  19 +import tinycolor from 'tinycolor2';
  20 +import { interpolateOnPointSegment } from 'leaflet-geometryutil';
  21 +
  22 +import { Component, OnInit, Input, ViewChild, AfterViewInit, ChangeDetectorRef, SecurityContext } from '@angular/core';
  23 +import { MapWidgetController, TbMapWidgetV2 } from '../lib/maps/map-widget2';
  24 +import { MapProviders } from '../lib/maps/map-models';
  25 +import { parseArray, parseTemplate, safeExecute } from '@app/core/utils';
  26 +import { initSchema, addToSchema, addGroupInfo } from '@app/core/schema-utils';
  27 +import { tripAnimationSchema } from '../lib/maps/schemes';
  28 +import { DomSanitizer } from '@angular/platform-browser';
  29 +import { WidgetConfig, JsonSchema, JsonSettingsSchema } from '@app/shared/public-api';
  30 +import { WidgetContext } from '@app/modules/home/models/widget-component.models';
  31 +import { getRatio, findAngle } from '../lib/maps/maps-utils';
  32 +
  33 +
  34 +@Component({
  35 + selector: 'trip-animation',
  36 + templateUrl: './trip-animation.component.html',
  37 + styleUrls: ['./trip-animation.component.scss']
  38 +})
  39 +export class TripAnimationComponent implements OnInit, AfterViewInit {
  40 +
  41 + constructor(private cd: ChangeDetectorRef, private sanitizer: DomSanitizer) { }
  42 +
  43 + @Input() ctx: WidgetContext;
  44 +
  45 + @ViewChild('map') mapContainer;
  46 +
  47 + mapWidget: MapWidgetController;
  48 + historicalData;
  49 + intervals;
  50 + normalizationStep = 1000;
  51 + interpolatedData = [];
  52 + widgetConfig: WidgetConfig;
  53 + settings;
  54 + mainTooltip = '';
  55 + visibleTooltip = false;
  56 + activeTrip;
  57 +
  58 + static getSettingsSchema(): JsonSettingsSchema {
  59 + const schema = initSchema();
  60 + addToSchema(schema, TbMapWidgetV2.getProvidersSchema());
  61 + addGroupInfo(schema, 'Map Provider Settings');
  62 + addToSchema(schema, tripAnimationSchema);
  63 + addGroupInfo(schema, 'Trip Animation Settings');
  64 + return schema;
  65 + }
  66 +
  67 + ngOnInit(): void {
  68 + this.widgetConfig = this.ctx.widgetConfig;
  69 + const settings = {
  70 + normalizationStep: 1000,
  71 + showLabel: false,
  72 + buttonColor: tinycolor(this.widgetConfig.color).setAlpha(0.54).toRgbString(),
  73 + disabledButtonColor: tinycolor(this.widgetConfig.color).setAlpha(0.3).toRgbString(),
  74 + rotationAngle: 0
  75 + }
  76 + this.settings = { ...settings, ...this.ctx.settings };
  77 + const subscription = this.ctx.subscriptions[Object.keys(this.ctx.subscriptions)[0]];
  78 + if (subscription) subscription.callbacks.onDataUpdated = (updated) => {
  79 + this.historicalData = parseArray(this.ctx.data);
  80 + this.activeTrip = this.historicalData[0][0];
  81 + this.calculateIntervals();
  82 + this.timeUpdated(this.intervals[0]);
  83 + this.mapWidget.map.updatePolylines(this.interpolatedData.map(ds => _.values(ds)));
  84 +
  85 + this.mapWidget.map.map?.invalidateSize();
  86 + this.cd.detectChanges();
  87 + }
  88 + }
  89 +
  90 + ngAfterViewInit() {
  91 + const ctxCopy: WidgetContext = _.cloneDeep(this.ctx);
  92 + ctxCopy.settings.showLabel = false;
  93 + ctxCopy.settings.showTooltip = false;
  94 + this.mapWidget = new MapWidgetController(MapProviders.openstreet, false, ctxCopy, this.mapContainer.nativeElement);
  95 + }
  96 +
  97 + timeUpdated(time: number) {
  98 + const currentPosition = this.interpolatedData.map(dataSource => dataSource[time]);
  99 + this.activeTrip = currentPosition[0];
  100 + if (this.settings.showPolygon) {
  101 + this.mapWidget.map.updatePolygons(this.interpolatedData);
  102 + }
  103 + this.mapWidget.map.updateMarkers(currentPosition);
  104 + }
  105 +
  106 + setActiveTrip() {
  107 +
  108 + }
  109 +
  110 + calculateIntervals() {
  111 + this.historicalData.forEach((dataSource, index) => {
  112 + this.intervals = [];
  113 + for (let time = dataSource[0]?.time; time < dataSource[dataSource.length - 1]?.time; time += this.normalizationStep) {
  114 + this.intervals.push(time);
  115 + }
  116 + this.intervals.push(dataSource[dataSource.length - 1]?.time);
  117 + this.interpolatedData[index] = this.interpolateArray(dataSource, this.intervals);
  118 + });
  119 + }
  120 +
  121 + showHideTooltip() {
  122 + const tooltipText: string = this.settings.useTooltipFunction ?
  123 + safeExecute(this.settings.tooolTipFunction, [this.activeTrip, this.historicalData, 0])
  124 + : this.settings.tooltipPattern;
  125 +
  126 + this.mainTooltip = this.sanitizer.sanitize(SecurityContext.HTML, parseTemplate(tooltipText, this.activeTrip))
  127 + this.visibleTooltip = !this.visibleTooltip;
  128 + }
  129 +
  130 + interpolateArray(originData, interpolatedIntervals) {
  131 +
  132 + const result = {};
  133 +
  134 + for (let i = 1, j = 0; i < originData.length && j < interpolatedIntervals.length;) {
  135 + const currentTime = interpolatedIntervals[j];
  136 + while (originData[i].time < currentTime) i++;
  137 + const before = originData[i - 1];
  138 + const after = originData[i];
  139 + const interpolation = interpolateOnPointSegment(
  140 + new L.Point(before.latitude, before.longitude),
  141 + new L.Point(after.latitude, after.longitude),
  142 + getRatio(before.time, after.time, currentTime));
  143 + result[currentTime] = ({
  144 + ...originData[i],
  145 + rotationAngle: findAngle(before, after) + this.settings.rotationAngle,
  146 + latitude: interpolation.x,
  147 + longitude: interpolation.y
  148 + });
  149 + j++;
  150 + }
  151 + return result;
  152 + }
  153 +}
  154 +
  155 +export let TbTripAnimationWidget = TripAnimationComponent;
  156 +
... ...
... ... @@ -31,6 +31,7 @@ import {
31 31 DateRangeNavigatorWidgetComponent
32 32 } from '@home/components/widget/lib/date-range-navigator/date-range-navigator.component';
33 33 import { MultipleInputWidgetComponent } from './lib/multiple-input-widget.component';
  34 +import { TripAnimationComponent } from './trip-animation/trip-animation.component';
34 35 import { WebCameraInputWidgetComponent } from './lib/web-camera-input.component';
35 36
36 37 @NgModule({
... ... @@ -45,6 +46,7 @@ import { WebCameraInputWidgetComponent } from './lib/web-camera-input.component'
45 46 DateRangeNavigatorWidgetComponent,
46 47 DateRangeNavigatorPanelComponent,
47 48 MultipleInputWidgetComponent,
  49 + TripAnimationComponent,
48 50 WebCameraInputWidgetComponent
49 51 ],
50 52 imports: [
... ... @@ -61,6 +63,7 @@ import { WebCameraInputWidgetComponent } from './lib/web-camera-input.component'
61 63 RpcWidgetsModule,
62 64 DateRangeNavigatorWidgetComponent,
63 65 MultipleInputWidgetComponent,
  66 + TripAnimationComponent,
64 67 WebCameraInputWidgetComponent
65 68 ],
66 69 providers: [
... ...
... ... @@ -14,13 +14,13 @@
14 14 /// limitations under the License.
15 15 ///
16 16
17   -import {Component, Inject, OnInit} from "@angular/core";
18   -import {DialogComponent} from "@shared/components/dialog.component";
19   -import {Store} from "@ngrx/store";
20   -import {AppState} from "@core/core.state";
21   -import {Router} from "@angular/router";
22   -import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
23   -import {FormBuilder, FormGroup} from "@angular/forms";
  17 +import {Component, Inject, OnInit} from '@angular/core';
  18 +import {DialogComponent} from '@shared/components/dialog.component';
  19 +import {Store} from '@ngrx/store';
  20 +import {AppState} from '@core/core.state';
  21 +import {Router} from '@angular/router';
  22 +import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
  23 +import {FormBuilder, FormGroup} from '@angular/forms';
24 24
25 25 export interface JsonObjectEdittDialogData {
26 26 jsonValue: Object;
... ...
... ... @@ -14,7 +14,7 @@
14 14 /// limitations under the License.
15 15 ///
16 16
17   -import {Directive, ElementRef, forwardRef, HostListener, Renderer2, SkipSelf} from "@angular/core";
  17 +import {Directive, ElementRef, forwardRef, HostListener, Renderer2, SkipSelf} from '@angular/core';
18 18 import {
19 19 ControlValueAccessor,
20 20 FormControl, FormGroupDirective,
... ... @@ -22,8 +22,8 @@ import {
22 22 NG_VALUE_ACCESSOR, NgForm,
23 23 ValidationErrors,
24 24 Validator
25   -} from "@angular/forms";
26   -import {ErrorStateMatcher} from "@angular/material/core";
  25 +} from '@angular/forms';
  26 +import {ErrorStateMatcher} from '@angular/material/core';
27 27
28 28 @Directive({
29 29 selector: '[tb-json-to-string]',
... ...
... ... @@ -113,7 +113,7 @@ const canonicalTitleMap = (titleMap: any, originalEnum?: string[]): { name: stri
113 113
114 114 const stdFormObj = (name: string, schema: any, options: DefaultsFormOptions): any => {
115 115 options = options || {};
116   - const f = options.global && options.global.formDefaults ? _.cloneDeep(options.global.formDefaults) : {};
  116 + const f: any = options.global && options.global.formDefaults ? _.cloneDeep(options.global.formDefaults) : {};
117 117 if (options.global && options.global.supressPropertyTitles === true) {
118 118 f.title = schema.title;
119 119 } else {
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2020 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<div class="trip-animation-control-panel">
  19 + <div>
  20 + <button mat-icon-button class="mat-icon-button" aria-label="Start" (click)="moveStart()">
  21 + <mat-icon class="material-icons" [ngStyle]="{'color': settings.buttonColor}">fast_rewind</mat-icon>
  22 + </button>
  23 + <button mat-icon-button class="mat-icon-button" aria-label="Previous" (click)="movePrev()">
  24 + <mat-icon class="material-icons" [ngStyle]="{'color': settings.buttonColor}">skip_previous</mat-icon>
  25 + </button>
  26 + <mat-slider [(ngModel)]="index" [min]="minTimeIndex" [max]="maxTimeIndex" (change)="changeIndex()">
  27 + </mat-slider>
  28 + <button mat-icon-button class="mat-icon-button" aria-label="Next" (click)="moveNext()">
  29 + <mat-icon class="material-icons" [ngStyle]="{'color': settings.buttonColor}">skip_next</mat-icon>
  30 + </button>
  31 + <button mat-icon-button class="mat-icon-button" aria-label="End" (click)="moveEnd()">
  32 + <mat-icon class="material-icons" [ngStyle]="{'color': settings.buttonColor}">fast_forward</mat-icon>
  33 + </button>
  34 + <button mat-icon-button class="mat-icon-button" aria-label="Play">
  35 + <mat-icon (click)="play()" *ngIf="!playing" class="material-icons"
  36 + [ngStyle]="{'color': settings.buttonColor}">
  37 + play_circle_outline
  38 + </mat-icon>
  39 + <mat-icon (click)="pause()" *ngIf="playing" class="material-icons"
  40 + [ngStyle]="{'color': settings.buttonColor}">
  41 + pause_circle_outline
  42 + </mat-icon>
  43 + </button>
  44 + <mat-select matInput [(ngModel)]="speed" (selectionChange)="reeneble()" class="speed-select" aria-label="Speed selector">
  45 + <mat-option [value]="speedValue" flex="1" *ngFor="let speedValue of speeds">{{speedValue}} </mat-option>
  46 + </mat-select>
  47 + </div>
  48 + <div class="panel-timer">
  49 + <span *ngIf="this.intervals[this.index]">{{ this.intervals[this.index] | date:'medium'}}</span>
  50 + <span *ngIf="!this.intervals[this.index]">{{ "widget.no-data-found" | translate}}</span>
  51 + </div>
\ No newline at end of file
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +.trip-animation-widget {
  17 + position: relative;
  18 + width: 100%;
  19 + height: 100%;
  20 + font-size: 16px;
  21 + line-height: 24px;
  22 +
  23 + .trip-animation-label-container {
  24 + height: 24px;
  25 + }
  26 +
  27 + .trip-animation-container {
  28 + position: relative;
  29 + z-index: 1;
  30 + flex: 1;
  31 + width: 100%;
  32 +
  33 + #trip-animation-map {
  34 + z-index: 1;
  35 + width: 100%;
  36 + height: 100%;
  37 +
  38 + .pointsLayerMarkerIcon {
  39 + border-radius: 50%;
  40 + }
  41 + }
  42 +
  43 + .trip-animation-info-panel {
  44 + position: absolute;
  45 + top: 0;
  46 + right: 0;
  47 + z-index: 2;
  48 + pointer-events: none;
  49 +
  50 + .md-button {
  51 + top: 0;
  52 + left: 0;
  53 + width: 32px;
  54 + min-width: 32px;
  55 + height: 32px;
  56 + min-height: 32px;
  57 + padding: 0 0 2px;
  58 + margin: 2px;
  59 + line-height: 24px;
  60 +
  61 + ng-md-icon {
  62 + width: 24px;
  63 + height: 24px;
  64 +
  65 + svg {
  66 + width: inherit;
  67 + height: inherit;
  68 + }
  69 + }
  70 + }
  71 + }
  72 +
  73 + .trip-animation-tooltip {
  74 + position: absolute;
  75 + top: 38px;
  76 + right: 0;
  77 + z-index: 2;
  78 + padding: 10px;
  79 + background-color: #fff;
  80 + transition: 0.3s ease-in-out;
  81 +
  82 + &-hidden {
  83 + transform: translateX(110%);
  84 + }
  85 + }
  86 + }
  87 +
  88 + .trip-animation-control-panel {
  89 + position: relative;
  90 + box-sizing: border-box;
  91 + width: 100%;
  92 + padding-bottom: 16px;
  93 + padding-left: 10px;
  94 +
  95 + md-slider-container {
  96 + md-slider {
  97 + min-width: 80px;
  98 + }
  99 +
  100 + button.md-button.md-icon-button {
  101 + width: 44px;
  102 + min-width: 44px;
  103 + height: 44px;
  104 + min-height: 44px;
  105 + margin: 0;
  106 + line-height: 28px;
  107 +
  108 + md-icon {
  109 + width: 28px;
  110 + height: 28px;
  111 + font-size: 28px;
  112 +
  113 + svg {
  114 + width: inherit;
  115 + height: inherit;
  116 + }
  117 + }
  118 + }
  119 +
  120 + md-select {
  121 + margin: 0;
  122 + }
  123 + }
  124 +
  125 + .panel-timer {
  126 + max-width: none;
  127 + padding-right: 250px;
  128 + padding-left: 90px;
  129 + margin-top: -20px;
  130 + font-size: 12px;
  131 + font-weight: 500;
  132 + text-align: center;
  133 + }
  134 + }
  135 +}
  136 +
  137 +.speed-select{
  138 + width: 50px;
  139 + margin-left: 20px;
  140 +}
... ...
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import { Component, OnInit, OnChanges, Input, Output, EventEmitter, ChangeDetectorRef } from '@angular/core';
  18 +import { interval, Subscription, Subscriber, SubscriptionLike, Observer } from 'rxjs';
  19 +import { filter, tap } from 'rxjs/operators';
  20 +import { HistorySelectSettings } from '@app/modules/home/components/widget/lib/maps/map-models';
  21 +
  22 +@Component({
  23 + selector: 'tb-history-selector',
  24 + templateUrl: './history-selector.component.html',
  25 + styleUrls: ['./history-selector.component.scss']
  26 +})
  27 +export class HistorySelectorComponent implements OnInit, OnChanges {
  28 +
  29 + @Input() settings: HistorySelectSettings
  30 + @Input() intervals = [];
  31 +
  32 + @Output() timeUpdated: EventEmitter<number> = new EventEmitter();
  33 +
  34 + animationTime;
  35 + minTimeIndex = 0;
  36 + maxTimeIndex = 0;
  37 + speed = 1;
  38 + index = 0;
  39 + playing = false;
  40 + interval;
  41 + speeds = [1, 5, 10, 25];
  42 +
  43 +
  44 + constructor(private cd: ChangeDetectorRef) { }
  45 +
  46 + ngOnInit(): void {
  47 + }
  48 +
  49 + ngOnChanges() {
  50 + this.maxTimeIndex = this.intervals?.length - 1;
  51 + }
  52 +
  53 + play() {
  54 + this.playing = true;
  55 + if (!this.interval)
  56 + this.interval = interval(1000 / this.speed)
  57 + .pipe(
  58 + filter(() => this.playing),
  59 + tap(() => this.index++)).subscribe(() => {
  60 + if (this.index < this.maxTimeIndex) {
  61 + this.cd.detectChanges();
  62 + this.timeUpdated.emit(this.intervals[this.index]);
  63 + }
  64 + else {
  65 + this.interval.complete();
  66 + }
  67 + }, err => {
  68 + console.log(err);
  69 + }, () => {
  70 + this.index = this.minTimeIndex;
  71 + this.playing = false;
  72 + this.interval = null;
  73 + this.cd.detectChanges();
  74 + });
  75 + }
  76 +
  77 + reeneble() {
  78 + if (this.playing) {
  79 + const position = this.index;
  80 + this.interval.complete();
  81 + this.index = position;
  82 + this.play();
  83 + }
  84 + }
  85 +
  86 + pause() {
  87 + this.playing = false;
  88 + this.cd.detectChanges();
  89 + this.timeUpdated.emit(this.intervals[this.index]);
  90 + }
  91 +
  92 + moveNext() {
  93 + if (this.index < this.maxTimeIndex) {
  94 + this.index++;
  95 + }
  96 + this.pause();
  97 + }
  98 +
  99 + movePrev() {
  100 + if (this.index > this.minTimeIndex) {
  101 + this.index++;
  102 + }
  103 + this.pause();
  104 + }
  105 +
  106 + moveStart() {
  107 + this.index = this.minTimeIndex;
  108 + this.pause();
  109 + }
  110 +
  111 + moveEnd() {
  112 + this.index = this.maxTimeIndex;
  113 + this.pause();
  114 + }
  115 +
  116 + changeIndex() {
  117 + this.timeUpdated.emit(this.intervals[this.index]);
  118 + }
  119 +}
... ...
... ... @@ -17,12 +17,12 @@
17 17 import { Component, forwardRef, Input, OnInit, ViewChild } from '@angular/core';
18 18 import { ControlValueAccessor, NG_VALUE_ACCESSOR, NgForm } from '@angular/forms';
19 19 import { ValueType, valueTypesMap } from '@shared/models/constants';
20   -import { isObject } from "@core/utils";
21   -import { MatDialog } from "@angular/material/dialog";
  20 +import { isObject } from '@core/utils';
  21 +import { MatDialog } from '@angular/material/dialog';
22 22 import {
23 23 JsonObjectEditDialogComponent,
24 24 JsonObjectEdittDialogData
25   -} from "@shared/components/dialog/json-object-edit-dialog.component";
  25 +} from '@shared/components/dialog/json-object-edit-dialog.component';
26 26
27 27 @Component({
28 28 selector: 'tb-value-input',
... ... @@ -79,7 +79,7 @@ export class ValueInputComponent implements OnInit, ControlValueAccessor {
79 79 (res) => {
80 80 if (res) {
81 81 this.modelValue = res;
82   - this.inputForm.control.patchValue({'value': this.modelValue});
  82 + this.inputForm.control.patchValue({value: this.modelValue});
83 83 }
84 84 }
85 85 );
... ...
... ... @@ -20,3 +20,4 @@ export * from './keyboard-shortcut.pipe';
20 20 export * from './milliseconds-to-time-string.pipe';
21 21 export * from './nospace.pipe';
22 22 export * from './truncate.pipe';
  23 +export * from './template.pipe';
... ...
... ... @@ -15,7 +15,7 @@
15 15 ///
16 16
17 17 import {Pipe, PipeTransform} from '@angular/core';
18   -import {isObject, isNumber} from "@core/utils";
  18 +import {isObject, isNumber} from '@core/utils';
19 19
20 20 @Pipe({name: 'tbJson'})
21 21 export class TbJsonPipe implements PipeTransform {
... ...
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import { Pipe, PipeTransform } from '@angular/core';
  18 +import { parseTemplate } from '@app/core/utils';
  19 +
  20 +@Pipe({ name: 'tbParseTemplate' })
  21 +export class TbTemplatePipe implements PipeTransform {
  22 + transform(template, data): string {
  23 + console.log('TbTemplatePipe -> transform -> template, data', template, data)
  24 + return parseTemplate(template, data);
  25 + }
  26 +}
... ...
... ... @@ -104,7 +104,7 @@ import { TbErrorComponent } from '@shared/components/tb-error.component';
104 104 import { EntityTypeListComponent } from '@shared/components/entity/entity-type-list.component';
105 105 import { EntitySubTypeListComponent } from '@shared/components/entity/entity-subtype-list.component';
106 106 import { TruncatePipe } from '@shared/pipe/truncate.pipe';
107   -import { TbJsonPipe } from "@shared/pipe/tbJson.pipe";
  107 +import { TbJsonPipe } from '@shared/pipe/tbJson.pipe';
108 108 import { ColorPickerDialogComponent } from '@shared/components/dialog/color-picker-dialog.component';
109 109 import { MatChipDraggableDirective } from '@shared/components/mat-chip-draggable.directive';
110 110 import { ColorInputComponent } from '@shared/components/color-input.component';
... ... @@ -125,8 +125,10 @@ import { TbCheatSheetComponent } from '@shared/components/cheatsheet.component';
125 125 import { TbHotkeysDirective } from '@shared/components/hotkeys.directive';
126 126 import { NavTreeComponent } from '@shared/components/nav-tree.component';
127 127 import { LedLightComponent } from '@shared/components/led-light.component';
128   -import { TbJsonToStringDirective } from "@shared/components/directives/tb-json-to-string.directive";
129   -import { JsonObjectEditDialogComponent } from "@shared/components/dialog/json-object-edit-dialog.component";
  128 +import { TbJsonToStringDirective } from '@shared/components/directives/tb-json-to-string.directive';
  129 +import { JsonObjectEditDialogComponent } from '@shared/components/dialog/json-object-edit-dialog.component';
  130 +import { HistorySelectorComponent } from './components/time/history-selector/history-selector.component';
  131 +import { TbTemplatePipe } from './pipe/public-api';
130 132
131 133 @NgModule({
132 134 providers: [
... ... @@ -207,9 +209,11 @@ import { JsonObjectEditDialogComponent } from "@shared/components/dialog/json-ob
207 209 HighlightPipe,
208 210 TruncatePipe,
209 211 TbJsonPipe,
  212 + TbTemplatePipe,
210 213 KeyboardShortcutPipe,
211 214 TbJsonToStringDirective,
212   - JsonObjectEditDialogComponent
  215 + JsonObjectEditDialogComponent,
  216 + HistorySelectorComponent
213 217 ],
214 218 imports: [
215 219 CommonModule,
... ... @@ -365,9 +369,11 @@ import { JsonObjectEditDialogComponent } from "@shared/components/dialog/json-ob
365 369 HighlightPipe,
366 370 TruncatePipe,
367 371 TbJsonPipe,
  372 + TbTemplatePipe,
368 373 KeyboardShortcutPipe,
369 374 TranslateModule,
370   - JsonObjectEditDialogComponent
  375 + JsonObjectEditDialogComponent,
  376 + HistorySelectorComponent
371 377 ]
372 378 })
373 379 export class SharedModule { }
... ...
  1 +<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M24 4c-7.72 0-14 6.28-14 14 0 10.5 14 26 14 26s14-15.5 14-26c0-7.72-6.28-14-14-14zm8 16h-6v6h-4v-6h-6v-4h6v-6h4v6h6v4z"/></svg>
\ No newline at end of file
... ...
... ... @@ -93,6 +93,8 @@ import { TbAnalogueCompass } from '@home/components/widget/lib/analogue-compass'
93 93 import { TbAnalogueRadialGauge } from '@home/components/widget/lib/analogue-radial-gauge';
94 94 import { TbAnalogueLinearGauge } from '@home/components/widget/lib/analogue-linear-gauge';
95 95 import { TbCanvasDigitalGauge } from '@home/components/widget/lib/digital-gauge';
  96 +import { TbMapWidgetV2 } from '@home/components/widget/lib/maps/map-widget2';
  97 +import { TbTripAnimationWidget } from '@app/modules/home/components/widget/trip-animation/trip-animation.component';
96 98
97 99 import * as tinycolor_ from 'tinycolor2';
98 100
... ... @@ -106,3 +108,5 @@ const tinycolor = tinycolor_;
106 108 (window as any).TbAnalogueRadialGauge = TbAnalogueRadialGauge;
107 109 (window as any).TbAnalogueLinearGauge = TbAnalogueLinearGauge;
108 110 (window as any).TbCanvasDigitalGauge = TbCanvasDigitalGauge;
  111 +(window as any).TbMapWidgetV2 = TbMapWidgetV2;
  112 +(window as any).TbTripAnimationWidget = TbTripAnimationWidget;
... ...
  1 +import * as L from 'leaflet'
  2 +
  3 +declare module 'leaflet' {
  4 +
  5 + namespace Control {
  6 + class AddMarker extends L.Control { }
  7 + }
  8 +
  9 + namespace control {
  10 + function addMarker(options): Control.AddMarker;
  11 + }
  12 +}
\ No newline at end of file
... ...
... ... @@ -19,7 +19,8 @@
19 19 "src/typings/jquery.typings.d.ts",
20 20 "src/typings/jquery.flot.typings.d.ts",
21 21 "src/typings/jquery.jstree.typings.d.ts",
22   - "src/typings/split.js.typings.d.ts"
  22 + "src/typings/split.js.typings.d.ts",
  23 + "src/typings/add-marker.d.ts"
23 24 ],
24 25 "paths": {
25 26 "@app/*": ["src/app/*"],
... ... @@ -33,6 +34,7 @@
33 34 },
34 35 "lib": [
35 36 "es2018",
  37 + "es2019",
36 38 "dom"
37 39 ]
38 40 }
... ...
... ... @@ -433,7 +433,7 @@ export default class TbMapWidgetV2 {
433 433 function calculateLocationColor(location, dataMap) {
434 434 if (location.settings.useColorFunction && location.settings.colorFunction) {
435 435 var color;
436   - try {
  436 + try {
437 437 color = location.settings.colorFunction(dataMap.dataMap, dataMap.dsDataMap, location.dsIndex);
438 438 } catch (e) {/**/
439 439 }
... ... @@ -450,7 +450,7 @@ export default class TbMapWidgetV2 {
450 450 if (location.settings.usePolygonColorFunction && location.settings.polygonColorFunction) {
451 451 var color;
452 452 try {
453   - color = location.settings.polygonColorFunction(dataMap.dataMap, dataMap.dsDataMap, location.dsIndex);
  453 + color = location.settings.polygonColorFunction(dataMap.dataMap, dataMap.dsDataMap, location.dsIndex);
454 454 } catch (e) {/**/
455 455 }
456 456 if (!color) {
... ... @@ -484,7 +484,7 @@ export default class TbMapWidgetV2 {
484 484 function calculateLocationMarkerImage(location, dataMap) {
485 485 if (location.settings.useMarkerImageFunction && location.settings.markerImageFunction) {
486 486 var image = null;
487   - try {
  487 + try {
488 488 image = location.settings.markerImageFunction(dataMap.dataMap, location.settings.markerImages, dataMap.dsDataMap, location.dsIndex);
489 489 } catch (e) {
490 490 image = null;
... ...