UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

238 lines 63.5 kB
import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; import { ControlContainer, NgModelGroup } from '@angular/forms'; import { isNil, isEmpty, assign, unset, get, set, cloneDeep, isEqual } from 'lodash-es'; import { AddressSpaceService } from './address-space.service'; import { OpcuaMeasurementObjectMapping, OpcuaCustomActionObjectMapping, OpcuaAlarmObjectMapping, OpcuaEventObjectMapping } from './mappings'; import * as i0 from "@angular/core"; import * as i1 from "./address-space.service"; import * as i2 from "@c8y/ngx-components"; import * as i3 from "@angular/common"; import * as i4 from "@angular/forms"; import * as i5 from "ngx-bootstrap/tooltip"; import * as i6 from "ngx-bootstrap/collapse"; import * as i7 from "ngx-bootstrap/dropdown"; import * as i8 from "@c8y/ngx-components/device-protocol-object-mappings"; import * as i9 from "./opcua-address-space-tree.component"; import * as i10 from "./opcua-device-protocol-data-reporting.component"; import * as i11 from "./opcua-device-protocol-object-mapping-status-icon.component"; import * as i12 from "./opcua-device-protocol-browse-path-validation.directive"; export class OpcuaDeviceProtocolMapping { constructor(addressSpaceService) { this.addressSpaceService = addressSpaceService; this.onAction = new EventEmitter(); this.isPathFocused = false; this.isBrowsePathUniq = true; this.dataReporting = 'default'; this.isTreeOpen = false; this.isNew = false; this.resetModel = false; this.mappingTypes = [ OpcuaMeasurementObjectMapping, OpcuaAlarmObjectMapping, OpcuaEventObjectMapping, OpcuaCustomActionObjectMapping ]; this.objectMappingState = { valid: false, dirty: false }; this.getMappings = () => this.getParentAttr('mappings'); } toggleDetail() { this.isDetailOpen = !this.isDetailOpen; if (this.resetModel) { this.initialFormSetup(); } } ngOnInit() { this.dataReportingName = 'ReportingMode' + this.index; this.initialFormSetup(); } ngOnChanges(changes) { // this is done to keep the "onDelete" logic in // opcua-device-protocol-detail.component intact if (!isNil(get(changes, '_model.previousValue')) && !isEqual(this._model, changes._model.previousValue)) { if (this.mapping && this.mapping.name === this._model.name) { this.mapping.id = this._model.id; } } } onMappingUpdate({ dirty, valid }) { this.objectMappingState = { valid, dirty }; } initialFormSetup() { const mapping = { id: '', browsePath: [], name: '', subscriptionType: { type: 'None' } }; this.mapping = assign({}, mapping, cloneDeep(this._model)); if (isEmpty(this.mapping.browsePath)) { this.isNew = true; this.isDetailOpen = true; } else { this.browsePath = this.stringfyBrowsePath(this.mapping.browsePath); this.nodeDisplayName = this.mapping.name; } if (this.referencedRootNodeId) { this.referencedNode = { nodeId: this.referencedRootNodeId }; this.addressSpaceService.triggerNodeToOpen({ node: { nodeId: this.referencedRootNodeId, children: [], expanded: false, absolutePaths: [[]] }, selectedAncestorIds: [] }); } else { this.referencedNode = { nodeId: '' }; } if (this.mapping?.customAction) { Object.assign(this.mapping, { customAction: { ...this.mapping.customAction, headers: this.mapHeadersObjectToList(this.mapping?.customAction?.headers) } }); } if (get(this._model, 'subscriptionType')) { this.dataReporting = 'custom'; } else { this.dataReporting = 'default'; } this.resetModel = false; } showAddressSpaceTree() { return !isEmpty(this.referencedServerId); } ngAfterViewInit() { if (get(this.mapping, 'subscriptionType') && get(this.mapping, 'subscriptionType.type') !== 'None') { this.dataReporting = 'custom'; } } mapHeadersObjectToList(headers) { if (Object.keys(headers).length > 0) { return Object.keys(headers).map(item => { return { key: item, value: headers[item] }; }); } } stringfyBrowsePath(path) { return JSON.stringify(path); } updateBrowsePath(node) { this.mapping.browsePath = node.relativePath; this.nodeDisplayName = node.displayName; this.mapping.name = this.nodeDisplayName; this.browsePath = this.stringfyBrowsePath(this.mapping.browsePath); this.browsePathModel.control.markAsDirty(); } updateDisplayname() { this.mapping.name = this.nodeDisplayName; } updateBrowsePathInput() { if (this.browsePath) { try { this.mapping.browsePath = JSON.parse(this.browsePath); } catch (error) { return; } } } save() { if (this.dataReporting === 'default') { unset(this.mapping, 'subscriptionType'); } if (get(this.mapping, 'measurementCreation')) { const { measurementCreation } = this.mapping; set(measurementCreation, 'fragmentName', get(measurementCreation, 'type')); } if (this.mapping.customAction) { this.mapping.customAction.headers = this.mapping.customAction.headers.reduce((result, item) => { result[item.key] = item.value; return result; }, {}); } this.onAction.emit({ action: 'save', data: this.mapping }); this.isDetailOpen = false; } cancel() { this.isDetailOpen = false; this.resetModel = true; if (this.mapping.id === 'new') { this.onAction.emit({ action: 'delete', data: assign({}, this.mapping) }); } } onDelete() { this.onAction.emit({ action: 'delete', data: this.mapping }); } canSave({ valid, dirty }) { const areValid = () => valid && this.objectMappingState.valid; const areDirty = () => dirty || this.objectMappingState.dirty; return areValid() && areDirty(); } isActive() { return this.isDetailOpen; } setTreeFromRefNode() { if (this.referencedRootNodeId) { this.addressSpaceService.triggerNodeToOpen({ node: { nodeId: this.referencedRootNodeId, children: [], expanded: false, absolutePaths: [[]] }, selectedAncestorIds: [] }); } } updateSubscriptionType(value) { this.mapping.subscriptionType = value; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: OpcuaDeviceProtocolMapping, deps: [{ token: i1.AddressSpaceService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: OpcuaDeviceProtocolMapping, selector: "opcua-device-protocol-mapping", inputs: { _model: ["resource", "_model"], index: "index", getParentAttr: "getParentAttr", referencedServerId: "referencedServerId", referencedRootNodeId: "referencedRootNodeId" }, outputs: { onAction: "onAction" }, viewQueries: [{ propertyName: "subFormRef", first: true, predicate: ["variableForm"], descendants: true }, { propertyName: "browsePathModel", first: true, predicate: ["browsePathModel"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div\n class=\"c8y-list__item\"\n [ngClass]=\"{ expanded: isDetailOpen }\"\n [attr.data-cy]=\"mapping.browsePath | json\"\n>\n <div class=\"c8y-list__item__block\">\n <div class=\"c8y-list__item__icon\">\n <i c8yIcon=\"sliders\"></i>\n </div>\n <div class=\"c8y-list__item__body\">\n <div class=\"content-flex-70\">\n <div class=\"col-5\">\n <p\n class=\"text-truncate\"\n title=\" {{ nodeDisplayName }} {{\n mapping.browsePath.length > 0 ? (mapping.browsePath | json) : ''\n }}\"\n (click)=\"toggleDetail()\"\n >\n {{ nodeDisplayName }}\n <small\n class=\"text-muted\"\n *ngIf=\"mapping.browsePath.length > 0\"\n >\n {{ mapping.browsePath | json }}\n </small>\n </p>\n </div>\n <div class=\"col-5\">\n <div class=\"list-functionalities\">\n <label class=\"small m-r-8 m-b-0 hidden-xs\">\n {{ 'Functionalities' | translate }}\n </label>\n <c8y-object-mapping-status-icons [mapping]=\"mapping\"></c8y-object-mapping-status-icons>\n </div>\n </div>\n <div class=\"flex-grow d-flex p-r-8\">\n <button\n class=\"btn btn-dot btn-dot--danger showOnHover m-l-auto\"\n [attr.aria-label]=\"'Remove' | translate\"\n tooltip=\"{{ 'Remove' | translate }}\"\n type=\"button\"\n [delay]=\"500\"\n >\n <i\n c8yIcon=\"minus-circle\"\n (click)=\"onDelete()\"\n ></i>\n </button>\n </div>\n </div>\n </div>\n <div class=\"c8y-list__item__actions\">\n <button\n class=\"collapse-btn\"\n title=\"{{ 'Expand' | translate }}\"\n type=\"button\"\n [ngClass]=\"{ active: isDetailOpen }\"\n (click)=\"toggleDetail()\"\n [attr.data-cy]=\"'toggleDetail'\"\n >\n <i c8yIcon=\"chevron-down\"></i>\n </button>\n </div>\n </div>\n <div\n class=\"collapse\"\n [collapse]=\"!isDetailOpen\"\n [isAnimated]=\"true\"\n >\n <div class=\"c8y-list__item__collapse--container\">\n <div\n class=\"form\"\n [ngModelGroup]=\"index\"\n #variableForm=\"ngModelGroup\"\n *ngIf=\"isDetailOpen\"\n >\n <div class=\"row p-t-8\">\n <c8y-form-group\n class=\"col-md-4\"\n [status]=\"!isBrowsePathUniq ? 'error' : ''\"\n *ngIf=\"showAddressSpaceTree(); else simpleInput\"\n >\n <label>{{ 'Path' | translate }}</label>\n <div\n class=\"dropdown fit-w\"\n dropdown\n #dropdown=\"bs-dropdown\"\n [insideClick]=\"true\"\n >\n <div class=\"input-group\">\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} {{ ['2:Node1', '2:SubNode1'] | json }}\"\n name=\"browsePath\"\n type=\"text\"\n autocomplete=\"off\"\n required\n c8yBrowsePathValidator\n [getMappings]=\"getMappings\"\n [model]=\"mapping\"\n [(ngModel)]=\"browsePath\"\n (change)=\"updateBrowsePathInput()\"\n (focus)=\"setTreeFromRefNode()\"\n #browsePathModel=\"ngModel\"\n />\n <span class=\"input-group-btn\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Toggle address space' | translate }}\"\n type=\"button\"\n dropdownToggle\n >\n <i [c8yIcon]=\"'caret-down'\"></i>\n </button>\n </span>\n </div>\n <div\n class=\"dropdown-menu panel-inner-scroll fit-w\"\n style=\"max-height: 240px\"\n *dropdownMenu\n >\n <opcua-address-space-tree\n [node]=\"referencedNode\"\n [moId]=\"referencedServerId\"\n (selectedNode)=\"updateBrowsePath($event); dropdown.hide()\"\n ></opcua-address-space-tree>\n </div>\n </div>\n <c8y-messages>\n <c8y-message\n name=\"invalidBrowsePathNotation\"\n text=\"{{ 'Must be a valid array of strings.' | translate }}\"\n ></c8y-message>\n <c8y-message\n name=\"browsePathNotUnique\"\n text=\"{{ 'Variable with this path is already added.' | translate }}\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n\n <ng-template #simpleInput>\n <c8y-form-group class=\"col-md-4\">\n <label>{{ 'Path' | translate }}</label>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} {{ ['2:Node1', '2:SubNode1'] | json }}\"\n name=\"browsePath\"\n type=\"text\"\n autocomplete=\"off\"\n required\n c8yBrowsePathValidator\n [getMappings]=\"getMappings\"\n [model]=\"mapping\"\n [(ngModel)]=\"browsePath\"\n (change)=\"updateBrowsePathInput()\"\n #browsePathModel=\"ngModel\"\n />\n <c8y-messages>\n <c8y-message\n name=\"invalidBrowsePathNotation\"\n text=\"{{ 'Must be a valid array of strings.' | translate }}\"\n ></c8y-message>\n <c8y-message\n name=\"browsePathNotUnique\"\n text=\"{{ 'Variable with this path is already added.' | translate }}\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </ng-template>\n\n <c8y-form-group class=\"col-md-4\">\n <label>{{ 'Name' | translate }}</label>\n <div class=\"input-group\">\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g. childDevice2' | translate }} \"\n name=\"displayName\"\n type=\"text\"\n autocomplete=\"off\"\n required\n [(ngModel)]=\"nodeDisplayName\"\n (change)=\"updateDisplayname()\"\n />\n </div>\n </c8y-form-group>\n </div>\n <div\n class=\"row\"\n ngModelGroup=\"dataReportingSection\"\n >\n <c8y-form-group class=\"col-sm-4 col-md-3 col-lg-2\">\n <label>\n <span>{{ 'Data reporting' | translate }}</span>\n </label>\n <div class=\"input-group\">\n <label\n class=\"c8y-radio radio-inline\"\n title=\"{{ 'Default' | translate }}\"\n >\n <input\n name=\"{{ dataReportingName }}\"\n type=\"radio\"\n value=\"default\"\n [(ngModel)]=\"dataReporting\"\n />\n <span></span>\n <span>{{ 'Default' | translate }}</span>\n </label>\n <label\n class=\"c8y-radio radio-inline\"\n title=\"{{ 'Custom' | translate }}\"\n >\n <input\n name=\"{{ dataReportingName }}\"\n type=\"radio\"\n value=\"custom\"\n [(ngModel)]=\"dataReporting\"\n />\n <span></span>\n <span>{{ 'Custom' | translate }}</span>\n </label>\n </div>\n </c8y-form-group>\n <div\n class=\"col-sm-8 col-md-9 col-lg-10\"\n *ngIf=\"dataReporting === 'custom'\"\n ngModelGroup=\"overriddenSubscription\"\n >\n <opcua-device-protocol-data-reporting\n [model]=\"mapping\"\n (onSubscriptionChange)=\"updateSubscriptionType($event)\"\n ></opcua-device-protocol-data-reporting>\n </div>\n </div>\n\n <c8y-device-protocol-object-mappings\n [data]=\"mapping\"\n [objectMappingTypes]=\"mappingTypes\"\n (onUpdate)=\"onMappingUpdate($event)\">\n </c8y-device-protocol-object-mappings>\n\n <button\n class=\"btn btn-default m-t-16 m-b-16\"\n title=\"{{ 'Cancel' | translate }}\"\n id=\"cancelBtn\"\n type=\"button\"\n (click)=\"cancel()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary m-t-16 m-b-16\"\n title=\"{{ 'Save' | translate }}\"\n id=\"saveBtn\"\n type=\"button\"\n (click)=\"save()\"\n [disabled]=\"!canSave(variableForm)\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i2.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i4.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i4.NgModelGroup, selector: "[ngModelGroup]", inputs: ["ngModelGroup"], exportAs: ["ngModelGroup"] }, { kind: "component", type: i2.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i2.MessageDirective, selector: "c8y-message", inputs: ["name", "text"] }, { kind: "component", type: i2.MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "directive", type: i2.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i5.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "directive", type: i6.CollapseDirective, selector: "[collapse]", inputs: ["display", "isAnimated", "collapse"], outputs: ["collapsed", "collapses", "expanded", "expands"], exportAs: ["bs-collapse"] }, { kind: "directive", type: i7.BsDropdownMenuDirective, selector: "[bsDropdownMenu],[dropdownMenu]", exportAs: ["bs-dropdown-menu"] }, { kind: "directive", type: i7.BsDropdownToggleDirective, selector: "[bsDropdownToggle],[dropdownToggle]", exportAs: ["bs-dropdown-toggle"] }, { kind: "directive", type: i7.BsDropdownDirective, selector: "[bsDropdown], [dropdown]", inputs: ["placement", "triggers", "container", "dropup", "autoClose", "isAnimated", "insideClick", "isDisabled", "isOpen"], outputs: ["isOpenChange", "onShown", "onHidden"], exportAs: ["bs-dropdown"] }, { kind: "component", type: i8.ObjectMappingComponent, selector: "c8y-device-protocol-object-mappings", inputs: ["data", "objectMappingTypes"], outputs: ["onUpdate"] }, { kind: "component", type: i9.OpcuaAddressSpaceTreeComponent, selector: "opcua-address-space-tree", inputs: ["moId", "node", "focusEmitter"], outputs: ["selectedNode"] }, { kind: "component", type: i10.OpcuaDeviceProtocolDataReportingComponent, selector: "opcua-device-protocol-data-reporting", inputs: ["model", "groupName"], outputs: ["onSubscriptionChange"] }, { kind: "directive", type: i11.OpcuaDeviceProtocolObjectMappingStatus, selector: "c8y-object-mapping-status-icons", inputs: ["mapping"] }, { kind: "directive", type: i12.OpcuaDeviceProtocolBrowsePathValidation, selector: "[c8yBrowsePathValidator][ngModel]", inputs: ["getMappings", "model"] }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i3.JsonPipe, name: "json" }], viewProviders: [{ provide: ControlContainer, useExisting: NgModelGroup }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: OpcuaDeviceProtocolMapping, decorators: [{ type: Component, args: [{ selector: 'opcua-device-protocol-mapping', viewProviders: [{ provide: ControlContainer, useExisting: NgModelGroup }], template: "<div\n class=\"c8y-list__item\"\n [ngClass]=\"{ expanded: isDetailOpen }\"\n [attr.data-cy]=\"mapping.browsePath | json\"\n>\n <div class=\"c8y-list__item__block\">\n <div class=\"c8y-list__item__icon\">\n <i c8yIcon=\"sliders\"></i>\n </div>\n <div class=\"c8y-list__item__body\">\n <div class=\"content-flex-70\">\n <div class=\"col-5\">\n <p\n class=\"text-truncate\"\n title=\" {{ nodeDisplayName }} {{\n mapping.browsePath.length > 0 ? (mapping.browsePath | json) : ''\n }}\"\n (click)=\"toggleDetail()\"\n >\n {{ nodeDisplayName }}\n <small\n class=\"text-muted\"\n *ngIf=\"mapping.browsePath.length > 0\"\n >\n {{ mapping.browsePath | json }}\n </small>\n </p>\n </div>\n <div class=\"col-5\">\n <div class=\"list-functionalities\">\n <label class=\"small m-r-8 m-b-0 hidden-xs\">\n {{ 'Functionalities' | translate }}\n </label>\n <c8y-object-mapping-status-icons [mapping]=\"mapping\"></c8y-object-mapping-status-icons>\n </div>\n </div>\n <div class=\"flex-grow d-flex p-r-8\">\n <button\n class=\"btn btn-dot btn-dot--danger showOnHover m-l-auto\"\n [attr.aria-label]=\"'Remove' | translate\"\n tooltip=\"{{ 'Remove' | translate }}\"\n type=\"button\"\n [delay]=\"500\"\n >\n <i\n c8yIcon=\"minus-circle\"\n (click)=\"onDelete()\"\n ></i>\n </button>\n </div>\n </div>\n </div>\n <div class=\"c8y-list__item__actions\">\n <button\n class=\"collapse-btn\"\n title=\"{{ 'Expand' | translate }}\"\n type=\"button\"\n [ngClass]=\"{ active: isDetailOpen }\"\n (click)=\"toggleDetail()\"\n [attr.data-cy]=\"'toggleDetail'\"\n >\n <i c8yIcon=\"chevron-down\"></i>\n </button>\n </div>\n </div>\n <div\n class=\"collapse\"\n [collapse]=\"!isDetailOpen\"\n [isAnimated]=\"true\"\n >\n <div class=\"c8y-list__item__collapse--container\">\n <div\n class=\"form\"\n [ngModelGroup]=\"index\"\n #variableForm=\"ngModelGroup\"\n *ngIf=\"isDetailOpen\"\n >\n <div class=\"row p-t-8\">\n <c8y-form-group\n class=\"col-md-4\"\n [status]=\"!isBrowsePathUniq ? 'error' : ''\"\n *ngIf=\"showAddressSpaceTree(); else simpleInput\"\n >\n <label>{{ 'Path' | translate }}</label>\n <div\n class=\"dropdown fit-w\"\n dropdown\n #dropdown=\"bs-dropdown\"\n [insideClick]=\"true\"\n >\n <div class=\"input-group\">\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} {{ ['2:Node1', '2:SubNode1'] | json }}\"\n name=\"browsePath\"\n type=\"text\"\n autocomplete=\"off\"\n required\n c8yBrowsePathValidator\n [getMappings]=\"getMappings\"\n [model]=\"mapping\"\n [(ngModel)]=\"browsePath\"\n (change)=\"updateBrowsePathInput()\"\n (focus)=\"setTreeFromRefNode()\"\n #browsePathModel=\"ngModel\"\n />\n <span class=\"input-group-btn\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Toggle address space' | translate }}\"\n type=\"button\"\n dropdownToggle\n >\n <i [c8yIcon]=\"'caret-down'\"></i>\n </button>\n </span>\n </div>\n <div\n class=\"dropdown-menu panel-inner-scroll fit-w\"\n style=\"max-height: 240px\"\n *dropdownMenu\n >\n <opcua-address-space-tree\n [node]=\"referencedNode\"\n [moId]=\"referencedServerId\"\n (selectedNode)=\"updateBrowsePath($event); dropdown.hide()\"\n ></opcua-address-space-tree>\n </div>\n </div>\n <c8y-messages>\n <c8y-message\n name=\"invalidBrowsePathNotation\"\n text=\"{{ 'Must be a valid array of strings.' | translate }}\"\n ></c8y-message>\n <c8y-message\n name=\"browsePathNotUnique\"\n text=\"{{ 'Variable with this path is already added.' | translate }}\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n\n <ng-template #simpleInput>\n <c8y-form-group class=\"col-md-4\">\n <label>{{ 'Path' | translate }}</label>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} {{ ['2:Node1', '2:SubNode1'] | json }}\"\n name=\"browsePath\"\n type=\"text\"\n autocomplete=\"off\"\n required\n c8yBrowsePathValidator\n [getMappings]=\"getMappings\"\n [model]=\"mapping\"\n [(ngModel)]=\"browsePath\"\n (change)=\"updateBrowsePathInput()\"\n #browsePathModel=\"ngModel\"\n />\n <c8y-messages>\n <c8y-message\n name=\"invalidBrowsePathNotation\"\n text=\"{{ 'Must be a valid array of strings.' | translate }}\"\n ></c8y-message>\n <c8y-message\n name=\"browsePathNotUnique\"\n text=\"{{ 'Variable with this path is already added.' | translate }}\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </ng-template>\n\n <c8y-form-group class=\"col-md-4\">\n <label>{{ 'Name' | translate }}</label>\n <div class=\"input-group\">\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g. childDevice2' | translate }} \"\n name=\"displayName\"\n type=\"text\"\n autocomplete=\"off\"\n required\n [(ngModel)]=\"nodeDisplayName\"\n (change)=\"updateDisplayname()\"\n />\n </div>\n </c8y-form-group>\n </div>\n <div\n class=\"row\"\n ngModelGroup=\"dataReportingSection\"\n >\n <c8y-form-group class=\"col-sm-4 col-md-3 col-lg-2\">\n <label>\n <span>{{ 'Data reporting' | translate }}</span>\n </label>\n <div class=\"input-group\">\n <label\n class=\"c8y-radio radio-inline\"\n title=\"{{ 'Default' | translate }}\"\n >\n <input\n name=\"{{ dataReportingName }}\"\n type=\"radio\"\n value=\"default\"\n [(ngModel)]=\"dataReporting\"\n />\n <span></span>\n <span>{{ 'Default' | translate }}</span>\n </label>\n <label\n class=\"c8y-radio radio-inline\"\n title=\"{{ 'Custom' | translate }}\"\n >\n <input\n name=\"{{ dataReportingName }}\"\n type=\"radio\"\n value=\"custom\"\n [(ngModel)]=\"dataReporting\"\n />\n <span></span>\n <span>{{ 'Custom' | translate }}</span>\n </label>\n </div>\n </c8y-form-group>\n <div\n class=\"col-sm-8 col-md-9 col-lg-10\"\n *ngIf=\"dataReporting === 'custom'\"\n ngModelGroup=\"overriddenSubscription\"\n >\n <opcua-device-protocol-data-reporting\n [model]=\"mapping\"\n (onSubscriptionChange)=\"updateSubscriptionType($event)\"\n ></opcua-device-protocol-data-reporting>\n </div>\n </div>\n\n <c8y-device-protocol-object-mappings\n [data]=\"mapping\"\n [objectMappingTypes]=\"mappingTypes\"\n (onUpdate)=\"onMappingUpdate($event)\">\n </c8y-device-protocol-object-mappings>\n\n <button\n class=\"btn btn-default m-t-16 m-b-16\"\n title=\"{{ 'Cancel' | translate }}\"\n id=\"cancelBtn\"\n type=\"button\"\n (click)=\"cancel()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary m-t-16 m-b-16\"\n title=\"{{ 'Save' | translate }}\"\n id=\"saveBtn\"\n type=\"button\"\n (click)=\"save()\"\n [disabled]=\"!canSave(variableForm)\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n </div>\n </div>\n</div>\n" }] }], ctorParameters: () => [{ type: i1.AddressSpaceService }], propDecorators: { subFormRef: [{ type: ViewChild, args: ['variableForm', { static: false }] }], browsePathModel: [{ type: ViewChild, args: ['browsePathModel', { static: false }] }], _model: [{ type: Input, args: ['resource'] }], index: [{ type: Input }], getParentAttr: [{ type: Input }], referencedServerId: [{ type: Input }], referencedRootNodeId: [{ type: Input }], onAction: [{ type: Output }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BjdWEtZGV2aWNlLXByb3RvY29sLW1hcHBpbmcuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vcHJvdG9jb2wtb3BjdWEvb3BjdWEtZGV2aWNlLXByb3RvY29sLW1hcHBpbmcuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vcHJvdG9jb2wtb3BjdWEvb3BjdWEtZGV2aWNlLXByb3RvY29sLW1hcHBpbmcuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsU0FBUyxFQUVULFlBQVksRUFDWixLQUFLLEVBQ0wsTUFBTSxFQUNOLFNBQVMsRUFHVixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsWUFBWSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDaEUsT0FBTyxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLFNBQVMsRUFBRSxPQUFPLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDeEYsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDOUQsT0FBTyxFQUNMLDZCQUE2QixFQUM3Qiw4QkFBOEIsRUFDOUIsdUJBQXVCLEVBQ3ZCLHVCQUF1QixFQUN4QixNQUFNLFlBQVksQ0FBQzs7Ozs7Ozs7Ozs7Ozs7QUFPcEIsTUFBTSxPQUFPLDBCQUEwQjtJQXFDckMsWUFBb0IsbUJBQXdDO1FBQXhDLHdCQUFtQixHQUFuQixtQkFBbUIsQ0FBcUI7UUE1QmxELGFBQVEsR0FBc0IsSUFBSSxZQUFZLEVBQU8sQ0FBQztRQU1oRSxrQkFBYSxHQUFHLEtBQUssQ0FBQztRQUl0QixxQkFBZ0IsR0FBRyxJQUFJLENBQUM7UUFDeEIsa0JBQWEsR0FBRyxTQUFTLENBQUM7UUFDMUIsZUFBVSxHQUFHLEtBQUssQ0FBQztRQUNuQixVQUFLLEdBQUcsS0FBSyxDQUFDO1FBQ2QsZUFBVSxHQUFHLEtBQUss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QztJQUVELHNCQUFzQixDQUFDLEtBQUs7UUFDMUIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsR0FBRyxLQUFLLENBQUM7SUFDeEMsQ0FBQzsrR0FqUFUsMEJBQTBCO21HQUExQiwwQkFBMEIsa2dCQ3pCdkMsc3BTQXlRQSxzeUlEbFBpQixDQUFDLEVBQUUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLFdBQVcsRUFBRSxZQUFZLEVBQUUsQ0FBQzs7NEZBRTlELDBCQUEwQjtrQkFMdEMsU0FBUzsrQkFDRSwrQkFBK0IsaUJBRTFCLENBQUMsRUFBRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsV0FBVyxFQUFFLFlBQVksRUFBRSxDQUFDO3dGQUczQixVQUFVO3NCQUF2RCxTQUFTO3VCQUFDLGNBQWMsRUFBRSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUU7Z0JBQ0ssZUFBZTtzQkFBL0QsU0FBUzt1QkFBQyxpQkFBaUIsRUFBRSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUU7Z0JBRTVCLE1BQU07c0JBQXhCLEtBQUs7dUJBQUMsVUFBVTtnQkFDUixLQUFLO3NCQUFiLEtBQUs7Z0JBQ0csYUFBYTtzQkFBckIsS0FBSztnQkFDRyxrQkFBa0I7c0JBQTFCLEtBQUs7Z0JBQ0csb0JBQW9CO3NCQUE1QixLQUFLO2dCQUNJLFFBQVE7c0JBQWpCLE1BQU0iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBDb21wb25lbnQsXG4gIE9uSW5pdCxcbiAgRXZlbnRFbWl0dGVyLFxuICBJbnB1dCxcbiAgT3V0cHV0LFxuICBWaWV3Q2hpbGQsXG4gIE9uQ2hhbmdlcyxcbiAgU2ltcGxlQ2hhbmdlc1xufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IENvbnRyb2xDb250YWluZXIsIE5nTW9kZWxHcm91cCB9IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcbmltcG9ydCB7IGlzTmlsLCBpc0VtcHR5LCBhc3NpZ24sIHVuc2V0LCBnZXQsIHNldCwgY2xvbmVEZWVwLCBpc0VxdWFsIH0gZnJvbSAnbG9kYXNoLWVzJztcbmltcG9ydCB7IEFkZHJlc3NTcGFjZVNlcnZpY2UgfSBmcm9tICcuL2FkZHJlc3Mtc3BhY2Uuc2VydmljZSc7XG5pbXBvcnQge1xuICBPcGN1YU1lYXN1cmVtZW50T2JqZWN0TWFwcGluZyxcbiAgT3BjdWFDdXN0b21BY3Rpb25PYmplY3RNYXBwaW5nLFxuICBPcGN1YUFsYXJtT2JqZWN0TWFwcGluZyxcbiAgT3BjdWFFdmVudE9iamVjdE1hcHBpbmdcbn0gZnJvbSAnLi9tYXBwaW5ncyc7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ29wY3VhLWRldmljZS1wcm90b2NvbC1tYXBwaW5nJyxcbiAgdGVtcGxhdGVVcmw6ICcuL29wY3VhLWRldmljZS1wcm90b2NvbC1tYXBwaW5nLmh0bWwnLFxuICB2aWV3UHJvdmlkZXJzOiBbeyBwcm92aWRlOiBDb250cm9sQ29udGFpbmVyLCB1c2VFeGlzdGluZzogTmdNb2RlbEdyb3VwIH1dXG59KVxuZXhwb3J0IGNsYXNzIE9wY3VhRGV2aWNlUHJvdG9jb2xNYXBwaW5nIGltcGxlbWVudHMgT25Jbml0LCBPbkNoYW5nZXMge1xuICBAVmlld0NoaWxkKCd2YXJpYWJsZUZvcm0nLCB7IHN0YXRpYzogZmFsc2UgfSkgc3ViRm9ybVJlZjogTmdNb2RlbEdyb3VwO1xuICBAVmlld0NoaWxkKCdicm93c2VQYXRoTW9kZWwnLCB7IHN0YXRpYzogZmFsc2UgfSkgYnJvd3NlUGF0aE1vZGVsOiBhbnk7XG5cbiAgQElucHV0KCdyZXNvdXJjZScpIF9tb2RlbDtcbiAgQElucHV0KCkgaW5kZXg7XG4gIEBJbnB1dCgpIGdldFBhcmVudEF0dHI7XG4gIEBJbnB1dCgpIHJlZmVyZW5jZWRTZXJ2ZXJJZDtcbiAgQElucHV0KCkgcmVmZXJlbmNlZFJvb3ROb2RlSWQ7XG4gIEBPdXRwdXQoKSBvbkFjdGlvbjogRXZlbnRFbWl0dGVyPGFueT4gPSBuZXcgRXZlbnRFbWl0dGVyPGFueT4oKTtcblxuICBtYXBwaW5nO1xuXG4gIGlzRGV0YWlsT3BlbjtcbiAgcmVmZXJlbmNlZE5vZGU7XG4gIGlzUGF0aEZvY3VzZWQgPSBmYWxzZTtcbiAgZ3JvdXBOYW1lOiBzdHJpbmc7XG4gIGJyb3dzZVBhdGg6IHN0cmluZztcbiAgbm9kZURpc3BsYXlOYW1lOiBzdHJpbmc7XG4gIGlzQnJvd3NlUGF0aFVuaXEgPSB0cnVlO1xuICBkYXRhUmVwb3J0aW5nID0gJ2RlZmF1bHQnO1xuICBpc1RyZWVPcGVuID0gZmFsc2U7XG4gIGlzTmV3ID0gZmFsc2U7XG4gIHJlc2V0TW9kZWwgPSBmYWxzZTtcbiAgZGF0YVJlcG9ydGluZ05hbWU7XG5cbiAgbWFwcGluZ1R5cGVzID0gW1xuICAgIE9wY3VhTWVhc3VyZW1lbnRPYmplY3RNYXBwaW5nLFxuICAgIE9wY3VhQWxhcm1PYmplY3RNYXBwaW5nLFxuICAgIE9wY3VhRXZlbnRPYmplY3RNYXBwaW5nLFxuICAgIE9wY3VhQ3VzdG9tQWN0aW9uT2JqZWN0TWFwcGluZ1xuICBdO1xuXG4gIHByaXZhdGUgb2JqZWN0TWFwcGluZ1N0YXRlID0ge1xuICAgIHZhbGlkOiBmYWxzZSxcbiAgICBkaXJ0eTogZmFsc2VcbiAgfTtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBhZGRyZXNzU3BhY2VTZXJ2aWNlOiBBZGRyZXNzU3BhY2VTZXJ2aWNlKSB7fVxuXG4gIHRvZ2dsZURldGFpbCgpIHtcbiAgICB0aGlzLmlzRGV0YWlsT3BlbiA9ICF0aGlzLmlzRGV0YWlsT3BlbjtcbiAgICBpZiAodGhpcy5yZXNldE1vZGVsKSB7XG4gICAgICB0aGlzLmluaXRpYWxGb3JtU2V0dXAoKTtcbiAgICB9XG4gIH1cblxuICBnZXRNYXBwaW5ncyA9ICgpID0+IHRoaXMuZ2V0UGFyZW50QXR0cignbWFwcGluZ3MnKTtcblxuICBuZ09uSW5pdCgpIHtcbiAgICB0aGlzLmRhdGFSZXBvcnRpbmdOYW1lID0gJ1JlcG9ydGluZ01vZGUnICsgdGhpcy5pbmRleDtcbiAgICB0aGlzLmluaXRpYWxGb3JtU2V0dXAoKTtcbiAgfVxuXG4gIG5nT25DaGFuZ2VzKGNoYW5nZXM6IFNpbXBsZUNoYW5nZXMpIHtcbiAgICAvLyB0aGlzIGlzIGRvbmUgdG8ga2VlcCB0aGUgXCJvbkRlbGV0ZVwiIGxvZ2ljIGluXG4gICAgLy8gb3BjdWEtZGV2aWNlLXByb3RvY29sLWRldGFpbC5jb21wb25lbnQgaW50YWN0XG4gICAgaWYgKFxuICAgICAgIWlzTmlsKGdldChjaGFuZ2VzLCAnX21vZGVsLnByZXZpb3VzVmFsdWUnKSkgJiZcbiAgICAgICFpc0VxdWFsKHRoaXMuX21vZGVsLCBjaGFuZ2VzLl9tb2RlbC5wcmV2aW91c1ZhbHVlKVxuICAgICkge1xuICAgICAgaWYgKHRoaXMubWFwcGluZyAmJiB0aGlzLm1hcHBpbmcubmFtZSA9PT0gdGhpcy5fbW9kZWwubmFtZSkge1xuICAgICAgICB0aGlzLm1hcHBpbmcuaWQgPSB0aGlzLl9tb2RlbC5pZDtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBvbk1hcHBpbmdVcGRhdGUoeyBkaXJ0eSwgdmFsaWQgfTogeyBkaXJ0eTogYm9vbGVhbjsgdmFsaWQ6IGJvb2xlYW47IHRvdWNoZWQ/OiBib29sZWFuIH0pIHtcbiAgICB0aGlzLm9iamVjdE1hcHBpbmdTdGF0ZSA9IHtcbiAgICAgIHZhbGlkLFxuICAgICAgZGlydHlcbiAgICB9O1xuICB9XG5cbiAgaW5pdGlhbEZvcm1TZXR1cCgpIHtcbiAgICBjb25zdCBtYXBwaW5nID0ge1xuICAgICAgaWQ6ICcnLFxuICAgICAgYnJvd3NlUGF0aDogW10sXG4gICAgICBuYW1lOiAnJyxcbiAgICAgIHN1YnNjcmlwdGlvblR5cGU6IHtcbiAgICAgICAgdHlwZTogJ05vbmUnXG4gICAgICB9XG4gICAgfTtcblxuICAgIHRoaXMubWFwcGluZyA9IGFzc2lnbih7fSwgbWFwcGluZywgY2xvbmVEZWVwKHRoaXMuX21vZGVsKSk7XG5cbiAgICBpZiAoaXNFbXB0eSh0aGlzLm1hcHBpbmcuYnJvd3NlUGF0aCkpIHtcbiAgICAgIHRoaXMuaXNOZXcgPSB0cnVlO1xuICAgICAgdGhpcy5pc0RldGFpbE9wZW4gPSB0cnVlO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmJyb3dzZVBhdGggPSB0aGlzLnN0cmluZ2Z5QnJvd3NlUGF0aCh0aGlzLm1hcHBpbmcuYnJvd3NlUGF0aCk7XG4gICAgICB0aGlzLm5vZGVEaXNwbGF5TmFtZSA9IHRoaXMubWFwcGluZy5uYW1lO1xuICAgIH1cblxuICAgIGlmICh0aGlzLnJlZmVyZW5jZWRSb290Tm9kZUlkKSB7XG4gICAgICB0aGlzLnJlZmVyZW5jZWROb2RlID0geyBub2RlSWQ6IHRoaXMucmVmZXJlbmNlZFJvb3ROb2RlSWQgfTtcbiAgICAgIHRoaXMuYWRkcmVzc1NwYWNlU2VydmljZS50cmlnZ2VyTm9kZVRvT3Blbih7XG4gICAgICAgIG5vZGU6IHtcbiAgICAgICAgICBub2RlSWQ6IHRoaXMucmVmZXJlbmNlZFJvb3ROb2RlSWQsXG4gICAgICAgICAgY2hpbGRyZW46IFtdLFxuICAgICAgICAgIGV4cGFuZGVkOiBmYWxzZSxcbiAgICAgICAgICBhYnNvbHV0ZVBhdGhzOiBbW11dXG4gICAgICAgIH0sXG4gICAgICAgIHNlbGVjdGVkQW5jZXN0b3JJZHM6IFtdXG4gICAgICB9KTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5yZWZlcmVuY2VkTm9kZSA9IHsgbm9kZUlkOiAnJyB9O1xuICAgIH1cblxuICAgIGlmICh0aGlzLm1hcHBpbmc/LmN1c3RvbUFjdGlvbikge1xuICAgICAgT2JqZWN0LmFzc2lnbih0aGlzLm1hcHBpbmcsIHtcbiAgICAgICAgY3VzdG9tQWN0aW9uOiB7XG4gICAgICAgICAgLi4udGhpcy5tYXBwaW5nLmN1c3RvbUFjdGlvbixcbiAgICAgICAgICBoZWFkZXJzOiB0aGlzLm1hcEhlYWRlcnNPYmplY3RUb0xpc3QodGhpcy5tYXBwaW5nPy5jdXN0b21BY3Rpb24/LmhlYWRlcnMpXG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH1cblxuICAgIGlmIChnZXQodGhpcy5fbW9kZWwsICdzdWJzY3JpcHRpb25UeXBlJykpIHtcbiAgICAgIHRoaXMuZGF0YVJlcG9ydGluZyA9ICdjdXN0b20nO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmRhdGFSZXBvcnRpbmcgPSAnZGVmYXVsdCc7XG4gICAgfVxuICAgIHRoaXMucmVzZXRNb2RlbCA9IGZhbHNlO1xuICB9XG5cbiAgc2hvd0FkZHJlc3NTcGFjZVRyZWUoKSB7XG4gICAgcmV0dXJuICFpc0VtcHR5KHRoaXMucmVmZXJlbmNlZFNlcnZlcklkKTtcbiAgfVxuXG4gIG5nQWZ0ZXJWaWV3SW5pdCgpIHtcbiAgICBpZiAoXG4gICAgICBnZXQodGhpcy5tYXBwaW5nLCAnc3Vic2NyaXB0aW9uVHlwZScpICYmXG4gICAgICBnZXQodGhpcy5tYXBwaW5nLCAnc3Vic2NyaXB0aW9uVHlwZS50eXBlJykgIT09ICdOb25lJ1xuICAgICkge1xuICAgICAgdGhpcy5kYXRhUmVwb3J0aW5nID0gJ2N1c3RvbSc7XG4gICAgfVxuICB9XG5cbiAgbWFwSGVhZGVyc09iamVjdFRvTGlzdChoZWFkZXJzKSB7XG4gICAgaWYgKE9iamVjdC5rZXlzKGhlYWRlcnMpLmxlbmd0aCA+IDApIHtcbiAgICAgIHJldHVybiBPYmplY3Qua2V5cyhoZWFkZXJzKS5tYXAoaXRlbSA9PiB7XG4gICAgICAgIHJldHVybiB7IGtleTogaXRlbSwgdmFsdWU6IGhlYWRlcnNbaXRlbV0gfTtcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuXG4gIHN0cmluZ2Z5QnJvd3NlUGF0aChwYXRoKSB7XG4gICAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KHBhdGgpO1xuICB9XG5cbiAgdXBkYXRlQnJvd3NlUGF0aChub2RlKSB7XG4gICAgdGhpcy5tYXBwaW5nLmJyb3dzZVBhdGggPSBub2RlLnJlbGF0aXZlUGF0aDtcbiAgICB0aGlzLm5vZGVEaXNwbGF5TmFtZSA9IG5vZGUuZGlzcGxheU5hbWU7XG4gICAgdGhpcy5tYXBwaW5nLm5hbWUgPSB0aGlzLm5vZGVEaXNwbGF5TmFtZTtcbiAgICB0aGlzLmJyb3dzZVBhdGggPSB0aGlzLnN0cmluZ2Z5QnJvd3NlUGF0aCh0aGlzLm1hcHBpbmcuYnJvd3NlUGF0aCk7XG4gICAgdGhpcy5icm93c2VQYXRoTW9kZWwuY29udHJvbC5tYXJrQXNEaXJ0eSgpO1xuICB9XG5cbiAgdXBkYXRlRGlzcGxheW5hbWUoKSB7XG4gICAgdGhpcy5tYXBwaW5nLm5hbWUgPSB0aGlzLm5vZGVEaXNwbGF5TmFtZTtcbiAgfVxuXG4gIHVwZGF0ZUJyb3dzZVBhdGhJbnB1dCgpIHtcbiAgICBpZiAodGhpcy5icm93c2VQYXRoKSB7XG4gICAgICB0cnkge1xuICAgICAgICB0aGlzLm1hcHBpbmcuYnJvd3NlUGF0aCA9IEpTT04ucGFyc2UodGhpcy5icm93c2VQYXRoKTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBzYXZlKCkge1xuICAgIGlmICh0aGlzLmRhdGFSZXBvcnRpbmcgPT09ICdkZWZhdWx0Jykge1xuICAgICAgdW5zZXQodGhpcy5tYXBwaW5nLCAnc3Vic2NyaXB0aW9uVHlwZScpO1xuICAgIH1cblxuICAgIGlmIChnZXQodGhpcy5tYXBwaW5nLCAnbWVhc3VyZW1lbnRDcmVhdGlvbicpKSB7XG4gICAgICBjb25zdCB7IG1lYXN1cmVtZW50Q3JlYXRpb24gfSA9IHRoaXMubWFwcGluZztcbiAgICAgIHNldChtZWFzdXJlbWVudENyZWF0aW9uLCAnZnJhZ21lbnROYW1lJywgZ2V0KG1lYXN1cmVtZW50Q3JlYXRpb24sICd0eXBlJykpO1xuICAgIH1cblxuICAgIGlmICh0aGlzLm1hcHBpbmcuY3VzdG9tQWN0aW9uKSB7XG4gICAgICB0aGlzLm1hcHBpbmcuY3VzdG9tQWN0aW9uLmhlYWRlcnMgPSB0aGlzLm1hcHBpbmcuY3VzdG9tQWN0aW9uLmhlYWRlcnMucmVkdWNlKFxuICAgICAgICAocmVzdWx0LCBpdGVtKSA9PiB7XG4gICAgICAgICAgcmVzdWx0W2l0ZW0ua2V5XSA9IGl0ZW0udmFsdWU7XG4gICAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICAgICAgfSxcbiAgICAgICAge31cbiAgICAgICk7XG4gICAgfVxuXG4gICAgdGhpcy5vbkFjdGlvbi5lbWl0KHtcbiAgICAgIGFjdGlvbjogJ3NhdmUnLFxuICAgICAgZGF0YTogdGhpcy5tYXBwaW5nXG4gICAgfSk7XG4gICAgdGhpcy5pc0RldGFpbE9wZW4gPSBmYWxzZTtcbiAgfVxuXG4gIGNhbmNlbCgpIHtcbiAgICB0aGlzLmlzRGV0YWlsT3BlbiA9IGZhbHNlO1xuICAgIHRoaXMucmVzZXRNb2RlbCA9IHRydWU7XG5cbiAgICBpZiAodGhpcy5tYXBwaW5nLmlkID09PSAnbmV3Jykge1xuICAgICAgdGhpcy5vbkFjdGlvbi5lbWl0KHtcbiAgICAgICAgYWN0aW9uOiAnZGVsZXRlJyxcbiAgICAgICAgZGF0YTogYXNzaWduKHt9LCB0aGlzLm1hcHBpbmcpXG4gICAgICB9KTtcbiAgICB9XG4gIH1cblxuICBvbkRlbGV0ZSgpIHtcbiAgICB0aGlzLm9uQWN0aW9uLmVtaXQoeyBhY3Rpb246ICdkZWxl