@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
88 lines • 23.7 kB
JavaScript
import { Component, Output, EventEmitter } from '@angular/core';
import { AddressSpaceService } from './address-space.service';
import { gettext } from '@c8y/ngx-components';
import { OpcuaService } from './opcuaService';
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/forms";
import * as i6 from "./opcua-address-space-tree.component";
import * as i7 from "./opcua-address-space-detail.component";
export class OpcuaAddressSpaceComponent {
constructor(addressSpaceService, opcuaService) {
this.addressSpaceService = addressSpaceService;
this.opcuaService = opcuaService;
this.selectednode = false;
this.loading = false;
this.searchInProgress = false;
this.focusStatus = new EventEmitter();
this.moId = '';
}
async ngOnInit() {
this.filterLabel = gettext('Filter…');
this.moId = this.opcuaService.getMoId();
}
ngOnDestroy() {
// The BehaviourSubject will store the last array of ancestorNodes from the previous search
// this would cause the component while subscribing in the init-phase to the subject to travers
// to the last searched node again. From user perspective it does not make sense, because the user
// left the Address space (tab) and should loose the context and just request a new search or
// browse the tree manually.
this.addressSpaceService.resetTreeToRootNode();
}
async searchNodes() {
this.searchInProgress = true;
this.clearNodeListAndCheckSearchString();
if (this.isSearch) {
this.currentNode = undefined;
this.nodeList = await this.addressSpaceService.getSearchedNodes(this.searchKey, this.moId);
this.searchInProgress = false;
this.nodeList.resultLabel = gettext('Results found');
}
}
clearNodeListAndCheckSearchString() {
this.isSearch = this.searchKey !== undefined && this.searchKey !== '' ? true : false;
if (!this.isSearch) {
this.searchInProgress = false;
}
}
clearSearch() {
this.isSearch = false;
this.searchKey = '';
this.currentNode = undefined;
}
getIcon(nodeClassName) {
return this.addressSpaceService.getIcon(nodeClassName);
}
async selectNode(node) {
if (node && node.nodeId && node.nodeId.length > 0) {
const res = await this.addressSpaceService.getNodeById(this.moId, node.nodeId);
this.toggleCurrentNode((await res.json()));
}
}
toggleCurrentNode(node) {
this.currentNode = this.isNodeSet(node) ? undefined : node;
}
backHandler(node) {
this.isSearch = false;
this.focusStatus.emit(node);
this.toggleCurrentNode(node);
}
isNodeSet(node) {
if (this.currentNode !== undefined && this.currentNode.nodeId === node.nodeId) {
return true;
}
return false;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: OpcuaAddressSpaceComponent, deps: [{ token: i1.AddressSpaceService }, { token: i2.OpcuaService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: OpcuaAddressSpaceComponent, selector: "opcua-address-space", outputs: { focusStatus: "focusStatus" }, ngImport: i0, template: "<div class=\"row split-scroll\">\n <div class=\"col-md-5 col-xs-12 scroll-column no-gutter-r\">\n <div class=\"card bg-level-2 split-scroll overflow-auto\">\n <div class=\"card-block separator-bottom sticky-top\">\n <div class=\"input-group input-group-search\">\n <input\n class=\"form-control\"\n placeholder=\"{{ filterLabel | translate }}\"\n type=\"search\"\n (keydown.enter)=\"searchNodes()\"\n [(ngModel)]=\"searchKey\"\n />\n <span class=\"input-group-btn\">\n <button\n class=\"btn btn-dot\"\n title=\"{{ 'Search' | translate }}\"\n type=\"submit\"\n *ngIf=\"!isSearch\"\n (click)=\"searchNodes()\"\n >\n <i c8yIcon=\"search\"></i>\n </button>\n <button\n class=\"btn btn-dot\"\n title=\"{{ 'Clear`input`' | translate }}\"\n type=\"button\"\n *ngIf=\"isSearch\"\n (click)=\"clearSearch()\"\n >\n <i c8yIcon=\"times\"></i>\n </button>\n </span>\n </div>\n <div\n class=\"p-t-16\"\n *ngIf=\"isSearch && !loading\"\n >\n <p *ngIf=\"!searchInProgress\">\n <em>{{ nodeList.resultLabel | translate }}</em>\n \n <span class=\"badge badge-info\">{{ nodeList?.length }}</span>\n </p>\n </div>\n </div>\n\n <div\n class=\"p-t-8\"\n *ngIf=\"(isSearch && loading) || searchInProgress\"\n >\n <c8y-loading></c8y-loading>\n </div>\n\n <div\n class=\"inner-scroll\"\n *ngIf=\"isSearch && !loading && !searchInProgress\"\n >\n <div\n class=\"list-group list-group-links\"\n *ngIf=\"isSearch && !loading\"\n >\n <button\n *ngFor=\"let nodeItem of nodeList\"\n (click)=\"selectNode(nodeItem)\"\n [ngClass]=\"{ 'list-group-item d-flex': true }\"\n >\n <div class=\"list-group-icon m-r-4\">\n <i\n class=\"m-r-4\"\n [c8yIcon]=\"getIcon(nodeItem.nodeClassName)\"\n ></i>\n </div>\n <div class=\"list-item-body text-truncate\">\n <span tile=\"nodeId\">{{ nodeItem.nodeId }}</span>\n {{ nodeItem.displayName }}\n </div>\n </button>\n </div>\n </div>\n <div\n class=\"inner-scroll\"\n *ngIf=\"!isSearch\"\n >\n <opcua-address-space-tree\n (selectedNode)=\"toggleCurrentNode($event)\"\n [focusEmitter]=\"focusStatus\"\n ></opcua-address-space-tree>\n </div>\n </div>\n </div>\n <opcua-address-space-detail\n class=\"col-md-7 col-xs-12 scroll-column no-gutter-l no-pointer\"\n [node]=\"currentNode\"\n (toggleAttrDetail)=\"backHandler($event)\"\n ></opcua-address-space-detail>\n</div>\n", dependencies: [{ kind: "directive", type: i3.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { 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.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: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i5.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i6.OpcuaAddressSpaceTreeComponent, selector: "opcua-address-space-tree", inputs: ["moId", "node", "focusEmitter"], outputs: ["selectedNode"] }, { kind: "component", type: i7.OpcuaAddressSpaceDetailComponent, selector: "opcua-address-space-detail", inputs: ["node"], outputs: ["toggleAttrDetail"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: OpcuaAddressSpaceComponent, decorators: [{
type: Component,
args: [{ selector: 'opcua-address-space', template: "<div class=\"row split-scroll\">\n <div class=\"col-md-5 col-xs-12 scroll-column no-gutter-r\">\n <div class=\"card bg-level-2 split-scroll overflow-auto\">\n <div class=\"card-block separator-bottom sticky-top\">\n <div class=\"input-group input-group-search\">\n <input\n class=\"form-control\"\n placeholder=\"{{ filterLabel | translate }}\"\n type=\"search\"\n (keydown.enter)=\"searchNodes()\"\n [(ngModel)]=\"searchKey\"\n />\n <span class=\"input-group-btn\">\n <button\n class=\"btn btn-dot\"\n title=\"{{ 'Search' | translate }}\"\n type=\"submit\"\n *ngIf=\"!isSearch\"\n (click)=\"searchNodes()\"\n >\n <i c8yIcon=\"search\"></i>\n </button>\n <button\n class=\"btn btn-dot\"\n title=\"{{ 'Clear`input`' | translate }}\"\n type=\"button\"\n *ngIf=\"isSearch\"\n (click)=\"clearSearch()\"\n >\n <i c8yIcon=\"times\"></i>\n </button>\n </span>\n </div>\n <div\n class=\"p-t-16\"\n *ngIf=\"isSearch && !loading\"\n >\n <p *ngIf=\"!searchInProgress\">\n <em>{{ nodeList.resultLabel | translate }}</em>\n \n <span class=\"badge badge-info\">{{ nodeList?.length }}</span>\n </p>\n </div>\n </div>\n\n <div\n class=\"p-t-8\"\n *ngIf=\"(isSearch && loading) || searchInProgress\"\n >\n <c8y-loading></c8y-loading>\n </div>\n\n <div\n class=\"inner-scroll\"\n *ngIf=\"isSearch && !loading && !searchInProgress\"\n >\n <div\n class=\"list-group list-group-links\"\n *ngIf=\"isSearch && !loading\"\n >\n <button\n *ngFor=\"let nodeItem of nodeList\"\n (click)=\"selectNode(nodeItem)\"\n [ngClass]=\"{ 'list-group-item d-flex': true }\"\n >\n <div class=\"list-group-icon m-r-4\">\n <i\n class=\"m-r-4\"\n [c8yIcon]=\"getIcon(nodeItem.nodeClassName)\"\n ></i>\n </div>\n <div class=\"list-item-body text-truncate\">\n <span tile=\"nodeId\">{{ nodeItem.nodeId }}</span>\n {{ nodeItem.displayName }}\n </div>\n </button>\n </div>\n </div>\n <div\n class=\"inner-scroll\"\n *ngIf=\"!isSearch\"\n >\n <opcua-address-space-tree\n (selectedNode)=\"toggleCurrentNode($event)\"\n [focusEmitter]=\"focusStatus\"\n ></opcua-address-space-tree>\n </div>\n </div>\n </div>\n <opcua-address-space-detail\n class=\"col-md-7 col-xs-12 scroll-column no-gutter-l no-pointer\"\n [node]=\"currentNode\"\n (toggleAttrDetail)=\"backHandler($event)\"\n ></opcua-address-space-detail>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.AddressSpaceService }, { type: i2.OpcuaService }], propDecorators: { focusStatus: [{
type: Output
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BjdWEtYWRkcmVzcy1zcGFjZS5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9wcm90b2NvbC1vcGN1YS9vcGN1YS1hZGRyZXNzLXNwYWNlLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uL3Byb3RvY29sLW9wY3VhL29wY3VhLWFkZHJlc3Mtc3BhY2UuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBVSxNQUFNLEVBQUUsWUFBWSxFQUFhLE1BQU0sZUFBZSxDQUFDO0FBQ25GLE9BQU8sRUFBRSxtQkFBbUIsRUFBb0IsTUFBTSx5QkFBeUIsQ0FBQztBQUNoRixPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFFOUMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGdCQUFnQixDQUFDOzs7Ozs7Ozs7QUFNOUMsTUFBTSxPQUFPLDBCQUEwQjtJQWFyQyxZQUNVLG1CQUF3QyxFQUN4QyxZQUEwQjtRQUQxQix3QkFBbUIsR0FBbkIsbUJBQW1CLENBQXFCO1FBQ3hDLGlCQUFZLEdBQVosWUFBWSxDQUFjO1FBYnBDLGlCQUFZLEdBQUcsS0FBSyxDQUFDO1FBR3JCLFlBQU8sR0FBRyxLQUFLLENBQUM7UUFDaEIscUJBQWdCLEdBQUcsS0FBSyxDQUFDO1FBS2YsZ0JBQVcsR0FBbUMsSUFBSSxZQUFZLEVBQW9CLENBQUM7UUFDckYsU0FBSSxHQUFHLEVBQUUsQ0FBQztJQUlmLENBQUM7SUFFSixLQUFLLENBQUMsUUFBUTtRQUNaLElBQUksQ0FBQyxXQUFXLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQ3RDLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUMxQyxDQUFDO0lBRUQsV0FBVztRQUNULDJGQUEyRjtRQUMzRiwrRkFBK0Y7UUFDL0Ysa0dBQWtHO1FBQ2xHLDZGQUE2RjtRQUM3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QixNQUFNIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBPbkluaXQsIE91dHB1dCwgRXZlbnRFbWl0dGVyLCBPbkRlc3Ryb3kgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEFkZHJlc3NTcGFjZVNlcnZpY2UsIEFkZHJlc3NTcGFjZU5vZGUgfSBmcm9tICcuL2FkZHJlc3Mtc3BhY2Uuc2VydmljZSc7XG5pbXBvcnQgeyBnZXR0ZXh0IH0gZnJvbSAnQGM4eS9uZ3gtY29tcG9uZW50cyc7XG5pbXBvcnQgeyBEeW5hbWljRGF0YVNvdXJjZSB9IGZyb20gJy4vZHluYW1pYy1kYXRhLXNvdXJjZSc7XG5pbXBvcnQgeyBPcGN1YVNlcnZpY2UgfSBmcm9tICcuL29wY3VhU2VydmljZSc7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ29wY3VhLWFkZHJlc3Mtc3BhY2UnLFxuICB0ZW1wbGF0ZVVybDogJy4vb3BjdWEtYWRkcmVzcy1zcGFjZS5jb21wb25lbnQuaHRtbCdcbn0pXG5leHBvcnQgY2xhc3MgT3BjdWFBZGRyZXNzU3BhY2VDb21wb25lbnQgaW1wbGVtZW50cyBPbkluaXQsIE9uRGVzdHJveSB7XG4gIGN1cnJlbnROb2RlOiBBZGRyZXNzU3BhY2VOb2RlO1xuICBzZWxlY3RlZG5vZGUgPSBmYWxzZTtcbiAgc2VhcmNoS2V5OiBzdHJpbmc7XG4gIGlzU2VhcmNoOiBib29sZWFuO1xuICBsb2FkaW5nID0gZmFsc2U7XG4gIHNlYXJjaEluUHJvZ3Jlc3MgPSBmYWxzZTtcbiAgZmlsdGVyTGFiZWw6IHN0cmluZztcbiAgZGF0YVNvdXJjZTogRHluYW1pY0RhdGFTb3VyY2U7XG4gIG5vZGVMaXN0O1xuXG4gIEBPdXRwdXQoKSBmb2N1c1N0YXR1czogRXZlbnRFbWl0dGVyPEFkZHJlc3NTcGFjZU5vZGU+ID0gbmV3IEV2ZW50RW1pdHRlcjxBZGRyZXNzU3BhY2VOb2RlPigpO1xuICBwcml2YXRlIG1vSWQgPSAnJztcbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSBhZGRyZXNzU3BhY2VTZXJ2aWNlOiBBZGRyZXNzU3BhY2VTZXJ2aWNlLFxuICAgIHByaXZhdGUgb3BjdWFTZXJ2aWNlOiBPcGN1YVNlcnZpY2VcbiAgKSB7fVxuXG4gIGFzeW5jIG5nT25Jbml0KCkge1xuICAgIHRoaXMuZmlsdGVyTGFiZWwgPSBnZXR0ZXh0KCdGaWx0ZXLigKYnKTtcbiAgICB0aGlzLm1vSWQgPSB0aGlzLm9wY3VhU2VydmljZS5nZXRNb0lkKCk7XG4gIH1cblxuICBuZ09uRGVzdHJveSgpIHtcbiAgICAvLyBUaGUgQmVoYXZpb3VyU3ViamVjdCB3aWxsIHN0b3JlIHRoZSBsYXN0IGFycmF5IG9mIGFuY2VzdG9yTm9kZXMgZnJvbSB0aGUgcHJldmlvdXMgc2VhcmNoXG4gICAgLy8gdGhpcyB3b3VsZCBjYXVzZSB0aGUgY29tcG9uZW50IHdoaWxlIHN1YnNjcmliaW5nIGluIHRoZSBpbml0LXBoYXNlIHRvIHRoZSBzdWJqZWN0IHRvIHRyYXZlcnNcbiAgICAvLyB0byB0aGUgbGFzdCBzZWFyY2hlZCBub2RlIGFnYWluLiBGcm9tIHVzZXIgcGVyc3BlY3RpdmUgaXQgZG9lcyBub3QgbWFrZSBzZW5zZSwgYmVjYXVzZSB0aGUgdXNlclxuICAgIC8vIGxlZnQgdGhlIEFkZHJlc3Mgc3BhY2UgKHRhYikgYW5kIHNob3VsZCBsb29zZSB0aGUgY29udGV4dCBhbmQganVzdCByZXF1ZXN0IGEgbmV3IHNlYXJjaCBvclxuICAgIC8vIGJyb3dzZSB0aGUgdHJlZSBtYW51YWxseS5cbiAgICB0aGlzLmFkZHJlc3NTcGFjZVNlcnZpY2UucmVzZXRUcmVlVG9Sb290Tm9kZSgpO1xuICB9XG5cbiAgYXN5bmMgc2VhcmNoTm9kZXMoKSB7XG4gICAgdGhpcy5zZWFyY2hJblByb2dyZXNzID0gdHJ1ZTtcbiAgICB0aGlzLmNsZWFyTm9kZUxpc3RBbmRDaGVja1NlYXJjaFN0cmluZygpO1xuICAgIGlmICh0aGlzLmlzU2VhcmNoKSB7XG4gICAgICB0aGlzLmN1cnJlbnROb2RlID0gdW5kZWZpbmVkO1xuICAgICAgdGhpcy5ub2RlTGlzdCA9IGF3YWl0IHRoaXMuYWRkcmVzc1NwYWNlU2VydmljZS5nZXRTZWFyY2hlZE5vZGVzKHRoaXMuc2VhcmNoS2V5LCB0aGlzLm1vSWQpO1xuICAgICAgdGhpcy5zZWFyY2hJblByb2dyZXNzID0gZmFsc2U7XG4gICAgICB0aGlzLm5vZGVMaXN0LnJlc3VsdExhYmVsID0gZ2V0dGV4dCgnUmVzdWx0cyBmb3VuZCcpO1xuICAgIH1cbiAgfVxuXG4gIGNsZWFyTm9kZUxpc3RBbmRDaGVja1NlYXJjaFN0cmluZygpIHtcbiAgICB0aGlzLmlzU2VhcmNoID0gdGhpcy5zZWFyY2hLZXkgIT09IHVuZGVmaW5lZCAmJiB0aGlzLnNlYXJjaEtleSAhPT0gJycgPyB0cnVlIDogZmFsc2U7XG4gICAgaWYgKCF0aGlzLmlzU2VhcmNoKSB7XG4gICAgICB0aGlzLnNlYXJjaEluUHJvZ3Jlc3MgPSBmYWxzZTtcbiAgICB9XG4gIH1cblxuICBjbGVhclNlYXJjaCgpIHtcbiAgICB0aGlzLmlzU2VhcmNoID0gZmFsc2U7XG4gICAgdGhpcy5zZWFyY2hLZXkgPSAnJztcbiAgICB0aGlzLmN1cnJlbnROb2RlID0gdW5kZWZpbmVkO1xuICB9XG5cbiAgZ2V0SWNvbihub2RlQ2xhc3NOYW1lOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gdGhpcy5hZGRyZXNzU3BhY2VTZXJ2aWNlLmdldEljb24obm9kZUNsYXNzTmFtZSk7XG4gIH1cblxuICBhc3luYyBzZWxlY3ROb2RlKG5vZGUpIHtcbiAgICBpZiAobm9kZSAmJiBub2RlLm5vZGVJZCAmJiBub2RlLm5vZGVJZC5sZW5ndGggPiAwKSB7XG4gICAgICBjb25zdCByZXMgPSBhd2FpdCB0aGlzLmFkZHJlc3NTcGFjZVNlcnZpY2UuZ2V0Tm9kZUJ5SWQodGhpcy5tb0lkLCBub2RlLm5vZGVJZCk7XG4gICAgICB0aGlzLnRvZ2dsZUN1cnJlbnROb2RlKChhd2FpdCByZXMuanNvbigpKSBhcyBBZGRyZXNzU3BhY2VOb2RlKTtcbiAgICB9XG4gIH1cblxuICB0b2dnbGVDdXJyZW50Tm9kZShub2RlOiBBZGRyZXNzU3BhY2VOb2RlKSB7XG4gICAgdGhpcy5jdXJyZW50Tm9kZSA9IHRoaXMuaXNOb2RlU2V0KG5vZGUpID8gdW5kZWZpbmVkIDogbm9kZTtcbiAgfVxuXG4gIGJhY2tIYW5kbGVyKG5vZGUpIHtcbiAgICB0aGlzLmlzU2VhcmNoID0gZmFsc2U7XG4gICAgdGhpcy5mb2N1c1N0YXR1cy5lbWl0KG5vZGUpO1xuICAgIHRoaXMudG9nZ2xlQ3VycmVudE5vZGUobm9kZSk7XG4gIH1cblxuICBpc05vZGVTZXQobm9kZTogQWRkcmVzc1NwYWNlTm9kZSkge1xuICAgIGlmICh0aGlzLmN1cnJlbnROb2RlICE9PSB1bmRlZmluZWQgJiYgdGhpcy5jdXJyZW50Tm9kZS5ub2RlSWQgPT09IG5vZGUubm9kZUlkKSB7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG59XG4iLCI8ZGl2IGNsYXNzPVwicm93IHNwbGl0LXNjcm9sbFwiPlxuICA8ZGl2IGNsYXNzPVwiY29sLW1kLTUgY29sLXhzLTEyIHNjcm9sbC1jb2x1bW4gbm8tZ3V0dGVyLXJcIj5cbiAgICA8ZGl2IGNsYXNzPVwiY2FyZCBiZy1sZXZlbC0yIHNwbGl0LXNjcm9sbCBvdmVyZmxvdy1hdXRvXCI+XG4gICAgICA8ZGl2IGNsYXNzPVwiY2FyZC1ibG9jayBzZXBhcmF0b3ItYm90dG9tIHN0aWNreS10b3BcIj5cbiAgICAgICAgPGRpdiBjbGFzcz1cImlucHV0LWdyb3VwIGlucHV0LWdyb3VwLXNlYXJjaFwiPlxuICAgICAgICAgIDxpbnB1dFxuICAgICAgICAgICAgY2xhc3M9XCJmb3JtLWNvbnRyb2xcIlxuICAgICAgICAgICAgcGxhY2Vob2xkZXI9XCJ7eyBmaWx0ZXJMYWJlbCB8IHRyYW5zbGF0ZSB9fVwiXG4gICAgICAgICAgICB0eXBlPVwic2VhcmNoXCJcbiAgICAgICAgICAgIChrZXlkb3duLmVudGVyKT1cInNlYXJjaE5vZGVzKClcIlxuICAgICAgICAgICAgWyhuZ01vZGVsKV09XCJzZWFyY2hLZXlcIlxuICAgICAgICAgIC8+XG4gICAgICAgICAgPHNwYW4gY2xhc3M9XCJpbnB1dC1ncm91cC1idG5cIj5cbiAgICAgICAgICAgIDxidXR0b25cbiAgICAgICAgICAgICAgY2xhc3M9XCJidG4gYnRuLWRvdFwiXG4gICAgICAgICAgICAgIHRpdGxlPVwie3sgJ1NlYXJjaCcgfCB0cmFuc2xhdGUgfX1cIlxuICAgICAgICAgICAgICB0eXBlPVwic3VibWl0XCJcbiAgICAgICAgICAgICAgKm5nSWY9XCIhaXNTZWFyY2hcIlxuICAgICAgICAgICAgICAoY2xpY2spPVwic2VhcmNoTm9kZXMoKVwiXG4gICAgICAgICAgICA+XG4gICAgICAgICAgICAgIDxpIGM4eUljb249XCJzZWFyY2hcIj48L2k+XG4gICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgIDxidXR0b25cbiAgICAgICAgICAgICAgY2xhc3M9XCJidG4gYnRuLWRvdFwiXG4gICAgICAgICAgICAgIHRpdGxlPVwie3sgJ0NsZWFyYGlucHV0YCcgfCB0cmFuc2xhdGUgfX1cIlxuICAgICAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAgICAgKm5nSWY9XCJpc1NlYXJjaFwiXG4gICAgICAgICAgICAgIChjbGljayk9XCJjbGVhclNlYXJjaCgpXCJcbiAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgPGkgYzh5SWNvbj1cInRpbWVzXCI+PC9pPlxuICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgPC9zcGFuPlxuICAgICAgICA8L2Rpdj5cbiAgICAgICAgPGRpdlxuICAgICAgICAgIGNsYXNzPVwicC10LTE2XCJcbiAgICAgICAgICAqbmdJZj1cImlzU2VhcmNoICYmICFsb2FkaW5nXCJcbiAgICAgICAgPlxuICAgICAgICAgIDxwICpuZ0lmPVwiIXNlYXJjaEluUHJvZ3Jlc3NcIj5cbiAgICAgICAgICAgIDxlbT57eyBub2RlTGlzdC5yZXN1bHRMYWJlbCB8IHRyYW5zbGF0ZSB9fTwvZW0+XG4gICAgICAgICAgICAmbmJzcDtcbiAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwiYmFkZ2UgYmFkZ2UtaW5mb1wiPnt7IG5vZGVMaXN0Py5sZW5ndGggfX08L3NwYW4+XG4gICAgICAgICAgPC9wPlxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvZGl2PlxuXG4gICAgICA8ZGl2XG4gICAgICAgIGNsYXNzPVwicC10LThcIlxuICAgICAgICAqbmdJZj1cIihpc1NlYXJjaCAmJiBsb2FkaW5nKSB8fCBzZWFyY2hJblByb2dyZXNzXCJcbiAgICAgID5cbiAgICAgICAgPGM4eS1sb2FkaW5nPjwvYzh5LWxvYWRpbmc+XG4gICAgICA8L2Rpdj5cblxuICAgICAgPGRpdlxuICAgICAgICBjbGFzcz1cImlubmVyLXNjcm9sbFwiXG4gICAgICAgICpuZ0lmPVwiaXNTZWFyY2ggJiYgIWxvYWRpbmcgJiYgIXNlYXJjaEluUHJvZ3Jlc3NcIlxuICAgICAgPlxuICAgICAgICA8ZGl2XG4gICAgICAgICAgY2xhc3M9XCJsaXN0LWdyb3VwIGxpc3QtZ3JvdXAtbGlua3NcIlxuICAgICAgICAgICpuZ0lmPVwiaXNTZWFyY2ggJiYgIWxvYWRpbmdcIlxuICAgICAgICA+XG4gICAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICAgKm5nRm9yPVwibGV0IG5vZGVJdGVtIG9mIG5vZGVMaXN0XCJcbiAgICAgICAgICAgIChjbGljayk9XCJzZWxlY3ROb2RlKG5vZGVJdGVtKVwiXG4gICAgICAgICAgICBbbmdDbGFzc109XCJ7ICdsaXN0LWdyb3VwLWl0ZW0gZC1mbGV4JzogdHJ1ZSB9XCJcbiAgICAgICAgICA+XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwibGlzdC1ncm91cC1pY29uIG0tci00XCI+XG4gICAgICAgICAgICAgIDxpXG4gICAgICAgICAgICAgICAgY2xhc3M9XCJtLXItNFwiXG4gICAgICAgICAgICAgICAgW2M4eUljb25dPVwiZ2V0SWNvbihub2RlSXRlbS5ub2RlQ2xhc3NOYW1lKVwiXG4gICAgICAgICAgICAgID48L2k+XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJsaXN0LWl0ZW0tYm9keSB0ZXh0LXRydW5jYXRlXCI+XG4gICAgICAgICAgICAgIDxzcGFuIHRpbGU9XCJub2RlSWRcIj57eyBub2RlSXRlbS5ub2RlSWQgfX08L3NwYW4+XG4gICAgICAgICAgICAgIHt7IG5vZGVJdGVtLmRpc3BsYXlOYW1lIH19XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L2Rpdj5cbiAgICAgIDxkaXZcbiAgICAgICAgY2xhc3M9XCJpbm5lci1zY3JvbGxcIlxuICAgICAgICAqbmdJZj1cIiFpc1NlYXJjaFwiXG4gICAgICA+XG4gICAgICAgIDxvcGN1YS1hZGRyZXNzLXNwYWNlLXRyZWVcbiAgICAgICAgICAoc2VsZWN0ZWROb2RlKT1cInRvZ2dsZUN1cnJlbnROb2RlKCRldmVudClcIlxuICAgICAgICAgIFtmb2N1c0VtaXR0ZXJdPVwiZm9jdXNTdGF0dXNcIlxuICAgICAgICA+PC9vcGN1YS1hZGRyZXNzLXNwYWNlLXRyZWU+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cbiAgPC9kaXY+XG4gIDxvcGN1YS1hZGRyZXNzLXNwYWNlLWRldGFpbFxuICAgIGNsYXNzPVwiY29sLW1kLTcgY29sLXhzLTEyIHNjcm9sbC1jb2x1bW4gbm8tZ3V0dGVyLWwgbm8tcG9pbnRlclwiXG4gICAgW25vZGVdPVwiY3VycmVudE5vZGVcIlxuICAgICh0b2dnbGVBdHRyRGV0YWlsKT1cImJhY2tIYW5kbGVyKCRldmVudClcIlxuICA+PC9vcGN1YS1hZGRyZXNzLXNwYWNlLWRldGFpbD5cbjwvZGl2PlxuIl19