UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

173 lines 33.4 kB
import { Component, Input, Output, EventEmitter } from '@angular/core'; import { AddressSpaceService } from './address-space.service'; import { OpcuaService } from './opcuaService'; import { AlertService } from '@c8y/ngx-components'; import { DynamicDataSource } from './dynamic-data-source'; import { NestedTreeControl } from '@angular/cdk/tree'; import { clone } from 'lodash'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import * as i0 from "@angular/core"; import * as i1 from "./address-space.service"; import * as i2 from "./opcuaService"; import * as i3 from "@c8y/ngx-components"; import * as i4 from "@angular/common"; import * as i5 from "@angular/cdk/tree"; export class OpcuaAddressSpaceTreeComponent { set moId(id) { this._moId = id || undefined; } constructor(addressSpaceService, opcuaService, alertService) { this.addressSpaceService = addressSpaceService; this.opcuaService = opcuaService; this.alertService = alertService; this.focusEmitter = new EventEmitter(); this.selectedNode = new EventEmitter(); this.dataSource = null; this.loading = false; this.destroy$ = new Subject(); this.getChildren = (node) => (node.expanded ? node.children : []); this.hasChild = (_, _nodeData) => this.addressSpaceService.childrenAvailable(_nodeData.references); } ngOnInit() { this.initializeDataSet(); } ngOnChanges(changes) { if (changes.moId && changes.moId.previousValue && changes.moId.currentValue !== changes.moId.previousValue) { this.initializeDataSet(); } } initializeDataSet() { this.nodeNavDataSubscription = this.addressSpaceService .getNodeNavData$() .pipe(takeUntil(this.destroy$)) .subscribe(nodeNavData => this.openNode(nodeNavData)); this.subscriptionRef = this.focusEmitter.subscribe(node => { this.focused = this.isFocusedNode(node) ? undefined : node; }); } ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); // clean up the address-space-tree this.addressSpaceService.resetTreeToRootNode(); if (this.nodeNavDataSubscription && !this.nodeNavDataSubscription.closed) { this.nodeNavDataSubscription.unsubscribe(); } if (this.subscriptionRef && !this.subscriptionRef.closed) { this.subscriptionRef.unsubscribe(); } } async openNode(nodeNavData) { const { node, selectedAncestorIds } = nodeNavData; let nodeId; // We just set the nodeId when the selectedAncestorIds variable an empty array. // If selectedAncestorIds contain any id we assume that the tree should be travsersed beginning // from the root node. if (node && node.nodeId && selectedAncestorIds && selectedAncestorIds.length === 0) { nodeId = node.nodeId; } // Always recreate the tree when routing to a specific nested node, // because previous modifications to the tree-structure could cause errors // while traversing with 'old' tree-data // ----------------- // setupTree is able to handle nodeId = undefined await this.setupTree(nodeId); if (!selectedAncestorIds || selectedAncestorIds.length === 0) { return; } if (nodeNavData && this.dataSource) { const clonedAncestors = clone(selectedAncestorIds); clonedAncestors.shift(); const n = await this.dataSource.toggleNode(this.dataSource.data[0], true); this.setChildNodes(n.children, clonedAncestors); this.toggleFocusedNode(node); } } setChildNodes(nodes, ids) { if (nodes) { ids.forEach(async (id) => { const match = nodes.find(n => n.nodeId === id); if (match && ids.length > 0) { const idx = ids.findIndex(value => value === id); if (idx >= 0) { ids.splice(idx, 1); } const toggledNode = await this.dataSource.toggleNode(match, true); this.setChildNodes(toggledNode.children, ids); } }); } } async setupTree(nodeId) { this.loading = true; if (!this._moId || this._moId.length === 0) { this._moId = this.opcuaService.getMoId(); } // addressSpaceService.getNode returns either the root node of the server (moId) // or if nodeId !== undefined the node with given nodeId const res = await this.addressSpaceService.getNode(this._moId, nodeId); if (res) { if (res.status !== 200) { const data = res.json ? await res.json() : undefined; this.alertService.addServerFailure({ data, res }); this.dataSource = undefined; } else { const rootNode = (await res.json()); this.nestedTreeControl = new NestedTreeControl(this.getChildren); this.dataSource = new DynamicDataSource(this.nestedTreeControl, this.addressSpaceService, this._moId); this.dataSource.data = [rootNode]; } this.loading = false; } else { this.loading = false; } } getMoId() { if (!this._moId || this._moId.length === 0) { return this.opcuaService.getMoId(); } return this._moId; } getIcon(nodeClassName) { return this.addressSpaceService.getIcon(nodeClassName); } toggleFocusedNode(node) { const relativePath = []; this.getRelativePath(node, relativePath); node.relativePath = relativePath; this.selectedNode.emit(node); this.focused = this.isFocusedNode(node) ? undefined : node; } isFocusedNode(node) { if (this.focused) { return node.nodeId === this.focused.nodeId; } return false; } getRelativePath(node, relativePath) { if (node.parentNode) { relativePath.unshift(node.browseName); this.getRelativePath(node.parentNode, relativePath); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: OpcuaAddressSpaceTreeComponent, deps: [{ token: i1.AddressSpaceService }, { token: i2.OpcuaService }, { token: i3.AlertService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: OpcuaAddressSpaceTreeComponent, selector: "opcua-address-space-tree", inputs: { moId: "moId", node: "node", focusEmitter: "focusEmitter" }, outputs: { selectedNode: "selectedNode" }, usesOnChanges: true, ngImport: i0, template: "<div\n class=\"card-block\"\n *ngIf=\"dataSource && !loading\"\n>\n <cdk-tree\n [dataSource]=\"dataSource\"\n [treeControl]=\"nestedTreeControl\"\n >\n <!-- This is the tree node template for leaf nodes -->\n <cdk-nested-tree-node\n class=\"interact\"\n *cdkTreeNodeDef=\"let node\"\n (click)=\"toggleFocusedNode(node)\"\n [ngClass]=\"{ strong: isFocusedNode(node) }\"\n >\n <span>\n <i\n class=\"m-r-4 interact\"\n [c8yIcon]=\"getIcon(node.nodeClassName)\"\n [ngClass]=\"{ strong: isFocusedNode(node) }\"\n ></i>\n {{ node.displayName }}\n </span>\n </cdk-nested-tree-node>\n <!-- This is the tree node template for expandable nodes -->\n <cdk-nested-tree-node *cdkTreeNodeDef=\"let node; when: hasChild\">\n <div role=\"group\">\n <div class=\"d-flex a-i-center\">\n <button\n class=\"btn-clean text-primary m-r-4\"\n title=\"{{ 'Expand node' | translate }}\"\n cdkTreeNodeToggle\n [disabled]=\"node.currentlyLoadingChildren\"\n >\n <i\n [ngClass]=\"{\n 'dlt-c8y-icon-plus-square': !node.expanded,\n 'dlt-c8y-icon-minus-square': node.expanded\n }\"\n ></i>\n </button>\n <i\n class=\"m-r-4 interact\"\n [c8yIcon]=\"getIcon(node.nodeClassName)\"\n ></i>\n <span\n class=\"interact\"\n (click)=\"toggleFocusedNode(node)\"\n [ngClass]=\"{ strong: isFocusedNode(node) }\"\n >\n {{ node.displayName }}\n </span>\n <span\n class=\"m-l-4\"\n [style.visibility]=\"node.currentlyLoadingChildren ? 'visible' : 'hidden'\"\n >\n <i class=\"dlt-c8y-icon-circle-o-notch icon-spin\"></i>\n </span>\n </div>\n <ng-container cdkTreeNodeOutlet></ng-container>\n </div>\n </cdk-nested-tree-node>\n </cdk-tree>\n</div>\n<div\n class=\"p-t-8\"\n *ngIf=\"loading\"\n>\n <c8y-loading></c8y-loading>\n</div>\n<div\n class=\"alert alert-info m-t-16\"\n *ngIf=\"!dataSource && !loading\"\n translate\n>\n No source data available to fetch address space.\n</div>\n", dependencies: [{ kind: "directive", type: i3.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i3.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "directive", type: i5.CdkNestedTreeNode, selector: "cdk-nested-tree-node", exportAs: ["cdkNestedTreeNode"] }, { kind: "directive", type: i5.CdkTreeNodeDef, selector: "[cdkTreeNodeDef]", inputs: ["cdkTreeNodeDefWhen"] }, { kind: "directive", type: i5.CdkTreeNodeToggle, selector: "[cdkTreeNodeToggle]", inputs: ["cdkTreeNodeToggleRecursive"] }, { kind: "component", type: i5.CdkTree, selector: "cdk-tree", inputs: ["dataSource", "treeControl", "levelAccessor", "childrenAccessor", "trackBy", "expansionKey"], exportAs: ["cdkTree"] }, { kind: "directive", type: i5.CdkTreeNodeOutlet, selector: "[cdkTreeNodeOutlet]" }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: OpcuaAddressSpaceTreeComponent, decorators: [{ type: Component, args: [{ selector: 'opcua-address-space-tree', template: "<div\n class=\"card-block\"\n *ngIf=\"dataSource && !loading\"\n>\n <cdk-tree\n [dataSource]=\"dataSource\"\n [treeControl]=\"nestedTreeControl\"\n >\n <!-- This is the tree node template for leaf nodes -->\n <cdk-nested-tree-node\n class=\"interact\"\n *cdkTreeNodeDef=\"let node\"\n (click)=\"toggleFocusedNode(node)\"\n [ngClass]=\"{ strong: isFocusedNode(node) }\"\n >\n <span>\n <i\n class=\"m-r-4 interact\"\n [c8yIcon]=\"getIcon(node.nodeClassName)\"\n [ngClass]=\"{ strong: isFocusedNode(node) }\"\n ></i>\n {{ node.displayName }}\n </span>\n </cdk-nested-tree-node>\n <!-- This is the tree node template for expandable nodes -->\n <cdk-nested-tree-node *cdkTreeNodeDef=\"let node; when: hasChild\">\n <div role=\"group\">\n <div class=\"d-flex a-i-center\">\n <button\n class=\"btn-clean text-primary m-r-4\"\n title=\"{{ 'Expand node' | translate }}\"\n cdkTreeNodeToggle\n [disabled]=\"node.currentlyLoadingChildren\"\n >\n <i\n [ngClass]=\"{\n 'dlt-c8y-icon-plus-square': !node.expanded,\n 'dlt-c8y-icon-minus-square': node.expanded\n }\"\n ></i>\n </button>\n <i\n class=\"m-r-4 interact\"\n [c8yIcon]=\"getIcon(node.nodeClassName)\"\n ></i>\n <span\n class=\"interact\"\n (click)=\"toggleFocusedNode(node)\"\n [ngClass]=\"{ strong: isFocusedNode(node) }\"\n >\n {{ node.displayName }}\n </span>\n <span\n class=\"m-l-4\"\n [style.visibility]=\"node.currentlyLoadingChildren ? 'visible' : 'hidden'\"\n >\n <i class=\"dlt-c8y-icon-circle-o-notch icon-spin\"></i>\n </span>\n </div>\n <ng-container cdkTreeNodeOutlet></ng-container>\n </div>\n </cdk-nested-tree-node>\n </cdk-tree>\n</div>\n<div\n class=\"p-t-8\"\n *ngIf=\"loading\"\n>\n <c8y-loading></c8y-loading>\n</div>\n<div\n class=\"alert alert-info m-t-16\"\n *ngIf=\"!dataSource && !loading\"\n translate\n>\n No source data available to fetch address space.\n</div>\n" }] }], ctorParameters: () => [{ type: i1.AddressSpaceService }, { type: i2.OpcuaService }, { type: i3.AlertService }], propDecorators: { moId: [{ type: Input }], node: [{ type: Input }], focusEmitter: [{ type: Input }], selectedNode: [{ type: Output }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BjdWEtYWRkcmVzcy1zcGFjZS10cmVlLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3Byb3RvY29sLW9wY3VhL29wY3VhLWFkZHJlc3Mtc3BhY2UtdHJlZS5jb21wb25lbnQudHMiLCIuLi8uLi8uLi9wcm90b2NvbC1vcGN1YS9vcGN1YS1hZGRyZXNzLXNwYWNlLXRyZWUuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUNMLFNBQVMsRUFDVCxLQUFLLEVBQ0wsTUFBTSxFQUVOLFlBQVksRUFJYixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQW9CLG1CQUFtQixFQUFzQixNQUFNLHlCQUF5QixDQUFDO0FBQ3BHLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUM5QyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDbkQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDMUQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDdEQsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUMvQixPQUFPLEVBQUUsT0FBTyxFQUFnQixNQUFNLE1BQU0sQ0FBQztBQUM3QyxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7Ozs7Ozs7QUFNM0MsTUFBTSxPQUFPLDhCQUE4QjtJQUN6QyxJQUNJLElBQUksQ0FBQyxFQUFVO1FBQ2pCLElBQUksQ0FBQyxLQUFLLEdBQUcsRUFBRSxJQUFJLFNBQVMsQ0FBQztJQUMvQixDQUFDO0lBY0QsWUFDVSxtQkFBd0MsRUFDeEMsWUFBMEIsRUFDMUIsWUFBMEI7UUFGMUIsd0JBQW1CLEdBQW5CLG1CQUFtQixDQUFxQjtRQUN4QyxpQkFBWSxHQUFaLFlBQVksQ0FBYztRQUMxQixpQkFBWSxHQUFaLFlBQVksQ0FBYztR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QUM7UUFDN0IsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztJQUM3RCxDQUFDO0lBRUQsYUFBYSxDQUFDLElBQXNCO1FBQ2xDLElBQUksSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ2pCLE9BQU8sSUFBSSxDQUFDLE1BQU0sS0FBSyxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQztRQUM3QyxDQUFDO1FBQ0QsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0lBRU8sZUFBZSxDQUFDLElBQXNCLEVBQUUsWUFBc0I7UUFDcEUsSUFBSSxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7WUFDcEIsWUFBWSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUM7WUFDdEMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLFlBQVksQ0FBQyxDQUFDO1FBQ3RELENBQUM7SUFDSCxDQUFDOytHQWxMVSw4QkFBOEI7bUdBQTlCLDhCQUE4QixzTUN2QjNDLHF3RUE2RUE7OzRGRHREYSw4QkFBOEI7a0JBSjFDLFNBQVM7K0JBQ0UsMEJBQTBCOzhJQUtoQyxJQUFJO3NCQURQLEtBQUs7Z0JBS0csSUFBSTtzQkFBWixLQUFLO2dCQUNHLFlBQVk7c0JBQXBCLEtBQUs7Z0JBQ0ksWUFBWTtzQkFBckIsTUFBTSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIENvbXBvbmVudCxcbiAgSW5wdXQsXG4gIE91dHB1dCxcbiAgT25Jbml0LFxuICBFdmVudEVtaXR0ZXIsXG4gIE9uRGVzdHJveSxcbiAgT25DaGFuZ2VzLFxuICBTaW1wbGVDaGFuZ2VzXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgQWRkcmVzc1NwYWNlTm9kZSwgQWRkcmVzc1NwYWNlU2VydmljZSwgTm9kZU5hdmlnYXRpb25EYXRhIH0gZnJvbSAnLi9hZGRyZXNzLXNwYWNlLnNlcnZpY2UnO1xuaW1wb3J0IHsgT3BjdWFTZXJ2aWNlIH0gZnJvbSAnLi9vcGN1YVNlcnZpY2UnO1xuaW1wb3J0IHsgQWxlcnRTZXJ2aWNlIH0gZnJvbSAnQGM4eS9uZ3gtY29tcG9uZW50cyc7XG5pbXBvcnQgeyBEeW5hbWljRGF0YVNvdXJjZSB9IGZyb20gJy4vZHluYW1pYy1kYXRhLXNvdXJjZSc7XG5pbXBvcnQgeyBOZXN0ZWRUcmVlQ29udHJvbCB9IGZyb20gJ0Bhbmd1bGFyL2Nkay90cmVlJztcbmltcG9ydCB7IGNsb25lIH0gZnJvbSAnbG9kYXNoJztcbmltcG9ydCB7IFN1YmplY3QsIFN1YnNjcmlwdGlvbiB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgdGFrZVVudGlsIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdvcGN1YS1hZGRyZXNzLXNwYWNlLXRyZWUnLFxuICB0ZW1wbGF0ZVVybDogJy4vb3BjdWEtYWRkcmVzcy1zcGFjZS10cmVlLmNvbXBvbmVudC5odG1sJ1xufSlcbmV4cG9ydCBjbGFzcyBPcGN1YUFkZHJlc3NTcGFjZVRyZWVDb21wb25lbnQgaW1wbGVtZW50cyBPbkluaXQsIE9uRGVzdHJveSwgT25DaGFuZ2VzIHtcbiAgQElucHV0KClcbiAgc2V0IG1vSWQoaWQ6IHN0cmluZykge1xuICAgIHRoaXMuX21vSWQgPSBpZCB8fCB1bmRlZmluZWQ7XG4gIH1cblxuICBASW5wdXQoKSBub2RlO1xuICBASW5wdXQoKSBmb2N1c0VtaXR0ZXI6IEV2ZW50RW1pdHRlcjxBZGRyZXNzU3BhY2VOb2RlPiA9IG5ldyBFdmVudEVtaXR0ZXI8QWRkcmVzc1NwYWNlTm9kZT4oKTtcbiAgQE91dHB1dCgpIHNlbGVjdGVkTm9kZTogRXZlbnRFbWl0dGVyPEFkZHJlc3NTcGFjZU5vZGU+ID0gbmV3IEV2ZW50RW1pdHRlcjxBZGRyZXNzU3BhY2VOb2RlPigpO1xuICBuZXN0ZWRUcmVlQ29udHJvbDogTmVzdGVkVHJlZUNvbnRyb2w8QWRkcmVzc1NwYWNlTm9kZT47XG4gIGRhdGFTb3VyY2U6IER5bmFtaWNEYXRhU291cmNlID0gbnVsbDtcbiAgZm9jdXNlZDogQWRkcmVzc1NwYWNlTm9kZTtcbiAgbG9hZGluZyA9IGZhbHNlO1xuICBzdWJzY3JpcHRpb25SZWY6IFN1YnNjcmlwdGlvbjtcbiAgbm9kZU5hdkRhdGFTdWJzY3JpcHRpb246IFN1YnNjcmlwdGlvbjtcbiAgcHJpdmF0ZSBfbW9JZDogc3RyaW5nO1xuICBwcml2YXRlIGRlc3Ryb3kkOiBTdWJqZWN0PHZvaWQ+ID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIGFkZHJlc3NTcGFjZVNlcnZpY2U6IEFkZHJlc3NTcGFjZVNlcnZpY2UsXG4gICAgcHJpdmF0ZSBvcGN1YVNlcnZpY2U6IE9wY3VhU2VydmljZSxcbiAgICBwcml2YXRlIGFsZXJ0U2VydmljZTogQWxlcnRTZXJ2aWNlXG4gICkge31cblxuICBnZXRDaGlsZHJlbiA9IChub2RlOiBBZGRyZXNzU3BhY2VOb2RlKSA9PiAobm9kZS5leHBhbmRlZCA/IG5vZGUuY2hpbGRyZW4gOiBbXSk7XG4gIGhhc0NoaWxkID0gKF86IG51bWJlciwgX25vZGVEYXRhOiBBZGRyZXNzU3BhY2VOb2RlKSA9PlxuICAgIHRoaXMuYWRkcmVzc1NwYWNlU2VydmljZS5jaGlsZHJlbkF2YWlsYWJsZShfbm9kZURhdGEucmVmZXJlbmNlcyk7XG5cbiAgbmdPbkluaXQoKSB7XG4gICAgdGhpcy5pbml0aWFsaXplRGF0YVNldCgpO1xuICB9XG5cbiAgbmdPbkNoYW5nZXMoY2hhbmdlczogU2ltcGxlQ2hhbmdlcykge1xuICAgIGlmIChcbiAgICAgIGNoYW5nZXMubW9JZCAmJlxuICAgICAgY2hhbmdlcy5tb0lkLnByZXZpb3VzVmFsdWUgJiZcbiAgICAgIGNoYW5nZXMubW9JZC5jdXJyZW50VmFsdWUgIT09IGNoYW5nZXMubW9JZC5wcmV2aW91c1ZhbHVlXG4gICAgKSB7XG4gICAgICB0aGlzLmluaXRpYWxpemVEYXRhU2V0KCk7XG4gICAgfVxuICB9XG5cbiAgaW5pdGlhbGl6ZURhdGFTZXQoKSB7XG4gICAgdGhpcy5ub2RlTmF2RGF0YVN1YnNjcmlwdGlvbiA9IHRoaXMuYWRkcmVzc1NwYWNlU2VydmljZVxuICAgICAgLmdldE5vZGVOYXZEYXRhJCgpXG4gICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5kZXN0cm95JCkpXG4gICAgICAuc3Vic2NyaWJlKG5vZGVOYXZEYXRhID0+IHRoaXMub3Blbk5vZGUobm9kZU5hdkRhdGEpKTtcbiAgICB0aGlzLnN1YnNjcmlwdGlvblJlZiA9IHRoaXMuZm9jdXNFbWl0dGVyLnN1YnNjcmliZShub2RlID0+IHtcbiAgICAgIHRoaXMuZm9jdXNlZCA9IHRoaXMuaXNGb2N1c2VkTm9kZShub2RlKSA/IHVuZGVmaW5lZCA6IG5vZGU7XG4gICAgfSk7XG4gIH1cblxuICBuZ09uRGVzdHJveSgpIHtcbiAgICB0aGlzLmRlc3Ryb3kkLm5leHQoKTtcbiAgICB0aGlzLmRlc3Ryb3kkLmNvbXBsZXRlKCk7XG4gICAgLy8gY2xlYW4gdXAgdGhlIGFkZHJlc3Mtc3BhY2UtdHJlZVxuICAgIHRoaXMuYWRkcmVzc1NwYWNlU2VydmljZS5yZXNldFRyZWVUb1Jvb3ROb2RlKCk7XG5cbiAgICBpZiAodGhpcy5ub2RlTmF2RGF0YVN1YnNjcmlwdGlvbiAmJiAhdGhpcy5ub2RlTmF2RGF0YVN1YnNjcmlwdGlvbi5jbG9zZWQpIHtcbiAgICAgIHRoaXMubm9kZU5hdkRhdGFTdWJzY3JpcHRpb24udW5zdWJzY3JpYmUoKTtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5zdWJzY3JpcHRpb25SZWYgJiYgIXRoaXMuc3Vic2NyaXB0aW9uUmVmLmNsb3NlZCkge1xuICAgICAgdGhpcy5zdWJzY3JpcHRpb25SZWYudW5zdWJzY3JpYmUoKTtcbiAgICB9XG4gIH1cblxuICBhc3luYyBvcGVuTm9kZShub2RlTmF2RGF0YTogTm9kZU5hdmlnYXRpb25EYXRhKSB7XG4gICAgY29uc3QgeyBub2RlLCBzZWxlY3RlZEFuY2VzdG9ySWRzIH0gPSBub2RlTmF2RGF0YTtcbiAgICBsZXQgbm9kZUlkO1xuXG4gICAgLy8gV2UganVzdCBzZXQgdGhlIG5vZGVJZCB3aGVuIHRoZSBzZWxlY3RlZEFuY2VzdG9ySWRzIHZhcmlhYmxlIGFuIGVtcHR5IGFycmF5LlxuICAgIC8vIElmIHNlbGVjdGVkQW5jZXN0b3JJZHMgY29udGFpbiBhbnkgaWQgd2UgYXNzdW1lIHRoYXQgdGhlIHRyZWUgc2hvdWxkIGJlIHRyYXZzZXJzZWQgYmVnaW5uaW5nXG4gICAgLy8gZnJvbSB0aGUgcm9vdCBub2RlLlxuICAgIGlmIChub2RlICYmIG5vZGUubm9kZUlkICYmIHNlbGVjdGVkQW5jZXN0b3JJZHMgJiYgc2VsZWN0ZWRBbmNlc3Rvcklkcy5sZW5ndGggPT09IDApIHtcbiAgICAgIG5vZGVJZCA9IG5vZGUubm9kZUlkO1xuICAgIH1cbiAgICAvLyBBbHdheXMgcmVjcmVhdGUgdGhlIHRyZWUgd2hlbiByb3V0aW5nIHRvIGEgc3BlY2lmaWMgbmVzdGVkIG5vZGUsXG4gICAgLy8gYmVjYXVzZSBwcmV2aW91cyBtb2RpZmljYXRpb25zIHRvIHRoZSB0cmVlLXN0cnVjdHVyZSBjb3VsZCBjYXVzZSBlcnJvcnNcbiAgICAvLyB3aGlsZSB0cmF2ZXJzaW5nIHdpdGggJ29sZCcgdHJlZS1kYXRhXG4gICAgLy8gLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAvLyBzZXR1cFRyZWUgaXMgYWJsZSB0byBoYW5kbGUgbm9kZUlkID0gdW5kZWZpbmVkXG4gICAgYXdhaXQgdGhpcy5zZXR1cFRyZWUobm9kZUlkKTtcblxuICAgIGlmICghc2VsZWN0ZWRBbmNlc3RvcklkcyB8fCBzZWxlY3RlZEFuY2VzdG9ySWRzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGlmIChub2RlTmF2RGF0YSAmJiB0aGlzLmRhdGFTb3VyY2UpIHtcbiAgICAgIGNvbnN0IGNsb25lZEFuY2VzdG9ycyA9IGNsb25lKHNlbGVjdGVkQW5jZXN0b3JJZHMpO1xuICAgICAgY2xvbmVkQW5jZXN0b3JzLnNoaWZ0KCk7XG5cbiAgICAgIGNvbnN0IG4gPSBhd2FpdCB0aGlzLmRhdGFTb3VyY2UudG9nZ2xlTm9kZSh0aGlzLmRhdGFTb3VyY2UuZGF0YVswXSwgdHJ1ZSk7XG4gICAgICB0aGlzLnNldENoaWxkTm9kZXMobi5jaGlsZHJlbiwgY2xvbmVkQW5jZXN0b3JzKTtcblxuICAgICAgdGhpcy50b2dnbGVGb2N1c2VkTm9kZShub2RlKTtcbiAgICB9XG4gIH1cblxuICBzZXRDaGlsZE5vZGVzKG5vZGVzOiBBZGRyZXNzU3BhY2VOb2RlW10sIGlkczogc3RyaW5nW10pIHtcbiAgICBpZiAobm9kZXMpIHtcbiAgICAgIGlkcy5mb3JFYWNoKGFzeW5jIGlkID0+IHtcbiAgICAgICAgY29uc3QgbWF0Y2ggPSBub2Rlcy5maW5kKG4gPT4gbi5ub2RlSWQgPT09IGlkKTtcbiAgICAgICAgaWYgKG1hdGNoICYmIGlkcy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgY29uc3QgaWR4ID0gaWRzLmZpbmRJbmRleCh2YWx1ZSA9PiB2YWx1ZSA9PT0gaWQpO1xuICAgICAgICAgIGlmIChpZHggPj0gMCkge1xuICAgICAgICAgICAgaWRzLnNwbGljZShpZHgsIDEpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBjb25zdCB0b2dnbGVkTm9kZSA9IGF3YWl0IHRoaXMuZGF0YVNvdXJjZS50b2dnbGVOb2RlKG1hdGNoLCB0cnVlKTtcbiAgICAgICAgICB0aGlzLnNldENoaWxkTm9kZXModG9nZ2xlZE5vZGUuY2hpbGRyZW4sIGlkcyk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIHNldHVwVHJlZShub2RlSWQ/OiBzdHJpbmcpIHtcbiAgICB0aGlzLmxvYWRpbmcgPSB0cnVlO1xuXG4gICAgaWYgKCF0aGlzLl9tb0lkIHx8IHRoaXMuX21vSWQubGVuZ3RoID09PSAwKSB7XG4gICAgICB0aGlzLl9tb0lkID0gdGhpcy5vcGN1YVNlcnZpY2UuZ2V0TW9JZCgpO1xuICAgIH1cblxuICAgIC8vIGFkZHJlc3NTcGFjZVNlcnZpY2UuZ2V0Tm9kZSByZXR1cm5zIGVpdGhlciB0aGUgcm9vdCBub2RlIG9mIHRoZSBzZXJ2ZXIgKG1vSWQpXG4gICAgLy8gb3IgaWYgbm9kZUlkICE9PSB1bmRlZmluZWQgdGhlIG5vZGUgd2l0aCBnaXZlbiBub2RlSWRcbiAgICBjb25zdCByZXMgPSBhd2FpdCB0aGlzLmFkZHJlc3NTcGFjZVNlcnZpY2UuZ2V0Tm9kZSh0aGlzLl9tb0lkLCBub2RlSWQpO1xuICAgIGlmIChyZXMpIHtcbiAgICAgIGlmIChyZXMuc3RhdHVzICE9PSAyMDApIHtcbiAgICAgICAgY29uc3QgZGF0YSA9IHJlcy5qc29uID8gYXdhaXQgcmVzLmpzb24oKSA6IHVuZGVmaW5lZDtcbiAgICAgICAgdGhpcy5hbGVydFNlcnZpY2UuYWRkU2VydmVyRmFpbHVyZSh7IGRhdGEsIHJlcyB9KTtcbiAgICAgICAgdGhpcy5kYXRhU291cmNlID0gdW5kZWZpbmVkO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29uc3Qgcm9vdE5vZGUgPSAoYXdhaXQgcmVzLmpzb24oKSkgYXMgQWRkcmVzc1NwYWNlTm9kZTtcbiAgICAgICAgdGhpcy5uZXN0ZWRUcmVlQ29udHJvbCA9IG5ldyBOZXN0ZWRUcmVlQ29udHJvbDxBZGRyZXNzU3BhY2VOb2RlPih0aGlzLmdldENoaWxkcmVuKTtcbiAgICAgICAgdGhpcy5kYXRhU291cmNlID0gbmV3IER5bmFtaWNEYXRhU291cmNlKFxuICAgICAgICAgIHRoaXMubmVzdGVkVHJlZUNvbnRyb2wsXG4gICAgICAgICAgdGhpcy5hZGRyZXNzU3BhY2VTZXJ2aWNlLFxuICAgICAgICAgIHRoaXMuX21vSWRcbiAgICAgICAgKTtcbiAgICAgICAgdGhpcy5kYXRhU291cmNlLmRhdGEgPSBbcm9vdE5vZGVdO1xuICAgICAgfVxuICAgICAgdGhpcy5sb2FkaW5nID0gZmFsc2U7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMubG9hZGluZyA9IGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIGdldE1vSWQoKSB7XG4gICAgaWYgKCF0aGlzLl9tb0lkIHx8IHRoaXMuX21vSWQubGVuZ3RoID09PSAwKSB7XG4gICAgICByZXR1cm4gdGhpcy5vcGN1YVNlcnZpY2UuZ2V0TW9JZCgpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fbW9JZDtcbiAgfVxuXG4gIGdldEljb24obm9kZUNsYXNzTmFtZSkge1xuICAgIHJldHVybiB0aGlzLmFkZHJlc3NTcGFjZVNlcnZpY2UuZ2V0SWNvbihub2RlQ2xhc3NOYW1lKTtcbiAgfVxuXG4gIHRvZ2dsZUZvY3VzZWROb2RlKG5vZGUpIHtcbiAgICBjb25zdCByZWxhdGl2ZVBhdGggPSBbXTtcbiAgICB0aGlzLmdldFJlbGF0aXZlUGF0aChub2RlLCByZWxhdGl2ZVBhdGgpO1xuICAgIG5vZGUucmVsYXRpdmVQYXRoID0gcmVsYXRpdmVQYXRoO1xuXG4gICAgdGhpcy5zZWxlY3RlZE5vZGUuZW1pdChub2RlKTtcbiAgICB0aGlzLmZvY3VzZWQgPSB0aGlzLmlzRm9jdXNlZE5vZGUobm9kZSkgPyB1bmRlZmluZWQgOiBub2RlO1xuICB9XG5cbiAgaXNGb2N1c2VkTm9kZShub2RlOiBBZGRyZXNzU3BhY2VOb2RlKSB7XG4gICAgaWYgKHRoaXMuZm9jdXNlZCkge1xuICAgICAgcmV0dXJuIG5vZGUubm9kZUlkID09PSB0aGlzLmZvY3VzZWQubm9kZUlkO1xuICAgIH1cbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICBwcml2YXRlIGdldFJlbGF0aXZlUGF0aChub2RlOiBBZGRyZXNzU3BhY2VOb2RlLCByZWxhdGl2ZVBhdGg6IHN0cmluZ1tdKSB7XG4gICAgaWYgKG5vZGUucGFyZW50Tm9kZSkge1xuICAgICAgcmVsYXRpdmVQYXRoLnVuc2hpZnQobm9kZS5icm93c2VOYW1lKTtcbiAgICAgIHRoaXMuZ2V0UmVsYXRpdmVQYXRoKG5vZGUucGFyZW50Tm9kZSwgcmVsYXRpdmVQYXRoKTtcbiAgICB9XG4gIH1cbn1cbiIsIjxkaXZcbiAgY2xhc3M9XCJjYXJkLWJsb2NrXCJcbiAgKm5nSWY9XCJkYXRhU291cmNlICYmICFsb2FkaW5nXCJcbj5cbiAgPGNkay10cmVlXG4gICAgW2RhdGFTb3VyY2VdPVwiZGF0YVNvdXJjZVwiXG4gICAgW3RyZWVDb250cm9sXT1cIm5lc3RlZFRyZWVDb250cm9sXCJcbiAgPlxuICAgIDwhLS0gVGhpcyBpcyB0aGUgdHJlZSBub2RlIHRlbXBsYXRlIGZvciBsZWFmIG5vZGVzIC0tPlxuICAgIDxjZGstbmVzdGVkLXRyZWUtbm9kZVxuICAgICAgY2xhc3M9XCJpbnRlcmFjdFwiXG4gICAgICAqY2RrVHJlZU5vZGVEZWY9XCJsZXQgbm9kZVwiXG4gICAgICAoY2xpY2spPVwidG9nZ2xlRm9jdXNlZE5vZGUobm9kZSlcIlxuICAgICAgW25nQ2xhc3NdPVwieyBzdHJvbmc6IGlzRm9jdXNlZE5vZGUobm9kZSkgfVwiXG4gICAgPlxuICAgICAgPHNwYW4+XG4gICAgICAgIDxpXG4gICAgICAgICAgY2xhc3M9XCJtLXItNCBpbnRlcmFjdFwiXG4gICAgICAgICAgW2M4eUljb25dPVwiZ2V0SWNvbihub2RlLm5vZGVDbGFzc05hbWUpXCJcbiAgICAgICAgICBbbmdDbGFzc109XCJ7IHN0cm9uZzogaXNGb2N1c2VkTm9kZShub2RlKSB9XCJcbiAgICAgICAgPjwvaT5cbiAgICAgICAge3sgbm9kZS5kaXNwbGF5TmFtZSB9fVxuICAgICAgPC9zcGFuPlxuICAgIDwvY2RrLW5lc3RlZC10cmVlLW5vZGU+XG4gICAgPCEtLSBUaGlzIGlzIHRoZSB0cmVlIG5vZGUgdGVtcGxhdGUgZm9yIGV4cGFuZGFibGUgbm9kZXMgLS0+XG4gICAgPGNkay1uZXN0ZWQtdHJlZS1ub2RlICpjZGtUcmVlTm9kZURlZj1cImxldCBub2RlOyB3aGVuOiBoYXNDaGlsZFwiPlxuICAgICAgPGRpdiByb2xlPVwiZ3JvdXBcIj5cbiAgICAgICAgPGRpdiBjbGFzcz1cImQtZmxleCBhLWktY2VudGVyXCI+XG4gICAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICAgY2xhc3M9XCJidG4tY2xlYW4gdGV4dC1wcmltYXJ5IG0tci00XCJcbiAgICAgICAgICAgIHRpdGxlPVwie3sgJ0V4cGFuZCBub2RlJyB8IHRyYW5zbGF0ZSB9fVwiXG4gICAgICAgICAgICBjZGtUcmVlTm9kZVRvZ2dsZVxuICAgICAgICAgICAgW2Rpc2FibGVkXT1cIm5vZGUuY3VycmVudGx5TG9hZGluZ0NoaWxkcmVuXCJcbiAgICAgICAgICA+XG4gICAgICAgICAgICA8aVxuICAgICAgICAgICAgICBbbmdDbGFzc109XCJ7XG4gICAgICAgICAgICAgICAgJ2RsdC1jOHktaWNvbi1wbHVzLXNxdWFyZSc6ICFub2RlLmV4cGFuZGVkLFxuICAgICAgICAgICAgICAgICdkbHQtYzh5LWljb24tbWludXMtc3F1YXJlJzogbm9kZS5leHBhbmRlZFxuICAgICAgICAgICAgICB9XCJcbiAgICAgICAgICAgID48L2k+XG4gICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgPGlcbiAgICAgICAgICAgIGNsYXNzPVwibS1yLTQgaW50ZXJhY3RcIlxuICAgICAgICAgICAgW2M4eUljb25dPVwiZ2V0SWNvbihub2RlLm5vZGVDbGFzc05hbWUpXCJcbiAgICAgICAgICA+PC9pPlxuICAgICAgICAgIDxzcGFuXG4gICAgICAgICAgICBjbGFzcz1cImludGVyYWN0XCJcbiAgICAgICAgICAgIChjbGljayk9XCJ0b2dnbGVGb2N1c2VkTm9kZShub2RlKVwiXG4gICAgICAgICAgICBbbmdDbGFzc109XCJ7IHN0cm9uZzogaXNGb2N1c2VkTm9kZShub2RlKSB9XCJcbiAgICAgICAgICA+XG4gICAgICAgICAgICB7eyBub2RlLmRpc3BsYXlOYW1lIH19XG4gICAgICAgICAgPC9zcGFuPlxuICAgICAgICAgIDxzcGFuXG4gICAgICAgICAgICBjbGFzcz1cIm0tbC00XCJcbiAgICAgICAgICAgIFtzdHlsZS52aXNpYmlsaXR5XT1cIm5vZGUuY3VycmVudGx5TG9hZGluZ0NoaWxkcmVuID8gJ3Zpc2libGUnIDogJ2hpZGRlbidcIlxuICAgICAgICAgID5cbiAgICAgICAgICAgIDxpIGNsYXNzPVwiZGx0LWM4eS1pY29uLWNpcmNsZS1vLW5vdGNoIGljb24tc3BpblwiPjwvaT5cbiAgICAgICAgICA8L3NwYW4+XG4gICAgICAgIDwvZGl2PlxuICAgICAgICA8bmctY29udGFpbmVyIGNka1RyZWVOb2RlT3V0bGV0PjwvbmctY29udGFpbmVyPlxuICAgICAgPC9kaXY+XG4gICAgPC9jZGstbmVzdGVkLXRyZWUtbm9kZT5cbiAgPC9jZGstdHJlZT5cbjwvZGl2PlxuPGRpdlxuICBjbGFzcz1cInAtdC04XCJcbiAgKm5nSWY9XCJsb2FkaW5nXCJcbj5cbiAgPGM4eS1sb2FkaW5nPjwvYzh5LWxvYWRpbmc+XG48L2Rpdj5cbjxkaXZcbiAgY2xhc3M9XCJhbGVydCBhbGVydC1pbmZvIG0tdC0xNlwiXG4gICpuZ0lmPVwiIWRhdGFTb3VyY2UgJiYgIWxvYWRpbmdcIlxuICB0cmFuc2xhdGVcbj5cbiAgTm8gc291cmNlIGRhdGEgYXZhaWxhYmxlIHRvIGZldGNoIGFkZHJlc3Mgc3BhY2UuXG48L2Rpdj5cbiJdfQ==