UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

151 lines 45.9 kB
import { Component, Input, EventEmitter } from '@angular/core'; import { InventoryService } from '@c8y/client'; import { gettext } from '@c8y/ngx-components'; import { reject } from 'lodash-es'; import * as i0 from "@angular/core"; import * as i1 from "@c8y/client"; 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/collapse"; export class OpcuaAutoApplySettingsComponent { constructor(inventoryService) { this.inventoryService = inventoryService; this.opcuaServers = []; this.selectedItems = []; this.filteredList = []; this.checked = {}; this.sizeToShowFilter = 5; this.constraints = { browsePathMatchesRegex: '', matchesNodeIds: [], serverObjectHasFragment: '', matchesServerIds: [] }; this.placeholderSelectServerIds = gettext('Select server IDs from list'); this.updateSelectedItem = new EventEmitter(); } async ngOnInit() { const { data } = await this.inventoryService.list({ pageSize: 1000, withTotalPages: true, type: 'c8y_OpcuaServer' }); this.opcuaServers = data; this.filteredList = data; this.selectedItems = []; const { matchesServerIds } = this.constraints; data.forEach(server => { if (matchesServerIds && matchesServerIds.length > 0 && matchesServerIds.find(itemId => itemId === server.id)) { this.selectedItems.push(server); this.checked[server.id] = true; this.showServerIds = true; } }); } set model(model) { if (model && model.applyConstraints) { this.constraints = model.applyConstraints; } this._model = model; } get model() { return this._model; } serverIdsSelected(items) { if (this.constraints) { this.constraints.matchesServerIds = items.map((item) => item.id); } this.selectedItems = items; } onChangeNodeId(event) { if (event.target.checked) { this.showRootNodes = true; this.add(); } else { this.showRootNodes = false; this.constraints.matchesNodeIds = []; } } onChangeShowServerIds(event) { if (!event.target.checked) { this.constraints.matchesServerIds = []; this.showServerIds = false; this.selectedItems = []; this.checked = {}; } else { this.showServerIds = true; } } onChangeShowBrowsePath(event) { if (!event.target.checked) { this.constraints.browsePathMatchesRegex = ''; this.showBrowsePath = false; } else { this.showBrowsePath = true; } } onChangeShowServerFragment(event) { if (!event.target.checked) { this.constraints.serverObjectHasFragment = ''; this.showServerFragment = false; } else { this.showServerFragment = true; } } add() { this.constraints.matchesNodeIds.push(''); } remove(index) { this.constraints.matchesNodeIds.splice(index, 1); } trackByFn(index, _item) { return index; } updateConstraints(items) { if (this.constraints) { this.constraints.matchesServerIds = items.map((item) => item.id); } } filterItems(filterText) { if (filterText.length !== 0) { const search = new RegExp(filterText, 'i'); this.filteredList = this.opcuaServers.filter(({ name, id }) => { return search.test(name) || search.test(id); }); } else { this.filteredList = this.opcuaServers; } } isChecked(item) { return this.checked[item.id]; } onSelect(selected, item) { if (!selected) { this.selectedItems = reject(this.selectedItems, { id: item.id }); delete this.checked[item.id]; } else { this.selectedItems.push(item); this.checked[item.id] = selected; } this.updateSelectedItem.emit(true); this.updateConstraints(this.selectedItems); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: OpcuaAutoApplySettingsComponent, deps: [{ token: i1.InventoryService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: OpcuaAutoApplySettingsComponent, selector: "opcua-auto-apply", inputs: { model: "model" }, ngImport: i0, template: "<div class=\"row\">\n <div class=\"col-md-4\">\n <p translate>\n Specifying auto-apply constraints allows you to limit the scope where the device protocols are\n applied, for example by specifying a set of possible servers or node IDs.\n </p>\n <p translate>\n If no constraints are set, device protocols are applied at any fitting location on the OPC UA\n server.\n </p>\n </div>\n\n <div class=\"col-md-6\">\n <ul class=\"list-group\" style=\"box-shadow: none;\">\n <!-- Limit device type to a set of servers -->\n <li class=\"list-group-item\">\n <label\n title=\"{{ 'Limit device protocol to a set of servers' | translate }}\"\n class=\"c8y-checkbox\"\n >\n <input\n type=\"checkbox\"\n [checked]=\"\n constraints.matchesServerIds !== null && constraints.matchesServerIds.length > 0\n \"\n (change)=\"onChangeShowServerIds($event)\"\n />\n <span></span>\n <span class=\"m-l-8\">\n {{ 'Limit device protocol to a set of servers' | translate }}\n </span>\n </label>\n <div\n class=\"collapse\"\n [collapse]=\"\n (!showServerIds &&\n (constraints.matchesServerIds !== null && constraints.matchesServerIds.length < 1)) ||\n (!showServerIds && constraints.matchesServerIds === null)\n \"\n [isAnimated]=\"true\"\n >\n <c8y-form-group class=\"m-t-8 m-b-8\">\n <c8y-select-legacy\n [items]=\"opcuaServers\"\n [selected]=\"selectedItems\"\n [placeholder]=\"placeholderSelectServerIds\"\n (onChange)=\"serverIdsSelected($event)\"\n [updateItems]=\"updateSelectedItem\"\n >\n \n <c8y-li *ngIf=\"opcuaServers.length > sizeToShowFilter\" class=\"sticky-top\">\n <c8y-filter [icon]=\"'search'\" (onSearch)=\"filterItems($event)\"></c8y-filter>\n </c8y-li>\n <c8y-li *ngIf=\"filteredList && filteredList.length === 0\">\n <c8y-li-body> \n <div class=\"c8y-empty-state text-left\">\n <p>{{ 'No items to display.' | translate }}</p>\n </div>\n </c8y-li-body>\n </c8y-li>\n <c8y-li *ngFor=\"let item of filteredList\">\n <c8y-li-checkbox\n [selected]=\"isChecked(item)\"\n (onSelect)=\"onSelect($event, item)\"\n ></c8y-li-checkbox>\n <c8y-li-body>{{ item.name | translate }}<br>\n <span class=\"text-label-small m-r-4\" translate>ID</span> <small>{{ item.id }}</small>\n </c8y-li-body>\n </c8y-li>\n </c8y-select-legacy>\n </c8y-form-group>\n </div>\n </li>\n <!-- Limit device type scope in the address space -->\n <li class=\"list-group-item\">\n <label\n title=\"{{ 'Limit device protocol scope in the address space' | translate }}\"\n class=\"c8y-checkbox\"\n >\n <input\n type=\"checkbox\"\n [checked]=\"\n constraints.browsePathMatchesRegex !== null &&\n constraints.browsePathMatchesRegex.length > 0\n \"\n (change)=\"onChangeShowBrowsePath($event)\"\n />\n <span></span>\n <span class=\"m-l-8\">\n {{ 'Limit device protocol scope in the address space' | translate }}\n </span>\n </label>\n <div\n class=\"collapse\"\n [isAnimated]=\"true\"\n [collapse]=\"\n (!showBrowsePath &&\n constraints.browsePathMatchesRegex !== null &&\n constraints.browsePathMatchesRegex.length < 1) ||\n (!showBrowsePath && constraints.browsePathMatchesRegex === null)\n \"\n >\n <c8y-form-group class=\"m-t-8 m-b-8\">\n <input\n name=\"browsePath\"\n type=\"text\"\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} /objects/devices/.*\"\n [(ngModel)]=\"constraints.browsePathMatchesRegex\"\n ngDefaultControl\n />\n </c8y-form-group>\n </div>\n </li>\n <!-- Limit device type to servers with a certain fragment-->\n <li class=\"list-group-item\">\n <label\n title=\"{{ 'Limit device protocol to servers with a certain fragment' | translate }}\"\n class=\"c8y-checkbox\"\n >\n <input\n type=\"checkbox\"\n [checked]=\"constraints.serverObjectHasFragment !== null && constraints.serverObjectHasFragment.length > 0\"\n (change)=\"onChangeShowServerFragment($event)\"\n />\n <span></span>\n <span class=\"m-l-8\">\n {{ 'Limit device protocol to servers with a certain fragment' | translate }}\n </span>\n </label>\n <div\n class=\"collapse\"\n [isAnimated]=\"true\"\n [collapse]=\"(!showServerFragment && constraints.serverObjectHasFragment !== null && constraints.serverObjectHasFragment.length < 1) || (!showServerFragment && constraints.serverObjectHasFragment === null)\"\n >\n <c8y-form-group class=\"m-t-8 m-b-8\">\n <input\n name=\"serverFragment\"\n type=\"text\"\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} c8y_SomeServerMarker\"\n [(ngModel)]=\"constraints.serverObjectHasFragment\"\n ngDefaultControl\n />\n </c8y-form-group>\n </div>\n </li>\n <!-- Limit device type to a specific root node ID -->\n <li class=\"list-group-item\">\n <label\n title=\"{{ 'Limit device protocol to specific root nodes ID' | translate }}\"\n class=\"c8y-checkbox\"\n >\n <input\n type=\"checkbox\"\n [checked]=\"constraints.matchesNodeIds !== null && constraints.matchesNodeIds.length > 0\"\n (change)=\"onChangeNodeId($event)\"\n />\n <span></span>\n <span class=\"m-l-8\">\n {{ 'Limit device protocol to specific root nodes ID' | translate }}\n </span>\n </label>\n <div\n class=\"collapse\"\n [isAnimated]=\"true\"\n [collapse]=\"(!showRootNodes && constraints.matchesNodeIds !== null && constraints.matchesNodeIds.length < 1) || ( !showRootNodes && constraints.matchesNodeIds === null)\"\n >\n <ul c8yInputGroupListContainer class=\"list-unstyled p-t-16\">\n <li\n class=\"m-b-8\"\n *ngFor=\"let item of constraints.matchesNodeIds; let i = index; trackBy: trackByFn\"\n >\n <c8y-input-group-list [index]=\"i\" (onAdd)=\"add()\" (onRemove)=\"remove($event)\">\n <c8y-form-group class=\"form-group--tooltip-validation\">\n <input\n type=\"text\"\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} nodeId\"\n [(ngModel)]=\"constraints.matchesNodeIds[i]\"\n [required]=\"true\"\n />\n </c8y-form-group>\n </c8y-input-group-list>\n </li>\n </ul> \n </div>\n </li>\n </ul>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i2.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i2.SelectLegacyComponent, selector: "c8y-select-legacy", inputs: ["placeholder", "selectedLabel", "applyLabel", "items", "selected", "updateItems", "disableApplyOnNoSelection", "addDropdownContainerToBody"], outputs: ["onChange"] }, { 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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { 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: "component", type: i2.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "component", type: i2.InputGroupListComponent, selector: "c8y-input-group-list", inputs: ["plus", "minus", "index"], outputs: ["onRemove", "onAdd"] }, { kind: "directive", type: i2.InputGroupListContainerDirective, selector: "[c8yInputGroupListContainer]" }, { kind: "directive", type: i2.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "component", type: i2.FilterInputComponent, selector: "c8y-filter", inputs: ["icon", "filterTerm"], outputs: ["onSearch"] }, { kind: "component", type: i2.ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "component", type: i2.ListItemBodyComponent, selector: "c8y-list-item-body, c8y-li-body", inputs: ["body"] }, { kind: "component", type: i2.ListItemCheckboxComponent, selector: "c8y-list-item-checkbox, c8y-li-checkbox", inputs: ["selected", "indeterminate", "disabled", "displayAsSwitch"], outputs: ["onSelect"] }, { kind: "directive", type: i5.CollapseDirective, selector: "[collapse]", inputs: ["display", "isAnimated", "collapse"], outputs: ["collapsed", "collapses", "expanded", "expands"], exportAs: ["bs-collapse"] }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: OpcuaAutoApplySettingsComponent, decorators: [{ type: Component, args: [{ selector: 'opcua-auto-apply', template: "<div class=\"row\">\n <div class=\"col-md-4\">\n <p translate>\n Specifying auto-apply constraints allows you to limit the scope where the device protocols are\n applied, for example by specifying a set of possible servers or node IDs.\n </p>\n <p translate>\n If no constraints are set, device protocols are applied at any fitting location on the OPC UA\n server.\n </p>\n </div>\n\n <div class=\"col-md-6\">\n <ul class=\"list-group\" style=\"box-shadow: none;\">\n <!-- Limit device type to a set of servers -->\n <li class=\"list-group-item\">\n <label\n title=\"{{ 'Limit device protocol to a set of servers' | translate }}\"\n class=\"c8y-checkbox\"\n >\n <input\n type=\"checkbox\"\n [checked]=\"\n constraints.matchesServerIds !== null && constraints.matchesServerIds.length > 0\n \"\n (change)=\"onChangeShowServerIds($event)\"\n />\n <span></span>\n <span class=\"m-l-8\">\n {{ 'Limit device protocol to a set of servers' | translate }}\n </span>\n </label>\n <div\n class=\"collapse\"\n [collapse]=\"\n (!showServerIds &&\n (constraints.matchesServerIds !== null && constraints.matchesServerIds.length < 1)) ||\n (!showServerIds && constraints.matchesServerIds === null)\n \"\n [isAnimated]=\"true\"\n >\n <c8y-form-group class=\"m-t-8 m-b-8\">\n <c8y-select-legacy\n [items]=\"opcuaServers\"\n [selected]=\"selectedItems\"\n [placeholder]=\"placeholderSelectServerIds\"\n (onChange)=\"serverIdsSelected($event)\"\n [updateItems]=\"updateSelectedItem\"\n >\n \n <c8y-li *ngIf=\"opcuaServers.length > sizeToShowFilter\" class=\"sticky-top\">\n <c8y-filter [icon]=\"'search'\" (onSearch)=\"filterItems($event)\"></c8y-filter>\n </c8y-li>\n <c8y-li *ngIf=\"filteredList && filteredList.length === 0\">\n <c8y-li-body> \n <div class=\"c8y-empty-state text-left\">\n <p>{{ 'No items to display.' | translate }}</p>\n </div>\n </c8y-li-body>\n </c8y-li>\n <c8y-li *ngFor=\"let item of filteredList\">\n <c8y-li-checkbox\n [selected]=\"isChecked(item)\"\n (onSelect)=\"onSelect($event, item)\"\n ></c8y-li-checkbox>\n <c8y-li-body>{{ item.name | translate }}<br>\n <span class=\"text-label-small m-r-4\" translate>ID</span> <small>{{ item.id }}</small>\n </c8y-li-body>\n </c8y-li>\n </c8y-select-legacy>\n </c8y-form-group>\n </div>\n </li>\n <!-- Limit device type scope in the address space -->\n <li class=\"list-group-item\">\n <label\n title=\"{{ 'Limit device protocol scope in the address space' | translate }}\"\n class=\"c8y-checkbox\"\n >\n <input\n type=\"checkbox\"\n [checked]=\"\n constraints.browsePathMatchesRegex !== null &&\n constraints.browsePathMatchesRegex.length > 0\n \"\n (change)=\"onChangeShowBrowsePath($event)\"\n />\n <span></span>\n <span class=\"m-l-8\">\n {{ 'Limit device protocol scope in the address space' | translate }}\n </span>\n </label>\n <div\n class=\"collapse\"\n [isAnimated]=\"true\"\n [collapse]=\"\n (!showBrowsePath &&\n constraints.browsePathMatchesRegex !== null &&\n constraints.browsePathMatchesRegex.length < 1) ||\n (!showBrowsePath && constraints.browsePathMatchesRegex === null)\n \"\n >\n <c8y-form-group class=\"m-t-8 m-b-8\">\n <input\n name=\"browsePath\"\n type=\"text\"\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} /objects/devices/.*\"\n [(ngModel)]=\"constraints.browsePathMatchesRegex\"\n ngDefaultControl\n />\n </c8y-form-group>\n </div>\n </li>\n <!-- Limit device type to servers with a certain fragment-->\n <li class=\"list-group-item\">\n <label\n title=\"{{ 'Limit device protocol to servers with a certain fragment' | translate }}\"\n class=\"c8y-checkbox\"\n >\n <input\n type=\"checkbox\"\n [checked]=\"constraints.serverObjectHasFragment !== null && constraints.serverObjectHasFragment.length > 0\"\n (change)=\"onChangeShowServerFragment($event)\"\n />\n <span></span>\n <span class=\"m-l-8\">\n {{ 'Limit device protocol to servers with a certain fragment' | translate }}\n </span>\n </label>\n <div\n class=\"collapse\"\n [isAnimated]=\"true\"\n [collapse]=\"(!showServerFragment && constraints.serverObjectHasFragment !== null && constraints.serverObjectHasFragment.length < 1) || (!showServerFragment && constraints.serverObjectHasFragment === null)\"\n >\n <c8y-form-group class=\"m-t-8 m-b-8\">\n <input\n name=\"serverFragment\"\n type=\"text\"\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} c8y_SomeServerMarker\"\n [(ngModel)]=\"constraints.serverObjectHasFragment\"\n ngDefaultControl\n />\n </c8y-form-group>\n </div>\n </li>\n <!-- Limit device type to a specific root node ID -->\n <li class=\"list-group-item\">\n <label\n title=\"{{ 'Limit device protocol to specific root nodes ID' | translate }}\"\n class=\"c8y-checkbox\"\n >\n <input\n type=\"checkbox\"\n [checked]=\"constraints.matchesNodeIds !== null && constraints.matchesNodeIds.length > 0\"\n (change)=\"onChangeNodeId($event)\"\n />\n <span></span>\n <span class=\"m-l-8\">\n {{ 'Limit device protocol to specific root nodes ID' | translate }}\n </span>\n </label>\n <div\n class=\"collapse\"\n [isAnimated]=\"true\"\n [collapse]=\"(!showRootNodes && constraints.matchesNodeIds !== null && constraints.matchesNodeIds.length < 1) || ( !showRootNodes && constraints.matchesNodeIds === null)\"\n >\n <ul c8yInputGroupListContainer class=\"list-unstyled p-t-16\">\n <li\n class=\"m-b-8\"\n *ngFor=\"let item of constraints.matchesNodeIds; let i = index; trackBy: trackByFn\"\n >\n <c8y-input-group-list [index]=\"i\" (onAdd)=\"add()\" (onRemove)=\"remove($event)\">\n <c8y-form-group class=\"form-group--tooltip-validation\">\n <input\n type=\"text\"\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} nodeId\"\n [(ngModel)]=\"constraints.matchesNodeIds[i]\"\n [required]=\"true\"\n />\n </c8y-form-group>\n </c8y-input-group-list>\n </li>\n </ul> \n </div>\n </li>\n </ul>\n </div>\n</div>\n" }] }], ctorParameters: () => [{ type: i1.InventoryService }], propDecorators: { model: [{ type: Input }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BjdWEtYXV0by1hcHBseS1zZXR0aW5ncy5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9wcm90b2NvbC1vcGN1YS9vcGN1YS1hdXRvLWFwcGx5LXNldHRpbmdzLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uL3Byb3RvY29sLW9wY3VhL29wY3VhLWF1dG8tYXBwbHktc2V0dGluZ3MuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQVUsWUFBWSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRXZFLE9BQU8sRUFBa0IsZ0JBQWdCLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDL0QsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQzlDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxXQUFXLENBQUM7Ozs7Ozs7QUFLbkMsTUFBTSxPQUFPLCtCQUErQjtJQW9CMUMsWUFBb0IsZ0JBQWtDO1FBQWxDLHFCQUFnQixHQUFoQixnQkFBZ0IsQ0FBa0I7UUFuQnRELGlCQUFZLEdBQXFCLEVBQUUsQ0FBQztRQUNwQyxrQkFBYSxHQUFxQixFQUFFLENBQUM7UUFDckMsaUJBQVksR0FBcUIsRUFBRSxDQUFDO1FBQ3BDLFlBQU8sR0FBRyxFQUFFLENBQUM7UUFDSixxQkFBZ0IsR0FBVyxDQUFDLENBQUM7UUFDdEMsZ0JBQVcsR0FBeUI7WUFDbEMsc0JBQXNCLEVBQUUsRUFBRTtZQUMxQixjQUFjLEVBQUUsRUFBRTtZQUNsQix1QkFBdUIsRUFBRSxFQUFFO1lBQzNCLGdCQUFnQixFQUFFLEVBQUU7U0FDckIsQ0FBQztRQUNGLCtCQUEwQixHQUFXLE9BQU8sQ0FBQyw2QkFBNkIsQ0FBQyxDQUFDO1FBSzVFLHVCQUFrQixHQUEwQixJQUFJLFlBQVksRUFBRSxDQUFDO0lBR04sQ0FBQztJQUUxRCxLQUFLLENBQUMsUUFBUTtRQUNaLE1BQU0sRUFBRSxJ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QStMQTs7NEZEdExhLCtCQUErQjtrQkFKM0MsU0FBUzsrQkFDRSxrQkFBa0I7cUZBbURmLEtBQUs7c0JBQWpCLEtBQUsiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQsIElucHV0LCBPbkluaXQsIEV2ZW50RW1pdHRlciB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgQXV0b0FwcGx5Q29uc3RyYWludHMgfSBmcm9tICcuL29wY3VhLXByb3RvY29sLWRldmljZS10eXBlLmludGVyZmFjZSc7XG5pbXBvcnQgeyBJTWFuYWdlZE9iamVjdCwgSW52ZW50b3J5U2VydmljZSB9IGZyb20gJ0BjOHkvY2xpZW50JztcbmltcG9ydCB7IGdldHRleHQgfSBmcm9tICdAYzh5L25neC1jb21wb25lbnRzJztcbmltcG9ydCB7IHJlamVjdCB9IGZyb20gJ2xvZGFzaC1lcyc7XG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdvcGN1YS1hdXRvLWFwcGx5JyxcbiAgdGVtcGxhdGVVcmw6ICcuL29wY3VhLWF1dG8tYXBwbHktc2V0dGluZ3MuY29tcG9uZW50Lmh0bWwnXG59KVxuZXhwb3J0IGNsYXNzIE9wY3VhQXV0b0FwcGx5U2V0dGluZ3NDb21wb25lbnQgaW1wbGVtZW50cyBPbkluaXQge1xuICBvcGN1YVNlcnZlcnM6IElNYW5hZ2VkT2JqZWN0W10gPSBbXTtcbiAgc2VsZWN0ZWRJdGVtczogSU1hbmFnZWRPYmplY3RbXSA9IFtdO1xuICBmaWx0ZXJlZExpc3Q6IElNYW5hZ2VkT2JqZWN0W10gPSBbXTtcbiAgY2hlY2tlZCA9IHt9O1xuICByZWFkb25seSBzaXplVG9TaG93RmlsdGVyOiBudW1iZXIgPSA1O1xuICBjb25zdHJhaW50czogQXV0b0FwcGx5Q29uc3RyYWludHMgPSB7XG4gICAgYnJvd3NlUGF0aE1hdGNoZXNSZWdleDogJycsXG4gICAgbWF0Y2hlc05vZGVJZHM6IFtdLFxuICAgIHNlcnZlck9iamVjdEhhc0ZyYWdtZW50OiAnJyxcbiAgICBtYXRjaGVzU2VydmVySWRzOiBbXVxuICB9O1xuICBwbGFjZWhvbGRlclNlbGVjdFNlcnZlcklkczogc3RyaW5nID0gZ2V0dGV4dCgnU2VsZWN0IHNlcnZlciBJRHMgZnJvbSBsaXN0Jyk7XG4gIHNob3dTZXJ2ZXJJZHM6IGJvb2xlYW47XG4gIHNob3dCcm93c2VQYXRoOiBib29sZWFuO1xuICBzaG93U2VydmVyRnJhZ21lbnQ6IGJvb2xlYW47XG4gIHNob3dSb290Tm9kZXM6IGJvb2xlYW47XG4gIHVwZGF0ZVNlbGVjdGVkSXRlbTogRXZlbnRFbWl0dGVyPGJvb2xlYW4+ID0gbmV3IEV2ZW50RW1pdHRlcigpO1xuICBwcml2YXRlIF9tb2RlbDogSU1hbmFnZWRPYmplY3Q7XG5cbiAgY29uc3RydWN0b3IocHJpdmF0ZSBpbnZlbnRvcnlTZXJ2aWNlOiBJbnZlbnRvcnlTZXJ2aWNlKSB7fVxuXG4gIGFzeW5jIG5nT25Jbml0KCkge1xuICAgIGNvbnN0IHsgZGF0YSB9ID0gYXdhaXQgdGhpcy5pbnZlbnRvcnlTZXJ2aWNlLmxpc3Qoe1xuICAgICAgcGFnZVNpemU6IDEwMDAsXG4gICAgICB3aXRoVG90YWxQYWdlczogdHJ1ZSxcbiAgICAgIHR5cGU6ICdjOHlfT3BjdWFTZXJ2ZXInXG4gICAgfSk7XG5cbiAgICB0aGlzLm9wY3VhU2VydmVycyA9IGRhdGE7XG4gICAgdGhpcy5maWx0ZXJlZExpc3QgPSBkYXRhO1xuICAgIHRoaXMuc2VsZWN0ZWRJdGVtcyA9IFtdO1xuXG4gICAgY29uc3QgeyBtYXRjaGVzU2VydmVySWRzIH0gPSB0aGlzLmNvbnN0cmFpbnRzO1xuXG4gICAgZGF0YS5mb3JFYWNoKHNlcnZlciA9PiB7XG4gICAgICBpZiAoXG4gICAgICAgIG1hdGNoZXNTZXJ2ZXJJZHMgJiZcbiAgICAgICAgbWF0Y2hlc1NlcnZlcklkcy5sZW5ndGggPiAwICYmXG4gICAgICAgIG1hdGNoZXNTZXJ2ZXJJZHMuZmluZChpdGVtSWQgPT4gaXRlbUlkID09PSBzZXJ2ZXIuaWQpXG4gICAgICApIHtcbiAgICAgICAgdGhpcy5zZWxlY3RlZEl0ZW1zLnB1c2goc2VydmVyKTtcbiAgICAgICAgdGhpcy5jaGVja2VkW3NlcnZlci5pZF0gPSB0cnVlO1xuICAgICAgICB0aGlzLnNob3dTZXJ2ZXJJZHMgPSB0cnVlO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgQElucHV0KCkgc2V0IG1vZGVsKG1vZGVsKSB7XG4gICAgaWYgKG1vZGVsICYmIG1vZGVsLmFwcGx5Q29uc3RyYWludHMpIHtcbiAgICAgIHRoaXMuY29uc3RyYWludHMgPSBtb2RlbC5hcHBseUNvbnN0cmFpbnRzIGFzIEF1dG9BcHBseUNvbnN0cmFpbnRzO1xuICAgIH1cbiAgICB0aGlzLl9tb2RlbCA9IG1vZGVsO1xuICB9XG5cbiAgZ2V0IG1vZGVsKCkge1xuICAgIHJldHVybiB0aGlzLl9tb2RlbDtcbiAgfVxuXG4gIHNlcnZlcklkc1NlbGVjdGVkKGl0ZW1zOiBJTWFuYWdlZE9iamVjdFtdKSB7XG4gICAgaWYgKHRoaXMuY29uc3RyYWludHMpIHtcbiAgICAgIHRoaXMuY29uc3RyYWludHMubWF0Y2hlc1NlcnZlcklkcyA9IGl0ZW1zLm1hcCgoaXRlbTogSU1hbmFnZWRPYmplY3QpID0+IGl0ZW0uaWQpO1xuICAgIH1cbiAgICB0aGlzLnNlbGVjdGVkSXRlbXMgPSBpdGVtcztcbiAgfVxuXG4gIG9uQ2hhbmdlTm9kZUlkKGV2ZW50KSB7XG4gICAgaWYgKGV2ZW50LnRhcmdldC5jaGVja2VkKSB7XG4gICAgICB0aGlzLnNob3dSb290Tm9kZXMgPSB0cnVlO1xuICAgICAgdGhpcy5hZGQoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5zaG93Um9vdE5vZGVzID0gZmFsc2U7XG4gICAgICB0aGlzLmNvbnN0cmFpbnRzLm1hdGNoZXNOb2RlSWRzID0gW107XG4gICAgfVxuICB9XG5cbiAgb25DaGFuZ2VTaG93U2VydmVySWRzKGV2ZW50KSB7XG4gICAgaWYgKCFldmVudC50YXJnZXQuY2hlY2tlZCkge1xuICAgICAgdGhpcy5jb25zdHJhaW50cy5tYXRjaGVzU2VydmVySWRzID0gW107XG4gICAgICB0aGlzLnNob3dTZXJ2ZXJJZHMgPSBmYWxzZTtcbiAgICAgIHRoaXMuc2VsZWN0ZWRJdGVtcyA9IFtdO1xuICAgICAgdGhpcy5jaGVja2VkID0ge307XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuc2hvd1NlcnZlcklkcyA9IHRydWU7XG4gICAgfVxuICB9XG5cbiAgb25DaGFuZ2VTaG93QnJvd3NlUGF0aChldmVudCkge1xuICAgIGlmICghZXZlbnQudGFyZ2V0LmNoZWNrZWQpIHtcbiAgICAgIHRoaXMuY29uc3RyYWludHMuYnJvd3NlUGF0aE1hdGNoZXNSZWdleCA9ICcnO1xuICAgICAgdGhpcy5zaG93QnJvd3NlUGF0aCA9IGZhbHNlO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnNob3dCcm93c2VQYXRoID0gdHJ1ZTtcbiAgICB9XG4gIH1cblxuICBvbkNoYW5nZVNob3dTZXJ2ZXJGcmFnbWVudChldmVudCkge1xuICAgIGlmICghZXZlbnQudGFyZ2V0LmNoZWNrZWQpIHtcbiAgICAgIHRoaXMuY29uc3RyYWludHMuc2VydmVyT2JqZWN0SGFzRnJhZ21lbnQgPSAnJztcbiAgICAgIHRoaXMuc2hvd1NlcnZlckZyYWdtZW50ID0gZmFsc2U7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuc2hvd1NlcnZlckZyYWdtZW50ID0gdHJ1ZTtcbiAgICB9XG4gIH1cblxuICBhZGQoKSB7XG4gICAgdGhpcy5jb25zdHJhaW50cy5tYXRjaGVzTm9kZUlkcy5wdXNoKCcnKTtcbiAgfVxuXG4gIHJlbW92ZShpbmRleCkge1xuICAgIHRoaXMuY29uc3RyYWludHMubWF0Y2hlc05vZGVJZHMuc3BsaWNlKGluZGV4LCAxKTtcbiAgfVxuXG4gIHRyYWNrQnlGbihpbmRleDogYW55LCBfaXRlbTogYW55KSB7XG4gICAgcmV0dXJuIGluZGV4O1xuICB9XG5cbiAgdXBkYXRlQ29uc3RyYWludHMoaXRlbXMpIHtcbiAgICBpZiAodGhpcy5jb25zdHJhaW50cykge1xuICAgICAgdGhpcy5jb25zdHJhaW50cy5tYXRjaGVzU2VydmVySWRzID0gaXRlbXMubWFwKChpdGVtOiBJTWFuYWdlZE9iamVjdCkgPT4gaXRlbS5pZCk7XG4gICAgfVxuICB9XG5cbiAgZmlsdGVySXRlbXMoZmlsdGVyVGV4dCkge1xuICAgIGlmIChmaWx0ZXJUZXh0Lmxlbmd0aCAhPT0gMCkge1xuICAgICAgY29uc3Qgc2VhcmNoID0gbmV3IFJlZ0V4cChmaWx0ZXJUZXh0LCAnaScpO1xuICAgICAgdGhpcy5maWx0ZXJlZExpc3QgPSB0aGlzLm9wY3VhU2VydmVycy5maWx0ZXIoKHsgbmFtZSwgaWQgfSkgPT4ge1xuICAgICAgICByZXR1cm4gc2VhcmNoLnRlc3QobmFtZSkgfHwgc2VhcmNoLnRlc3QoaWQpO1xuICAgICAgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuZmlsdGVyZWRMaXN0ID0gdGhpcy5vcGN1YVNlcnZlcnM7XG4gICAgfVxuICB9XG5cbiAgaXNDaGVja2VkKGl0ZW0pIHtcbiAgICByZXR1cm4gdGhpcy5jaGVja2VkW2l0ZW0uaWRdO1xuICB9XG5cbiAgb25TZWxlY3Qoc2VsZWN0ZWQsIGl0ZW0pIHtcbiAgICBpZiAoIXNlbGVjdGVkKSB7XG4gICAgICB0aGlzLnNlbGVjdGVkSXRlbXMgPSByZWplY3QodGhpcy5zZWxlY3RlZEl0ZW1zLCB7IGlkOiBpdGVtLmlkIH0pO1xuICAgICAgZGVsZXRlIHRoaXMuY2hlY2tlZFtpdGVtLmlkXTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5zZWxlY3RlZEl0ZW1zLnB1c2goaXRlbSk7XG4gICAgICB0aGlzLmNoZWNrZWRbaXRlbS5pZF0gPSBzZWxlY3RlZDtcbiAgICB9XG4gICAgdGhpcy51cGRhdGVTZWxlY3RlZEl0ZW0uZW1pdCh0cnVlKTtcbiAgICB0aGlzLnVwZGF0ZUNvbnN0cmFpbnRzKHRoaXMuc2VsZWN0ZWRJdGVtcyk7XG4gIH1cbn1cbiIsIjxkaXYgY2xhc3M9XCJyb3dcIj5cbiAgPGRpdiBjbGFzcz1cImNvbC1tZC00XCI+XG4gICAgPHAgdHJhbnNsYXRlPlxuICAgICAgU3BlY2lmeWluZyBhdXRvLWFwcGx5IGNvbnN0cmFpbnRzIGFsbG93cyB5b3UgdG8gbGltaXQgdGhlIHNjb3BlIHdoZXJlIHRoZSBkZXZpY2UgcHJvdG9jb2xzIGFyZVxuICAgICAgYXBwbGllZCwgZm9yIGV4YW1wbGUgYnkgc3BlY2lmeWluZyBhIHNldCBvZiBwb3NzaWJsZSBzZXJ2ZXJzIG9yIG5vZGUgSURzLlxuICAgIDwvcD5cbiAgICA8cCB0cmFuc2xhdGU+XG4gICAgICBJZiBubyBjb25zdHJhaW50cyBhcmUgc2V0LCBkZXZpY2UgcHJvdG9jb2xzIGFyZSBhcHBsaWVkIGF0IGFueSBmaXR0aW5nIGxvY2F0aW9uIG9uIHRoZSBPUEMgVUFcbiAgICAgIHNlcnZlci5cbiAgICA8L3A+XG4gIDwvZGl2PlxuXG4gIDxkaXYgY2xhc3M9XCJjb2wtbWQtNlwiPlxuICAgIDx1bCBjbGFzcz1cImxpc3QtZ3JvdXBcIiBzdHlsZT1cImJveC1zaGFkb3c6IG5vbmU7XCI+XG4gICAgICA8IS0tIExpbWl0IGRldmljZSB0eXBlIHRvIGEgc2V0IG9mIHNlcnZlcnMgLS0+XG4gICAgICA8bGkgY2xhc3M9XCJsaXN0LWdyb3VwLWl0ZW1cIj5cbiAgICAgICAgPGxhYmVsXG4gICAgICAgICAgdGl0bGU9XCJ7eyAnTGltaXQgZGV2aWNlIHByb3RvY29sIHRvIGEgc2V0IG9mIHNlcnZlcnMnIHwgdHJhbnNsYXRlIH19XCJcbiAgICAgICAgICBjbGFzcz1cImM4eS1jaGVja2JveFwiXG4gICAgICAgID5cbiAgICAgICAgICA8aW5wdXRcbiAgICAgICAgICAgIHR5cGU9XCJjaGVja2JveFwiXG4gICAgICAgICAgICBbY2hlY2tlZF09XCJcbiAgICAgICAgICAgICAgY29uc3RyYWludHMubWF0Y2hlc1NlcnZlcklkcyAhPT0gbnVsbCAmJiBjb25zdHJhaW50cy5tYXRjaGVzU2VydmVySWRzLmxlbmd0aCA+IDBcbiAgICAgICAgICAgIFwiXG4gICAgICAgICAgICAoY2hhbmdlKT1cIm9uQ2hhbmdlU2hvd1NlcnZlcklkcygkZXZlbnQpXCJcbiAgICAgICAgICAvPlxuICAgICAgICAgIDxzcGFuPjwvc3Bhbj5cbiAgICAgICAgICA8c3BhbiBjbGFzcz1cIm0tbC04XCI+XG4gICAgICAgICAgICB7eyAnTGltaXQgZGV2aWNlIHByb3RvY29sIHRvIGEgc2V0IG9mIHNlcnZlcnMnIHwgdHJhbnNsYXRlIH19XG4gICAgICAgICAgPC9zcGFuPlxuICAgICAgICA8L2xhYmVsPlxuICAgICAgICA8ZGl2XG4gICAgICAgICAgY2xhc3M9XCJjb2xsYXBzZVwiXG4gICAgICAgICAgW2NvbGxhcHNlXT1cIlxuICAgICAgICAgICAgKCFzaG93U2VydmVySWRzICYmXG4gICAgICAgICAgICAgIChjb25zdHJhaW50cy5tYXRjaGVzU2VydmVySWRzICE9PSBudWxsICYmIGNvbnN0cmFpbnRzLm1hdGNoZXNTZXJ2ZXJJZHMubGVuZ3RoIDwgMSkpIHx8XG4gICAgICAgICAgICAoIXNob3dTZXJ2ZXJJZHMgJiYgY29uc3RyYWludHMubWF0Y2hlc1NlcnZlcklkcyA9PT0gbnVsbClcbiAgICAgICAgICBcIlxuICAgICAgICAgIFtpc0FuaW1hdGVkXT1cInRydWVcIlxuICAgICAgICA+XG4gICAgICAgICAgPGM4eS1mb3JtLWdyb3VwIGNsYXNzPVwibS10LTggbS1iLThcIj5cbiAgICAgICAgICAgIDxjOHktc2VsZWN0LWxlZ2FjeVxuICAgICAgICAgICAgICBbaXRlbXNdPVwib3BjdWFTZXJ2ZXJzXCJcbiAgICAgICAgICAgICAgW3NlbGVjdGVkXT1cInNlbGVjdGVkSXRlbXNcIlxuICAgICAgICAgICAgICBbcGxhY2Vob2xkZXJdPVwicGxhY2Vob2xkZXJTZWxlY3RTZXJ2ZXJJZHNcIlxuICAgICAgICAgICAgICAob25DaGFuZ2UpPVwic2VydmVySWRzU2VsZWN0ZWQoJGV2ZW50KVwiXG4gICAgICAgICAgICAgIFt1cGRhdGVJdGVtc109XCJ1cGRhdGVTZWxlY3RlZEl0ZW1cIlxuICAgICAgICAgICAgPlxuICAgICAgICAgICAgXG4gICAgICAgICAgICAgIDxjOHktbGkgKm5nSWY9XCJvcGN1YVNlcnZlcnMubGVuZ3RoID4gc2l6ZVRvU2hvd0ZpbHRlclwiIGNsYXNzPVwic3RpY2t5LXRvcFwiPlxuICAgICAgICAgICAgICAgIDxjOHktZmlsdGVyIFtpY29uXT1cIidzZWFyY2gnXCIgIChvblNlYXJjaCk9XCJmaWx0ZXJJdGVtcygkZXZlbnQpXCI+PC9jOHktZmlsdGVyPlxuICAgICAgICAgICAgICA8L2M4eS1saT5cbiAgICAgICAgICAgICAgPGM4eS1saSAqbmdJZj1cImZpbHRlcmVkTGlzdCAmJiBmaWx0ZXJlZExpc3QubGVuZ3RoID09PSAwXCI+XG4gICAgICAgICAgICAgICAgPGM4eS1saS1ib2R5PiBcbiAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJjOHktZW1wdHktc3RhdGUgdGV4dC1sZWZ0XCI+XG4gICAgICAgICAgICAgICAgICA8cD57eyAnTm8gaXRlbXMgdG8gZGlzcGxheS4nIHwgdHJhbnNsYXRlIH19PC9wPlxuICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgIDwvYzh5LWxpLWJvZHk+XG4gICAgICAgICAgICAgIDwvYzh5LWxpPlxuICAgICAgICAgICAgICA8Yzh5LWxpICpuZ0Zvcj1cImxldCBpdGVtIG9mIGZpbHRlcmVkTGlzdFwiPlxuICAgICAgICAgICAgICAgIDxjOHktbGktY2hlY2tib3hcbiAgICAgICAgICAgICAgICAgIFtzZWxlY3RlZF09XCJpc0NoZWNrZWQoaXRlbSlcIlxuICAgICAgICAgICAgICAgICAgKG9uU2VsZWN0KT1cIm9uU2VsZWN0KCRldmVudCwgaXRlbSlcIlxuICAgICAgICAgICAgICAgID48L2M4eS1saS1jaGVja2JveD5cbiAgICAgICAgICAgICAgICA8Yzh5LWxpLWJvZHk+e3sgaXRlbS5uYW1lIHwgdHJhbnNsYXRlIH19PGJyPlxuICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJ0ZXh0LWxhYmVsLXNtYWxsIG0tci00XCIgdHJhbnNsYXRlPklEPC9zcGFuPiA8c21hbGw+e3sgaXRlbS5pZCB9fTwvc21hbGw+XG4gICAgICAgICAgICAgICAgPC9jOHktbGktYm9keT5cbiAgICAgICAgICAgICAgPC9jOHktbGk+XG4gICAgICAgICAgICA8L2M4eS1zZWxlY3QtbGVnYWN5PlxuICAgICAgICAgIDwvYzh5LWZvcm0tZ3JvdXA+XG4gICAgICAgIDwvZGl2PlxuICAgICAgPC9saT5cbiAgICAgIDwhLS0gTGltaXQgZGV2aWNlIHR5cGUgc2NvcGUgaW4gdGhlIGFkZHJlc3Mgc3BhY2UgLS0+XG4gICAgICA8bGkgY2xhc3M9XCJsaXN0LWdyb3VwLWl0ZW1cIj5cbiAgICAgICAgPGxhYmVsXG4gICAgICAgICAgdGl0bGU9XCJ7eyAnTGltaXQgZGV2aWNlIHByb3RvY29sIHNjb3BlIGluIHRoZSBhZGRyZXNzIHNwYWNlJyB8IHRyYW5zbGF0ZSB9fVwiXG4gICAgICAgICAgY2xhc3M9XCJjOHktY2hlY2tib3hcIlxuICAgICAgICA+XG4gICAgICAgICAgPGlucHV0XG4gICAgICAgICAgICB0eXBlPVwiY2hlY2tib3hcIlxuICAgICAgICAgICAgW2NoZWNrZWRdPVwiXG4gICAgICAgICAgICAgIGNvbnN0cmFpbnRzLmJyb3dzZVBhdGhNYXRjaGVzUmVnZXggIT09IG51bGwgJiZcbiAgICAgICAgICAgICAgY29uc3RyYWludHMuYnJvd3NlUGF0aE1hdGNoZXNSZWdleC5sZW5ndGggPiAwXG4gICAgICAgICAgICBcIlxuICAgICAgICAgICAgKGNoYW5nZSk9XCJvbkNoYW5nZVNob3dCcm93c2VQYXRoKCRldmVudClcIlxuICAgICAgICAgIC8+XG4gICAgICAgICAgPHNwYW4+PC9zcGFuPlxuICAgICAgICAgIDxzcGFuIGNsYXNzPVwibS1sLThcIj5cbiAgICAgICAgICAgIHt7ICdMaW1pdCBkZXZpY2UgcHJvdG9jb2wgc2NvcGUgaW4gdGhlIGFkZHJlc3Mgc3BhY2UnIHwgdHJhbnNsYXRlIH19XG4gICAgICAgICAgPC9zcGFuPlxuICAgICAgICA8L2xhYmVsPlxuICAgICAgICA8ZGl2XG4gICAgICAgICAgY2xhc3M9XCJjb2xsYXBzZVwiXG4gICAgICAgICAgW2lzQW5pbWF0ZWRdPVwidHJ1ZVwiXG4gICAgICAgICAgW2NvbGxhcHNlXT1cIlxuICAgICAgICAgICAgKCFzaG93QnJvd3NlUGF0aCAmJlxuICAgICAgICAgICAgICBjb25zdHJhaW50cy5icm93c2VQYXRoTWF0Y2hlc1JlZ2V4ICE9PSBudWxsICYmXG4gICAgICAgICAgICAgIGNvbnN0cmFpbnRzLmJyb3dzZVBhdGhNYXRjaGVzUmVnZXgubGVuZ3RoIDwgMSkgfHxcbiAgICAgICAgICAgICghc2hvd0Jyb3dzZVBhdGggJiYgY29uc3RyYWludHMuYnJvd3NlUGF0aE1hdGNoZXNSZWdleCA9PT0gbnVsbClcbiAgICAgICAgICBcIlxuICAgICAgICA+XG4gICAgICAgICAgPGM4eS1mb3JtLWdyb3VwIGNsYXNzPVwibS10LTggbS1iLThcIj5cbiAgICAgICAgICAgIDxpbnB1dFxuICAgICAgICAgICAgICBuYW1lPVwiYnJvd3NlUGF0aFwiXG4gICAgICAgICAgICAgIHR5cGU9XCJ0ZXh0XCJcbiAgICAgICAgICAgICAgY2xhc3M9XCJmb3JtLWNvbnRyb2xcIlxuICAgICAgICAgICAgICBwbGFjZWhvbGRlcj1cInt7ICdlLmcuJyB8IHRyYW5zbGF0ZSB9fSAvb2JqZWN0cy9kZXZpY2VzLy4qXCJcbiAgICAgICAgICAgICAgWyhuZ01vZGVsKV09XCJjb25zdHJhaW50cy5icm93c2VQYXRoTWF0Y2hlc1JlZ2V4XCJcbiAgICAgICAgICAgICAgbmdEZWZhdWx0Q29udHJvbFxuICAgICAgICAgICAgLz5cbiAgICAgICAgICA8L2M4eS1mb3JtLWdyb3VwPlxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvbGk+XG4gICAgICA8IS0tIExpbWl0IGRldmljZSB0eXBlIHRvIHNlcnZlcnMgd2l0aCBhIGNlcnRhaW4gZnJhZ21lbnQtLT5cbiAgICAgIDxsaSBjbGFzcz1cImxpc3QtZ3JvdXAtaXRlbVwiPlxuICAgICAgICA8bGFiZWxcbiAgICAgICAgICB0aXRsZT1cInt7ICdMaW1pdCBkZXZpY2UgcHJvdG9jb2wgdG8gc2VydmVycyB3aXRoIGEgY2VydGFpbiBmcmFnbWVudCcgfCB0cmFuc2xhdGUgfX1cIlxuICAgICAgICAgIGNsYXNzPVwiYzh5LWNoZWNrYm94XCJcbiAgICAgICAgPlxuICAgICAgICAgIDxpbnB1dFxuICAgICAgICAgICAgdHlwZT1cImNoZWNrYm94XCJcbiAgICAgICAgICAgIFtjaGVja2VkXT1cImNvbnN0cmFpbnRzLnNlcnZlck9iamVjdEhhc0ZyYWdtZW50ICE9PSBudWxsICYmIGNvbnN0cmFpbnRzLnNlcnZlck9iamVjdEhhc0ZyYWdtZW50Lmxlbmd0aCA+IDBcIlxuICAgICAgICAgICAgKGNoYW5nZSk9XCJvbkNoYW5nZVNob3dTZXJ2ZXJGcmFnbWVudCgkZXZlbnQpXCJcbiAgICAgICAgICAvPlxuICAgICAgICAgIDxzcGFuPjwvc3Bhbj5cbiAgICAgICAgICA8c3BhbiBjbGFzcz1cIm0tbC04XCI+XG4gICAgICAgICAgICB7eyAnTGltaXQgZGV2aWNlIHByb3RvY29sIHRvIHNlcnZlcnMgd2l0aCBhIGNlcnRhaW4gZnJhZ21lbnQnIHwgdHJhbnNsYXRlIH19XG4gICAgICAgICAgPC9zcGFuPlxuICAgICAgICA8L2xhYmVsPlxuICAgICAgICA8ZGl2XG4gICAgICAgICAgY2xhc3M9XCJjb2xsYXBzZVwiXG4gICAgICAgICAgW2lzQW5pbWF0ZWRdPVwidHJ1ZVwiXG4gICAgICAgICAgW2NvbGxhcHNlXT1cIighc2hvd1NlcnZlckZyYWdtZW50ICYmIGNvbnN0cmFpbnRzLnNlcnZlck9iamVjdEhhc0ZyYWdtZW50ICE9PSBudWxsICYmIGNvbnN0cmFpbnRzLnNlcnZlck9iamVjdEhhc0ZyYWdtZW50Lmxlbmd0aCA8IDEpIHx8ICghc2hvd1NlcnZlckZyYWdtZW50ICYmIGNvbnN0cmFpbnRzLnNlcnZlck9iamVjdEhhc0ZyYWdtZW50ID09PSBudWxsKVwiXG4gICAgICAgID5cbiAgICAgICAgICA8Yzh5LWZvcm0tZ3JvdXAgY2xhc3M9XCJtLXQtOCBtLWItOFwiPlxuICAgICAgICAgICAgPGlucHV0XG4gICAgICAgICAgICAgIG5hbWU9XCJzZXJ2ZXJGcmFnbWVudFwiXG4gICAgICAgICAgICAgIHR5cGU9XCJ0ZXh0XCJcbiAgICAgICAgICAgICAgY2xhc3M9XCJmb3JtLWNvbnRyb2xcIlxuICAgICAgICAgICAgICBwbGFjZWhvbGRlcj1cInt7ICdlLmcuJyB8IHRyYW5zbGF0ZSB9fSBjOHlfU29tZVNlcnZlck1hcmtlclwiXG4gICAgICAgICAgICAgIFsobmdNb2RlbCldPVwiY29uc3RyYWludHMuc2VydmVyT2JqZWN0SGFzRnJhZ21lbnRcIlxuICAgICAgICAgICAgICBuZ0RlZmF1bHRDb250cm9sXG4gICAgICAgICAgICAvPlxuICAgICAgICAgIDwvYzh5LWZvcm0tZ3JvdXA+XG4gICAgICAgIDwvZGl2PlxuICAgICAgPC9saT5cbiAgICAgIDwhLS0gTGltaXQgZGV2aWNlIHR5cGUgdG8gYSBzcGVjaWZpYyByb290IG5vZGUgSUQgLS0+XG4gICAgICA8bGkgY2xhc3M9XCJsaXN0LWdyb3VwLWl0ZW1cIj5cbiAgICAgICAgPGxhYmVsXG4gICAgICAgICAgdGl0bGU9XCJ7eyAnTGltaXQgZGV2aWNlIHByb3RvY29sIHRvIHNwZWNpZmljIHJvb3Qgbm9kZXMgSUQnIHwgdHJhbnNsYXRlIH19XCJcbiAgICAgICAgICBjbGFzcz1cImM4eS1jaGVja2JveFwiXG4gICAgICAgID5cbiAgICAgICAgICA8aW5wdXRcbiAgICAgICAgICAgIHR5cGU9XCJjaGVja2JveFwiXG4gICAgICAgICAgICBbY2hlY2tlZF09XCJjb25zdHJhaW50cy5tYXRjaGVzTm9kZUlkcyAhPT0gbnVsbCAmJiBjb25zdHJhaW50cy5tYXRjaGVzTm9kZUlkcy5sZW5ndGggPiAwXCJcbiAgICAgICAgICAgIChjaGFuZ2UpPVwib25DaGFuZ2VOb2RlSWQoJGV2ZW50KVwiXG4gICAgICAgICAgLz5cbiAgICAgICAgICA8c3Bhbj48L3NwYW4+XG4gICAgICAgICAgPHNwYW4gY2xhc3M9XCJtLWwtOFwiPlxuICAgICAgICAgICAge3sgJ0xpbWl0IGRldmljZSBwcm90b2NvbCB0byBzcGVjaWZpYyByb290IG5vZGVzIElEJyB8IHRyYW5zbGF0ZSB9fVxuICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgPC9sYWJlbD5cbiAgICAgICAgPGRpdlxuICAgICAgICAgIGNsYXNzPVwiY29sbGFwc2VcIlxuICAgICAgICAgIFtpc0FuaW1hdGVkXT1cInRydWVcIlxuICAgICAgICAgIFtjb2xsYXBzZV09XCIoIXNob3dSb290Tm9kZXMgJiYgY29uc3RyYWludHMubWF0Y2hlc05vZGVJZHMgIT09IG51bGwgJiYgY29uc3RyYWludHMubWF0Y2hlc05vZGVJZHMubGVuZ3RoIDwgMSkgfHwgKCAhc2hvd1Jvb3ROb2RlcyAmJiBjb25zdHJhaW50cy5tYXRjaGVzTm9kZUlkcyA9PT0gbnVsbClcIlxuICAgICAgICA+XG4gICAgICAgICAgICA8dWwgYzh5SW5wdXRHcm91cExpc3RDb250YWluZXIgY2xhc3M9XCJsaXN0LXVuc3R5bGVkIHAtdC0xNlwiPlxuICAgICAgICAgICAgICA8bGlcbiAgICAgICAgICAgICAgICBjbGFzcz1cIm0tYi04XCJcbiAgICAgICAgICAgICAgICAqbmdGb3I9XCJsZXQgaXRlbSBvZiBjb25zdHJhaW50cy5tYXRjaGVzTm9kZUlkczsgbGV0IGkgPSBpbmRleDsgdHJhY2tCeTogdHJhY2tCeUZuXCJcbiAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgIDxjOHktaW5wdXQtZ3JvdXAtbGlzdCBbaW5kZXhdPVwiaVwiIChvbkFkZCk9XCJhZGQoKVwiIChvblJlbW92ZSk9XCJyZW1vdmUoJGV2ZW50KVwiPlxuICAgICAgICAgICAgICAgICAgPGM4eS1mb3JtLWdyb3VwIGNsYXNzPVwiZm9ybS1ncm91cC0tdG9vbHRpcC12YWxpZGF0aW9uXCI+XG4gICAgICAgICAgICAgICAgICAgIDxpbnB1dFxuICAgICAgICAgICAgICAgICAgICAgIHR5cGU9XCJ0ZXh0XCJcbiAgICAgICAgICAgICAgICAgICAgICBjbGFzcz1cImZvcm0tY29udHJvbFwiXG4gICAgICAgICAgICAgICAgICAgICAgcGxhY2Vob2xkZXI9XCJ7eyAnZS5nLicgfCB0cmFuc2xhdGUgfX0gbm9kZUlkXCJcbiAgICAgICAgICAgICAgICAgICAgICBbKG5nTW9kZWwpXT1cImNvbnN0cmFpbnRzLm1hdGNoZXNOb2RlSWRzW2ldXCJcbiAgICAgICAgICAgICAgICAgICAgICBbcmVxdWlyZWRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgICAgIC8+XG4gICAgICAgICAgICAgICAgICA8L2M4eS1mb3JtLWdyb3VwPlxuICAgICAgICAgICAgICAgIDwvYzh5LWlucHV0LWdyb3VwLWxpc3Q+XG4gICAgICAgICAgICAgIDwvbGk+XG4gICAgICAgICAgICA8L3VsPiBcbiAgICAgICAgPC9kaXY+XG4gICAgICA8L2xpPlxuICAgIDwvdWw+XG4gIDwvZGl2PlxuPC9kaXY+XG4iXX0=