UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

109 lines 41.7 kB
import { Component } from '@angular/core'; import { isEmpty, pull } from 'lodash-es'; import { CrlService } from '@c8y/client'; import { AlertService, gettext, ModalService, Status } from '@c8y/ngx-components'; import { saveAs } from 'file-saver'; 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/datepicker"; import * as i6 from "ngx-bootstrap/popover"; import * as i7 from "./crl-check-settings.component"; export class CrlSettingsComponent { constructor(crlService, alertService, modalService) { this.crlService = crlService; this.alertService = alertService; this.modalService = modalService; this.crls = [this.getEmptyCertificateRevocation()]; this.droppedFiles = []; this.today = new Date(); this.MANUAL_ENTRY_POPOVER = gettext('In this section, you can override or add individual entries to the CRL. Providing the serial number is mandatory. In case the revocation date is not set, it will be configured to the current date.'); this.FILE_UPLOAD_POPOVER = gettext('In this section, you can upload a file with the list of certificates to be revoked. The file must be in CSV format, and it should include the serial number and revocation date. If the revocation date is empty, it will be set to the current date.'); } async downloadCrl() { let arrayBuffer; try { const res = await this.crlService.downloadCrlFile(); arrayBuffer = await res.arrayBuffer(); const fileBinary = new File([arrayBuffer], 'cumulocity.crl', { type: 'application/pkix-crl' }); saveAs(fileBinary); } catch (ex) { this.alertService.addServerFailure(ex); } } downloadCrlTemplate() { const blob = new Blob([`SERIALNO,DATE\nSERIAL_NO1,${new Date().toISOString()}`], { type: 'text/csv;charset=utf-8;' }); const fileName = 'crl-template'; saveAs(blob, `${fileName}.csv`); } addCertificateRevocation() { this.crls.push(this.getEmptyCertificateRevocation()); } removeCertificateRevocation(certificateRevocation) { pull(this.crls, certificateRevocation); if (this.crls.length === 0) { this.addCertificateRevocation(); } } async save() { try { await this.confirm(); await this.saveCrls(); await this.uploadCrlFile(); await this.clearSavedData(); this.alertService.success(gettext('CRL saved.')); } catch (er) { this.alertService.addServerFailure(er); } } get isListEmpty() { return this.crls.length === 1 && isEmpty(this.crls[0].serialNumberInHex); } get isFileDropped() { return !!(this.droppedFiles && this.droppedFiles[0]); } async confirm() { const isOverrideManualEntries = !this.isListEmpty && this.isFileDropped; const status = isOverrideManualEntries ? Status.DANGER : Status.WARNING; const body = isOverrideManualEntries ? gettext('You are about to update the CRL. The CRL file content will override manual entries. Do you want to proceed?') : gettext('You are about to update the CRL. Do you want to proceed?'); await this.modalService.confirm(gettext('Update the CRL'), body, status, { ok: gettext('Update') }); } clearSavedData() { this.droppedFiles = []; this.crls = [this.getEmptyCertificateRevocation()]; } async saveCrls() { if (!this.isListEmpty) { await this.crlService.uploadCrls(this.crls); } } async uploadCrlFile() { if (this.isFileDropped) { await this.crlService.uploadCrlFile(this.droppedFiles[0].file); } } getEmptyCertificateRevocation() { return { serialNumberInHex: '' }; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CrlSettingsComponent, deps: [{ token: i1.CrlService }, { token: i2.AlertService }, { token: i2.ModalService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: CrlSettingsComponent, selector: "c8y-crl-settings", ngImport: i0, template: "<c8y-title>{{ 'Trusted certificates' | translate }}</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n icon=\"c8y-management\"\n label=\"{{ 'Management' | translate }}\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n icon=\"certificate\"\n label=\"{{ 'Trusted certificates' | translate }}\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-crl-check-settings></c8y-crl-check-settings>\n\n<c8y-help\n src=\"/docs/device-management-application/managing-device-data/#managing-trusted-certificate-settings\"\n [icon]=\"'settings'\"\n></c8y-help>\n\n<form\n class=\"card card--fullpage\"\n #crlManualForm=\"ngForm\"\n novalidate\n>\n <div class=\"card-header separator\">\n <div class=\"card-title\">\n {{ 'Offline configuration of Certificate Revocation List (CRL)' | translate }}\n </div>\n </div>\n <div class=\"inner-scroll\">\n <div class=\"card-block\">\n <div\n class=\"legend form-block\"\n >\n {{ 'Manual' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ MANUAL_ENTRY_POPOVER | translate }}\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n </div>\n\n <div\n class=\"row tight-grid\"\n data-cy=\"c8y-row-grid--new-row\"\n *ngFor=\"let certificateRevocation of crls; index as index; last as isLast\"\n >\n <div class=\"col-sm-5\">\n <c8y-form-group>\n <label\n [for]=\"'serialNumber' + index\"\n translate\n >\n Serial number\n </label>\n <input\n class=\"form-control\"\n required\n [name]=\"'serialNumber' + index\"\n [id]=\"'serialNumber' + index\"\n [(ngModel)]=\"certificateRevocation.serialNumberInHex\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: '8ab34fe5476' }\"\n c8yDefaultValidation=\"colonedHexNumber\"\n />\n </c8y-form-group>\n </div>\n <div class=\"col-sm-5\">\n <c8y-form-group class=\"datepicker\">\n <label [for]=\"'validTillPicker' + index\">\n {{ 'Valid till' | translate }}\n </label>\n <input\n class=\"form-control\"\n [attr.aria-label]=\"'Date to' | translate\"\n placeholder=\"{{ 'Date to' | translate }}\"\n [name]=\"'validTillPicker' + index\"\n [id]=\"'validTillPicker' + index\"\n [(ngModel)]=\"certificateRevocation.revocationDate\"\n [bsConfig]=\"{ customTodayClass: 'today' }\"\n bsDatepicker\n [maxDate]=\"today\"\n />\n </c8y-form-group>\n </div>\n <div class=\"col-sm-2\">\n <c8y-form-group>\n <div class=\"p-t-24\">\n <button\n *ngIf=\"crls.length > 1\"\n class=\"btn btn-link hidden-xs hidden-sm\"\n title=\"{{ 'Remove' | translate }}\"\n type=\"button\"\n (click)=\"removeCertificateRevocation(certificateRevocation)\"\n >\n <i\n class=\"text-danger\"\n c8yIcon=\"minus-circle\"\n ></i>\n </button>\n <button\n class=\"btn btn-link hidden-xs hidden-sm\"\n title=\"{{ 'Add' | translate }}\"\n type=\"button\"\n (click)=\"addCertificateRevocation()\"\n *ngIf=\"isLast\"\n >\n <i\n class=\"text-primary\"\n c8yIcon=\"plus-circle\"\n ></i>\n </button>\n\n <button\n *ngIf=\"crls.length > 1\"\n class=\"btn btn-danger btn-block btn-sm visible-xs visible-sm\"\n title=\"{{ 'Remove' | translate }}\"\n type=\"button\"\n (click)=\"removeCertificateRevocation(certificateRevocation)\"\n >\n <i c8yIcon=\"minus-circle\"></i>\n {{ 'Remove' | translate }}\n </button>\n <button\n class=\"btn btn-primary btn-block btn-sm visible-xs visible-sm\"\n title=\"{{ 'Add' | translate }}\"\n type=\"button\"\n (click)=\"addCertificateRevocation()\"\n *ngIf=\"isLast\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Add' | translate }}\n </button>\n </div>\n </c8y-form-group>\n </div>\n </div>\n <div class=\"legend form-block center\">\n {{ 'or' | translate }}\n </div>\n <div\n class=\"legend form-block\"\n >\n {{ 'File upload' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ FILE_UPLOAD_POPOVER | translate }}\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n </div>\n\n <c8y-drop-area\n class=\"drop-area-sm\"\n [title]=\"'Upload CRL file (.csv format)' | translate\"\n name=\"uploadCrlDropArea\"\n [(ngModel)]=\"droppedFiles\"\n [message]=\"'Upload CRL file (.csv format)' | translate\"\n [loadingMessage]=\"'Uploading\u2026' | translate\"\n [accept]=\"'.csv'\"\n [maxAllowedFiles]=\"1\"\n ></c8y-drop-area>\n <p class=\"help-block has-info text-muted m-t-4\">\n {{ 'CRL file content will override manual entries.' | translate }}\n </p>\n\n <button\n class=\"btn btn-sm btn-default m-t-16\"\n title=\"{{ 'Download template' | translate }}\"\n type=\"button\"\n (click)=\"downloadCrlTemplate()\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"template\"\n ></i>\n {{ 'Download template' | translate }}\n </button>\n </div>\n </div>\n <div class=\"card-footer separator\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Download CRL file' | translate }}\"\n type=\"button\"\n (click)=\"downloadCrl()\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"download\"\n ></i>\n {{ 'Download CRL file' | translate }}\n </button>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Save' | translate }}\"\n type=\"submit\"\n (click)=\"save()\"\n [disabled]=\"!(crlManualForm.form.valid || isListEmpty) || (isListEmpty && !isFileDropped)\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n</form>\n", dependencies: [{ kind: "component", type: i2.BreadcrumbComponent, selector: "c8y-breadcrumb" }, { kind: "component", type: i2.BreadcrumbItemComponent, selector: "c8y-breadcrumb-item", inputs: ["icon", "translate", "label", "path", "injector"] }, { kind: "directive", type: i2.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { 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.DropAreaComponent, selector: "c8y-drop-area", inputs: ["formControl", "title", "message", "icon", "loadingMessage", "forceHideList", "alwaysShow", "clickToOpen", "loading", "progress", "maxAllowedFiles", "files", "maxFileSizeInMegaBytes", "accept"], outputs: ["dropped"] }, { kind: "component", type: i2.TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "directive", type: i2.DefaultValidationDirective, selector: "[c8yDefaultValidation]", inputs: ["c8yDefaultValidation"] }, { kind: "directive", type: i4.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { 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.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.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: i2.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i2.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "component", type: i2.HelpComponent, selector: "c8y-help", inputs: ["src", "isCollapsed", "priority", "icon"] }, { kind: "directive", type: i5.BsDatepickerDirective, selector: "[bsDatepicker]", inputs: ["placement", "triggers", "outsideClick", "container", "outsideEsc", "isDisabled", "minDate", "maxDate", "minMode", "daysDisabled", "datesDisabled", "datesEnabled", "dateCustomClasses", "dateTooltipTexts", "isOpen", "bsValue", "bsConfig"], outputs: ["onShown", "onHidden", "bsValueChange"], exportAs: ["bsDatepicker"] }, { kind: "directive", type: i5.BsDatepickerInputDirective, selector: "input[bsDatepicker]" }, { kind: "directive", type: i6.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "component", type: i7.CrlCheckSettingsComponent, selector: "c8y-crl-check-settings" }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CrlSettingsComponent, decorators: [{ type: Component, args: [{ selector: 'c8y-crl-settings', template: "<c8y-title>{{ 'Trusted certificates' | translate }}</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n icon=\"c8y-management\"\n label=\"{{ 'Management' | translate }}\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n icon=\"certificate\"\n label=\"{{ 'Trusted certificates' | translate }}\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-crl-check-settings></c8y-crl-check-settings>\n\n<c8y-help\n src=\"/docs/device-management-application/managing-device-data/#managing-trusted-certificate-settings\"\n [icon]=\"'settings'\"\n></c8y-help>\n\n<form\n class=\"card card--fullpage\"\n #crlManualForm=\"ngForm\"\n novalidate\n>\n <div class=\"card-header separator\">\n <div class=\"card-title\">\n {{ 'Offline configuration of Certificate Revocation List (CRL)' | translate }}\n </div>\n </div>\n <div class=\"inner-scroll\">\n <div class=\"card-block\">\n <div\n class=\"legend form-block\"\n >\n {{ 'Manual' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ MANUAL_ENTRY_POPOVER | translate }}\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n </div>\n\n <div\n class=\"row tight-grid\"\n data-cy=\"c8y-row-grid--new-row\"\n *ngFor=\"let certificateRevocation of crls; index as index; last as isLast\"\n >\n <div class=\"col-sm-5\">\n <c8y-form-group>\n <label\n [for]=\"'serialNumber' + index\"\n translate\n >\n Serial number\n </label>\n <input\n class=\"form-control\"\n required\n [name]=\"'serialNumber' + index\"\n [id]=\"'serialNumber' + index\"\n [(ngModel)]=\"certificateRevocation.serialNumberInHex\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: '8ab34fe5476' }\"\n c8yDefaultValidation=\"colonedHexNumber\"\n />\n </c8y-form-group>\n </div>\n <div class=\"col-sm-5\">\n <c8y-form-group class=\"datepicker\">\n <label [for]=\"'validTillPicker' + index\">\n {{ 'Valid till' | translate }}\n </label>\n <input\n class=\"form-control\"\n [attr.aria-label]=\"'Date to' | translate\"\n placeholder=\"{{ 'Date to' | translate }}\"\n [name]=\"'validTillPicker' + index\"\n [id]=\"'validTillPicker' + index\"\n [(ngModel)]=\"certificateRevocation.revocationDate\"\n [bsConfig]=\"{ customTodayClass: 'today' }\"\n bsDatepicker\n [maxDate]=\"today\"\n />\n </c8y-form-group>\n </div>\n <div class=\"col-sm-2\">\n <c8y-form-group>\n <div class=\"p-t-24\">\n <button\n *ngIf=\"crls.length > 1\"\n class=\"btn btn-link hidden-xs hidden-sm\"\n title=\"{{ 'Remove' | translate }}\"\n type=\"button\"\n (click)=\"removeCertificateRevocation(certificateRevocation)\"\n >\n <i\n class=\"text-danger\"\n c8yIcon=\"minus-circle\"\n ></i>\n </button>\n <button\n class=\"btn btn-link hidden-xs hidden-sm\"\n title=\"{{ 'Add' | translate }}\"\n type=\"button\"\n (click)=\"addCertificateRevocation()\"\n *ngIf=\"isLast\"\n >\n <i\n class=\"text-primary\"\n c8yIcon=\"plus-circle\"\n ></i>\n </button>\n\n <button\n *ngIf=\"crls.length > 1\"\n class=\"btn btn-danger btn-block btn-sm visible-xs visible-sm\"\n title=\"{{ 'Remove' | translate }}\"\n type=\"button\"\n (click)=\"removeCertificateRevocation(certificateRevocation)\"\n >\n <i c8yIcon=\"minus-circle\"></i>\n {{ 'Remove' | translate }}\n </button>\n <button\n class=\"btn btn-primary btn-block btn-sm visible-xs visible-sm\"\n title=\"{{ 'Add' | translate }}\"\n type=\"button\"\n (click)=\"addCertificateRevocation()\"\n *ngIf=\"isLast\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Add' | translate }}\n </button>\n </div>\n </c8y-form-group>\n </div>\n </div>\n <div class=\"legend form-block center\">\n {{ 'or' | translate }}\n </div>\n <div\n class=\"legend form-block\"\n >\n {{ 'File upload' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ FILE_UPLOAD_POPOVER | translate }}\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n </div>\n\n <c8y-drop-area\n class=\"drop-area-sm\"\n [title]=\"'Upload CRL file (.csv format)' | translate\"\n name=\"uploadCrlDropArea\"\n [(ngModel)]=\"droppedFiles\"\n [message]=\"'Upload CRL file (.csv format)' | translate\"\n [loadingMessage]=\"'Uploading\u2026' | translate\"\n [accept]=\"'.csv'\"\n [maxAllowedFiles]=\"1\"\n ></c8y-drop-area>\n <p class=\"help-block has-info text-muted m-t-4\">\n {{ 'CRL file content will override manual entries.' | translate }}\n </p>\n\n <button\n class=\"btn btn-sm btn-default m-t-16\"\n title=\"{{ 'Download template' | translate }}\"\n type=\"button\"\n (click)=\"downloadCrlTemplate()\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"template\"\n ></i>\n {{ 'Download template' | translate }}\n </button>\n </div>\n </div>\n <div class=\"card-footer separator\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Download CRL file' | translate }}\"\n type=\"button\"\n (click)=\"downloadCrl()\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"download\"\n ></i>\n {{ 'Download CRL file' | translate }}\n </button>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Save' | translate }}\"\n type=\"submit\"\n (click)=\"save()\"\n [disabled]=\"!(crlManualForm.form.valid || isListEmpty) || (isListEmpty && !isFileDropped)\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n</form>\n" }] }], ctorParameters: () => [{ type: i1.CrlService }, { type: i2.AlertService }, { type: i2.ModalService }] }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JsLXNldHRpbmdzLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RydXN0ZWQtY2VydGlmaWNhdGVzL2NybC9jcmwtc2V0dGluZ3MuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vdHJ1c3RlZC1jZXJ0aWZpY2F0ZXMvY3JsL2NybC1zZXR0aW5ncy5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQzFDLE9BQU8sRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQzFDLE9BQU8sRUFBRSxVQUFVLEVBQTBCLE1BQU0sYUFBYSxDQUFDO0FBQ2pFLE9BQU8sRUFBRSxZQUFZLEVBQWUsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUMvRixPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sWUFBWSxDQUFDOzs7Ozs7Ozs7QUFNcEMsTUFBTSxPQUFPLG9CQUFvQjtJQWEvQixZQUNVLFVBQXNCLEVBQ3RCLFlBQTBCLEVBQzFCLFlBQTBCO1FBRjFCLGVBQVUsR0FBVixVQUFVLENBQVk7UUFDdEIsaUJBQVksR0FBWixZQUFZLENBQWM7UUFDMUIsaUJBQVksR0FBWixZQUFZLENBQWM7UUFmcEMsU0FBSSxHQUE2QixDQUFDLElBQUksQ0FBQyw2QkFBNkIsRUFBRSxDQUFDLENBQUM7UUFDeEUsaUJBQVksR0FBa0IsRUFBRSxDQUFDO1FBQ2pDLFVBQUssR0FBUyxJQUFJLElBQUksRUFBRSxDQUFDO1FBRXpCLHlCQUFvQixHQUFHLE9BQU8sQ0FDNUIsc01BQXNNLENBQ3ZNLENBQUM7UUFFRix3QkFBbUIsR0FBRyxPQUFPLENBQzNCLHVQQUF1UCxDQUN4UCxDQUFDO0lBTUMsQ0FBQztJQUVKLEtBQUssQ0FBQyxXQUFXO1FBQ2YsSUFBSSxXQUF3QixDQUFDO1FBQzdCLElB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QU8sQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFO1lBQ3ZFLEVBQUUsRUFBRSxPQUFPLENBQUMsUUFBUSxDQUFDO1NBQ3RCLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFTyxjQUFjO1FBQ3BCLElBQUksQ0FBQyxZQUFZLEdBQUcsRUFBRSxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxJQUFJLEdBQUcsQ0FBQyxJQUFJLENBQUMsNkJBQTZCLEVBQUUsQ0FBQyxDQUFDO0lBQ3JELENBQUM7SUFFTyxLQUFLLENBQUMsUUFBUTtRQUNwQixJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1lBQ3RCLE1BQU0sSUFBSSxDQUFDLFVBQVUsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQzlDLENBQUM7SUFDSCxDQUFDO0lBRU8sS0FBSyxDQUFDLGFBQWE7UUFDekIsSUFBSSxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7WUFDdkIsTUFBTSxJQUFJLENBQUMsVUFBVSxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ2pFLENBQUM7SUFDSCxDQUFDO0lBRU8sNkJBQTZCO1FBQ25DLE9BQU87WUFDTCxpQkFBaUIsRUFBRSxFQUFFO1NBQ3RCLENBQUM7SUFDSixDQUFDOytHQTNHVSxvQkFBb0I7bUdBQXBCLG9CQUFvQix3RENWakMsb3pOQW1OQTs7NEZEek1hLG9CQUFvQjtrQkFKaEMsU0FBUzsrQkFDRSxrQkFBa0IiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IGlzRW1wdHksIHB1bGwgfSBmcm9tICdsb2Rhc2gtZXMnO1xuaW1wb3J0IHsgQ3JsU2VydmljZSwgSUNlcnRpZmljYXRlUmV2b2NhdGlvbiB9IGZyb20gJ0BjOHkvY2xpZW50JztcbmltcG9ydCB7IEFsZXJ0U2VydmljZSwgRHJvcHBlZEZpbGUsIGdldHRleHQsIE1vZGFsU2VydmljZSwgU3RhdHVzIH0gZnJvbSAnQGM4eS9uZ3gtY29tcG9uZW50cyc7XG5pbXBvcnQgeyBzYXZlQXMgfSBmcm9tICdmaWxlLXNhdmVyJztcblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnYzh5LWNybC1zZXR0aW5ncycsXG4gIHRlbXBsYXRlVXJsOiAnLi9jcmwtc2V0dGluZ3MuY29tcG9uZW50Lmh0bWwnXG59KVxuZXhwb3J0IGNsYXNzIENybFNldHRpbmdzQ29tcG9uZW50IHtcbiAgY3JsczogSUNlcnRpZmljYXRlUmV2b2NhdGlvbltdID0gW3RoaXMuZ2V0RW1wdHlDZXJ0aWZpY2F0ZVJldm9jYXRpb24oKV07XG4gIGRyb3BwZWRGaWxlczogRHJvcHBlZEZpbGVbXSA9IFtdO1xuICB0b2RheTogRGF0ZSA9IG5ldyBEYXRlKCk7XG5cbiAgTUFOVUFMX0VOVFJZX1BPUE9WRVIgPSBnZXR0ZXh0KFxuICAgICdJbiB0aGlzIHNlY3Rpb24sIHlvdSBjYW4gb3ZlcnJpZGUgb3IgYWRkIGluZGl2aWR1YWwgZW50cmllcyB0byB0aGUgQ1JMLiBQcm92aWRpbmcgdGhlIHNlcmlhbCBudW1iZXIgaXMgbWFuZGF0b3J5LiBJbiBjYXNlIHRoZSByZXZvY2F0aW9uIGRhdGUgaXMgbm90IHNldCwgaXQgd2lsbCBiZSBjb25maWd1cmVkIHRvIHRoZSBjdXJyZW50IGRhdGUuJ1xuICApO1xuXG4gIEZJTEVfVVBMT0FEX1BPUE9WRVIgPSBnZXR0ZXh0KFxuICAgICdJbiB0aGlzIHNlY3Rpb24sIHlvdSBjYW4gdXBsb2FkIGEgZmlsZSB3aXRoIHRoZSBsaXN0IG9mIGNlcnRpZmljYXRlcyB0byBiZSByZXZva2VkLiBUaGUgZmlsZSBtdXN0IGJlIGluIENTViBmb3JtYXQsIGFuZCBpdCBzaG91bGQgaW5jbHVkZSB0aGUgc2VyaWFsIG51bWJlciBhbmQgcmV2b2NhdGlvbiBkYXRlLiBJZiB0aGUgcmV2b2NhdGlvbiBkYXRlIGlzIGVtcHR5LCBpdCB3aWxsIGJlIHNldCB0byB0aGUgY3VycmVudCBkYXRlLidcbiAgKTtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIGNybFNlcnZpY2U6IENybFNlcnZpY2UsXG4gICAgcHJpdmF0ZSBhbGVydFNlcnZpY2U6IEFsZXJ0U2VydmljZSxcbiAgICBwcml2YXRlIG1vZGFsU2VydmljZTogTW9kYWxTZXJ2aWNlXG4gICkge31cblxuICBhc3luYyBkb3dubG9hZENybCgpIHtcbiAgICBsZXQgYXJyYXlCdWZmZXI6IEFycmF5QnVmZmVyO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCByZXMgPSBhd2FpdCB0aGlzLmNybFNlcnZpY2UuZG93bmxvYWRDcmxGaWxlKCk7XG4gICAgICBhcnJheUJ1ZmZlciA9IGF3YWl0IHJlcy5hcnJheUJ1ZmZlcigpO1xuICAgICAgY29uc3QgZmlsZUJpbmFyeSA9IG5ldyBGaWxlKFthcnJheUJ1ZmZlcl0sICdjdW11bG9jaXR5LmNybCcsIHtcbiAgICAgICAgdHlwZTogJ2FwcGxpY2F0aW9uL3BraXgtY3JsJ1xuICAgICAgfSk7XG4gICAgICBzYXZlQXMoZmlsZUJpbmFyeSk7XG4gICAgfSBjYXRjaCAoZXgpIHtcbiAgICAgIHRoaXMuYWxlcnRTZXJ2aWNlLmFkZFNlcnZlckZhaWx1cmUoZXgpO1xuICAgIH1cbiAgfVxuXG4gIGRvd25sb2FkQ3JsVGVtcGxhdGUoKSB7XG4gICAgY29uc3QgYmxvYiA9IG5ldyBCbG9iKFtgU0VSSUFMTk8sREFURVxcblNFUklBTF9OTzEsJHtuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCl9YF0sIHtcbiAgICAgIHR5cGU6ICd0ZXh0L2NzdjtjaGFyc2V0PXV0Zi04OydcbiAgICB9KTtcbiAgICBjb25zdCBmaWxlTmFtZSA9ICdjcmwtdGVtcGxhdGUnO1xuICAgIHNhdmVBcyhibG9iLCBgJHtmaWxlTmFtZX0uY3N2YCk7XG4gIH1cblxuICBhZGRDZXJ0aWZpY2F0ZVJldm9jYXRpb24oKSB7XG4gICAgdGhpcy5jcmxzLnB1c2godGhpcy5nZXRFbXB0eUNlcnRpZmljYXRlUmV2b2NhdGlvbigpKTtcbiAgfVxuXG4gIHJlbW92ZUNlcnRpZmljYXRlUmV2b2NhdGlvbihjZXJ0aWZpY2F0ZVJldm9jYXRpb24pIHtcbiAgICBwdWxsKHRoaXMuY3JscywgY2VydGlmaWNhdGVSZXZvY2F0aW9uKTtcbiAgICBpZiAodGhpcy5jcmxzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgdGhpcy5hZGRDZXJ0aWZpY2F0ZVJldm9jYXRpb24oKTtcbiAgICB9XG4gIH1cblxuICBhc3luYyBzYXZlKCkge1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCB0aGlzLmNvbmZpcm0oKTtcbiAgICAgIGF3YWl0IHRoaXMuc2F2ZUNybHMoKTtcbiAgICAgIGF3YWl0IHRoaXMudXBsb2FkQ3JsRmlsZSgpO1xuICAgICAgYXdhaXQgdGhpcy5jbGVhclNhdmVkRGF0YSgpO1xuICAgICAgdGhpcy5hbGVydFNlcnZpY2Uuc3VjY2VzcyhnZXR0ZXh0KCdDUkwgc2F2ZWQuJykpO1xuICAgIH0gY2F0Y2ggKGVyKSB7XG4gICAgICB0aGlzLmFsZXJ0U2VydmljZS5hZGRTZXJ2ZXJGYWlsdXJlKGVyKTtcbiAgICB9XG4gIH1cblxuICBnZXQgaXNMaXN0RW1wdHkoKSB7XG4gICAgcmV0dXJuIHRoaXMuY3Jscy5sZW5ndGggPT09IDEgJiYgaXNFbXB0eSh0aGlzLmNybHNbMF0uc2VyaWFsTnVtYmVySW5IZXgpO1xuICB9XG5cbiAgZ2V0IGlzRmlsZURyb3BwZWQoKSB7XG4gICAgcmV0dXJuICEhKHRoaXMuZHJvcHBlZEZpbGVzICYmIHRoaXMuZHJvcHBlZEZpbGVzWzBdKTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgY29uZmlybSgpIHtcbiAgICBjb25zdCBpc092ZXJyaWRlTWFudWFsRW50cmllcyA9ICF0aGlzLmlzTGlzdEVtcHR5ICYmIHRoaXMuaXNGaWxlRHJvcHBlZDtcbiAgICBjb25zdCBzdGF0dXMgPSBpc092ZXJyaWRlTWFudWFsRW50cmllcyA/IFN0YXR1cy5EQU5HRVIgOiBTdGF0dXMuV0FSTklORztcbiAgICBjb25zdCBib2R5ID0gaXNPdmVycmlkZU1hbnVhbEVudHJpZXNcbiAgICAgID8gZ2V0dGV4dChcbiAgICAgICAgICAnWW91IGFyZSBhYm91dCB0byB1cGRhdGUgdGhlIENSTC4gVGhlIENSTCBmaWxlIGNvbnRlbnQgd2lsbCBvdmVycmlkZSBtYW51YWwgZW50cmllcy4gRG8geW91IHdhbnQgdG8gcHJvY2VlZD8nXG4gICAgICAgIClcbiAgICAgIDogZ2V0dGV4dCgnWW91IGFyZSBhYm91dCB0byB1cGRhdGUgdGhlIENSTC4gRG8geW91IHdhbnQgdG8gcHJvY2VlZD8nKTtcblxuICAgIGF3YWl0IHRoaXMubW9kYWxTZXJ2aWNlLmNvbmZpcm0oZ2V0dGV4dCgnVXBkYXRlIHRoZSBDUkwnKSwgYm9keSwgc3RhdHVzLCB7XG4gICAgICBvazogZ2V0dGV4dCgnVXBkYXRlJylcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgY2xlYXJTYXZlZERhdGEoKSB7XG4gICAgdGhpcy5kcm9wcGVkRmlsZXMgPSBbXTtcbiAgICB0aGlzLmNybHMgPSBbdGhpcy5nZXRFbXB0eUNlcnRpZmljYXRlUmV2b2NhdGlvbigpXTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgc2F2ZUNybHMoKSB7XG4gICAgaWYgKCF0aGlzLmlzTGlzdEVtcHR5KSB7XG4gICAgICBhd2FpdCB0aGlzLmNybFNlcnZpY2UudXBsb2FkQ3Jscyh0aGlzLmNybHMpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgdXBsb2FkQ3JsRmlsZSgpIHtcbiAgICBpZiAodGhpcy5pc0ZpbGVEcm9wcGVkKSB7XG4gICAgICBhd2FpdCB0aGlzLmNybFNlcnZpY2UudXBsb2FkQ3JsRmlsZSh0aGlzLmRyb3BwZWRGaWxlc1swXS5maWxlKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGdldEVtcHR5Q2VydGlmaWNhdGVSZXZvY2F0aW9uKCk6IElDZXJ0aWZpY2F0ZVJldm9jYXRpb24ge1xuICAgIHJldHVybiB7XG4gICAgICBzZXJpYWxOdW1iZXJJbkhleDogJydcbiAgICB9O1xuICB9XG59XG4iLCI8Yzh5LXRpdGxlPnt7ICdUcnVzdGVkIGNlcnRpZmljYXRlcycgfCB0cmFuc2xhdGUgfX08L2M4eS10aXRsZT5cblxuPGM4eS1icmVhZGNydW1iPlxuICA8Yzh5LWJyZWFkY3J1bWItaXRlbVxuICAgIGljb249XCJjOHktbWFuYWdlbWVudFwiXG4gICAgbGFiZWw9XCJ7eyAnTWFuYWdlbWVudCcgfCB0cmFuc2xhdGUgfX1cIlxuICA+PC9jOHktYnJlYWRjcnVtYi1pdGVtPlxuICA8Yzh5LWJyZWFkY3J1bWItaXRlbVxuICAgIGljb249XCJjZXJ0aWZpY2F0ZVwiXG4gICAgbGFiZWw9XCJ7eyAnVHJ1c3RlZCBjZXJ0aWZpY2F0ZXMnIHwgdHJhbnNsYXRlIH19XCJcbiAgPjwvYzh5LWJyZWFkY3J1bWItaXRlbT5cbjwvYzh5LWJyZWFkY3J1bWI+XG5cbjxjOHktY3JsLWNoZWNrLXNldHRpbmdzPjwvYzh5LWNybC1jaGVjay1zZXR0aW5ncz5cblxuPGM4eS1oZWxwXG4gIHNyYz1cIi9kb2NzL2RldmljZS1tYW5hZ2VtZW50LWFwcGxpY2F0aW9uL21hbmFnaW5nLWRldmljZS1kYXRhLyNtYW5hZ2luZy10cnVzdGVkLWNlcnRpZmljYXRlLXNldHRpbmdzXCJcbiAgW2ljb25dPVwiJ3NldHRpbmdzJ1wiXG4+PC9jOHktaGVscD5cblxuPGZvcm1cbiAgY2xhc3M9XCJjYXJkIGNhcmQtLWZ1bGxwYWdlXCJcbiAgI2NybE1hbnVhbEZvcm09XCJuZ0Zvcm1cIlxuICBub3ZhbGlkYXRlXG4+XG4gIDxkaXYgY2xhc3M9XCJjYXJkLWhlYWRlciBzZXBhcmF0b3JcIj5cbiAgICA8ZGl2IGNsYXNzPVwiY2FyZC10aXRsZVwiPlxuICAgICAge3sgJ09mZmxpbmUgY29uZmlndXJhdGlvbiBvZiBDZXJ0aWZpY2F0ZSBSZXZvY2F0aW9uIExpc3QgKENSTCknIHwgdHJhbnNsYXRlIH19XG4gICAgPC9kaXY+XG4gIDwvZGl2PlxuICA8ZGl2IGNsYXNzPVwiaW5uZXItc2Nyb2xsXCI+XG4gICAgPGRpdiBjbGFzcz1cImNhcmQtYmxvY2tcIj5cbiAgICAgIDxkaXZcbiAgICAgICAgY2xhc3M9XCJsZWdlbmQgZm9ybS1ibG9ja1wiXG4gICAgICA+XG4gICAgICAgIHt7ICdNYW51YWwnIHwgdHJhbnNsYXRlIH19XG4gICAgICAgIDxidXR0b25cbiAgICAgICAgICBjbGFzcz1cImJ0bi1oZWxwIGJ0bi1oZWxwLS1zbVwiXG4gICAgICAgICAgW2F0dHIuYXJpYS1sYWJlbF09XCInSGVscCcgfCB0cmFuc2xhdGVcIlxuICAgICAgICAgIHBvcG92ZXI9XCJ7eyBNQU5VQUxfRU5UUllfUE9QT1ZFUiB8IHRyYW5zbGF0ZSB9fVwiXG4gICAgICAgICAgcGxhY2VtZW50PVwicmlnaHRcIlxuICAgICAgICAgIHRyaWdnZXJzPVwiZm9jdXNcIlxuICAgICAgICAgIGNvbnRhaW5lcj1cImJvZHlcIlxuICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICA+PC9idXR0b24+XG4gICAgICA8L2Rpdj5cblxuICAgICAgPGRpdlxuICAgICAgICBjbGFzcz1cInJvdyB0aWdodC1ncmlkXCJcbiAgICAgICAgZGF0YS1jeT1cImM4eS1yb3ctZ3JpZC0tbmV3LXJvd1wiXG4gICAgICAgICpuZ0Zvcj1cImxldCBjZXJ0aWZpY2F0ZVJldm9jYXRpb24gb2YgY3JsczsgaW5kZXggYXMgaW5kZXg7IGxhc3QgYXMgaXNMYXN0XCJcbiAgICAgID5cbiAgICAgICAgPGRpdiBjbGFzcz1cImNvbC1zbS01XCI+XG4gICAgICAgICAgPGM4eS1mb3JtLWdyb3VwPlxuICAgICAgICAgICAgPGxhYmVsXG4gICAgICAgICAgICAgIFtmb3JdPVwiJ3NlcmlhbE51bWJlcicgKyBpbmRleFwiXG4gICAgICAgICAgICAgIHRyYW5zbGF0ZVxuICAgICAgICAgICAgPlxuICAgICAgICAgICAgICBTZXJpYWwgbnVtYmVyXG4gICAgICAgICAgICA8L2xhYmVsPlxuICAgICAgICAgICAgPGlucHV0XG4gICAgICAgICAgICAgIGNsYXNzPVwiZm9ybS1jb250cm9sXCJcbiAgICAgICAgICAgICAgcmVxdWlyZWRcbiAgICAgICAgICAgICAgW25hbWVdPVwiJ3NlcmlhbE51bWJlcicgKyBpbmRleFwiXG4gICAgICAgICAgICAgIFtpZF09XCInc2VyaWFsTnVtYmVyJyArIGluZGV4XCJcbiAgICAgICAgICAgICAgWyhuZ01vZGVsKV09XCJjZXJ0aWZpY2F0ZVJldm9jYXRpb24uc2VyaWFsTnVtYmVySW5IZXhcIlxuICAgICAgICAgICAgICBbcGxhY2Vob2xkZXJdPVwiJ2UuZy4ge3sgZXhhbXBsZSB9fScgfCB0cmFuc2xhdGU6IHsgZXhhbXBsZTogJzhhYjM0ZmU1NDc2JyB9XCJcbiAgICAgICAgICAgICAgYzh5RGVmYXVsdFZhbGlkYXRpb249XCJjb2xvbmVkSGV4TnVtYmVyXCJcbiAgICAgICAgICAgIC8+XG4gICAgICAgICAgPC9jOHktZm9ybS1ncm91cD5cbiAgICAgICAgPC9kaXY+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJjb2wtc20tNVwiPlxuICAgICAgICAgIDxjOHktZm9ybS1ncm91cCBjbGFzcz1cImRhdGVwaWNrZXJcIj5cbiAgICAgICAgICAgIDxsYWJlbCBbZm9yXT1cIid2YWxpZFRpbGxQaWNrZXInICsgaW5kZXhcIj5cbiAgICAgICAgICAgICAge3sgJ1ZhbGlkIHRpbGwnIHwgdHJhbnNsYXRlIH19XG4gICAgICAgICAgICA8L2xhYmVsPlxuICAgICAgICAgICAgPGlucHV0XG4gICAgICAgICAgICAgIGNsYXNzPVwiZm9ybS1jb250cm9sXCJcbiAgICAgICAgICAgICAgW2F0dHIuYXJpYS1sYWJlbF09XCInRGF0ZSB0bycgfCB0cmFuc2xhdGVcIlxuICAgICAgICAgICAgICBwbGFjZWhvbGRlcj1cInt7ICdEYXRlIHRvJyB8IHRyYW5zbGF0ZSB9fVwiXG4gICAgICAgICAgICAgIFtuYW1lXT1cIid2YWxpZFRpbGxQaWNrZXInICsgaW5kZXhcIlxuICAgICAgICAgICAgICBbaWRdPVwiJ3ZhbGlkVGlsbFBpY2tlcicgKyBpbmRleFwiXG4gICAgICAgICAgICAgIFsobmdNb2RlbCldPVwiY2VydGlmaWNhdGVSZXZvY2F0aW9uLnJldm9jYXRpb25EYXRlXCJcbiAgICAgICAgICAgICAgW2JzQ29uZmlnXT1cInsgY3VzdG9tVG9kYXlDbGFzczogJ3RvZGF5JyB9XCJcbiAgICAgICAgICAgICAgYnNEYXRlcGlja2VyXG4gICAgICAgICAgICAgIFttYXhEYXRlXT1cInRvZGF5XCJcbiAgICAgICAgICAgIC8+XG4gICAgICAgICAgPC9jOHktZm9ybS1ncm91cD5cbiAgICAgICAgPC9kaXY+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJjb2wtc20tMlwiPlxuICAgICAgICAgIDxjOHktZm9ybS1ncm91cD5cbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJwLXQtMjRcIj5cbiAgICAgICAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICAgICAgICpuZ0lmPVwiY3Jscy5sZW5ndGggPiAxXCJcbiAgICAgICAgICAgICAgICBjbGFzcz1cImJ0biBidG4tbGluayBoaWRkZW4teHMgaGlkZGVuLXNtXCJcbiAgICAgICAgICAgICAgICB0aXRsZT1cInt7ICdSZW1vdmUnIHwgdHJhbnNsYXRlIH19XCJcbiAgICAgICAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAgICAgICAoY2xpY2spPVwicmVtb3ZlQ2VydGlmaWNhdGVSZXZvY2F0aW9uKGNlcnRpZmljYXRlUmV2b2NhdGlvbilcIlxuICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgPGlcbiAgICAgICAgICAgICAgICAgIGNsYXNzPVwidGV4dC1kYW5nZXJcIlxuICAgICAgICAgICAgICAgICAgYzh5SWNvbj1cIm1pbnVzLWNpcmNsZVwiXG4gICAgICAgICAgICAgICAgPjwvaT5cbiAgICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICAgIDxidXR0b25cbiAgICAgICAgICAgICAgICBjbGFzcz1cImJ0biBidG4tbGluayBoaWRkZW4teHMgaGlkZGVuLXNtXCJcbiAgICAgICAgICAgICAgICB0aXRsZT1cInt7ICdBZGQnIHwgdHJhbnNsYXRlIH19XCJcbiAgICAgICAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAgICAgICAoY2xpY2spPVwiYWRkQ2VydGlmaWNhdGVSZXZvY2F0aW9uKClcIlxuICAgICAgICAgICAgICAgICpuZ0lmPVwiaXNMYXN0XCJcbiAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgIDxpXG4gICAgICAgICAgICAgICAgICBjbGFzcz1cInRleHQtcHJpbWFyeVwiXG4gICAgICAgICAgICAgICAgICBjOHlJY29uPVwicGx1cy1jaXJjbGVcIlxuICAgICAgICAgICAgICAgID48L2k+XG4gICAgICAgICAgICAgIDwvYnV0dG9uPlxuXG4gICAgICAgICAgICAgIDxidXR0b25cbiAgICAgICAgICAgICAgICAqbmdJZj1cImNybHMubGVuZ3RoID4gMVwiXG4gICAgICAgICAgICAgICAgY2xhc3M9XCJidG4gYnRuLWRhbmdlciBidG4tYmxvY2sgYnRuLXNtIHZpc2libGUteHMgdmlzaWJsZS1zbVwiXG4gICAgICAgICAgICAgICAgdGl0bGU9XCJ7eyAnUmVtb3ZlJyB8IHRyYW5zbGF0ZSB9fVwiXG4gICAgICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICAgICAgKGNsaWNrKT1cInJlbW92ZUNlcnRpZmljYXRlUmV2b2NhdGlvbihjZXJ0aWZpY2F0ZVJldm9jYXRpb24pXCJcbiAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgIDxpIGM4eUljb249XCJtaW51cy1jaXJjbGVcIj48L2k+XG4gICAgICAgICAgICAgICAge3sgJ1JlbW92ZScgfCB0cmFuc2xhdGUgfX1cbiAgICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICAgIDxidXR0b25cbiAgICAgICAgICAgICAgICBjbGFzcz1cImJ0biBidG4tcHJpbWFyeSBidG4tYmxvY2sgYnRuLXNtIHZpc2libGUteHMgdmlzaWJsZS1zbVwiXG4gICAgICAgICAgICAgICAgdGl0bGU9XCJ7eyAnQWRkJyB8IHRyYW5zbGF0ZSB9fVwiXG4gICAgICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICAgICAgKGNsaWNrKT1cImFkZENlcnRpZmljYXRlUmV2b2NhdGlvbigpXCJcbiAgICAgICAgICAgICAgICAqbmdJZj1cImlzTGFzdFwiXG4gICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICA8aSBjOHlJY29uPVwicGx1cy1jaXJjbGVcIj48L2k+XG4gICAgICAgICAgICAgICAge3sgJ0FkZCcgfCB0cmFuc2xhdGUgfX1cbiAgICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICA8L2M4eS1mb3JtLWdyb3VwPlxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvZGl2PlxuICAgICAgPGRpdiBjbGFzcz1cImxlZ2VuZCBmb3JtLWJsb2NrIGNlbnRlclwiPlxuICAgICAgICB7eyAnb3InIHwgdHJhbnNsYXRlIH19XG4gICAgICA8L2Rpdj5cbiAgICAgIDxkaXZcbiAgICAgICAgY2xhc3M9XCJsZWdlbmQgZm9ybS1ibG9ja1wiXG4gICAgICA+XG4gICAgICAgIHt7ICdGaWxlIHVwbG9hZCcgfCB0cmFuc2xhdGUgfX1cbiAgICAgICAgPGJ1dHRvblxuICAgICAgICAgIGNsYXNzPVwiYnRuLWhlbHAgYnRuLWhlbHAtLXNtXCJcbiAgICAgICAgICBbYXR0ci5hcmlhLWxhYmVsXT1cIidIZWxwJyB8IHRyYW5zbGF0ZVwiXG4gICAgICAgICAgcG9wb3Zlcj1cInt7IEZJTEVfVVBMT0FEX1BPUE9WRVIgfCB0cmFuc2xhdGUgfX1cIlxuICAgICAgICAgIHBsYWNlbWVudD1cInJpZ2h0XCJcbiAgICAgICAgICB0cmlnZ2Vycz1cImZvY3VzXCJcbiAgICAgICAgICBjb250YWluZXI9XCJib2R5XCJcbiAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgPjwvYnV0dG9uPlxuICAgICAgPC9kaXY+XG5cbiAgICAgIDxjOHktZHJvcC1hcmVhXG4gICAgICAgIGNsYXNzPVwiZHJvcC1hcmVhLXNtXCJcbiAgICAgICAgW3RpdGxlXT1cIidVcGxvYWQgQ1JMIGZpbGUgKC5jc3YgZm9ybWF0KScgfCB0cmFuc2xhdGVcIlxuICAgICAgICBuYW1lPVwidXBsb2FkQ3JsRHJvcEFyZWFcIlxuICAgICAgICBbKG5nTW9kZWwpXT1cImRyb3BwZWRGaWxlc1wiXG4gICAgICAgIFttZXNzYWdlXT1cIidVcGxvYWQgQ1JMIGZpbGUgKC5jc3YgZm9ybWF0KScgfCB0cmFuc2xhdGVcIlxuICAgICAgICBbbG9hZGluZ01lc3NhZ2VdPVwiJ1VwbG9hZGluZ+KApicgfCB0cmFuc2xhdGVcIlxuICAgICAgICBbYWNjZXB0XT1cIicuY3N2J1wiXG4gICAgICAgIFttYXhBbGxvd2VkRmlsZXNdPVwiMVwiXG4gICAgICA+PC9jOHktZHJvcC1hcmVhPlxuICAgICAgPHAgY2xhc3M9XCJoZWxwLWJsb2NrIGhhcy1pbmZvIHRleHQtbXV0ZWQgbS10LTRcIj5cbiAgICAgICAge3sgJ0NSTCBmaWxlIGNvbnRlbnQgd2lsbCBvdmVycmlkZSBtYW51YWwgZW50cmllcy4nIHwgdHJhbnNsYXRlIH19XG4gICAgICA8L3A+XG5cbiAgICAgIDxidXR0b25cbiAgICAgICAgY2xhc3M9XCJidG4gYnRuLXNtIGJ0bi1kZWZhdWx0IG0tdC0xNlwiXG4gICAgICAgIHRpdGxlPVwie3sgJ0Rvd25sb2FkIHRlbXBsYXRlJyB8IHRyYW5zbGF0ZSB9fVwiXG4gICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAoY2xpY2spPVwiZG93bmxvYWRDcmxUZW1wbGF0ZSgpXCJcbiAgICAgID5cbiAgICAgICAgPGlcbiAgICAgICAgICBjbGFzcz1cIm0tci00XCJcbiAgICAgICAgICBjOHlJY29uPVwidGVtcGxhdGVcIlxuICAgICAgICA+PC9pPlxuICAgICAgICB7eyAnRG93bmxvYWQgdGVtcGxhdGUnIHwgdHJhbnNsYXRlIH19XG4gICAgICA8L2J1dHRvbj5cbiAgICA8L2Rpdj5cbiAgPC9kaXY+XG4gIDxkaXYgY2xhc3M9XCJjYXJkLWZvb3RlciBzZXBhcmF0b3JcIj5cbiAgICA8YnV0dG9uXG4gICAgICBjbGFzcz1cImJ0biBidG4tZGVmYXVsdFwiXG4gICAgICB0aXRsZT1cInt7ICdEb3dubG9hZCBDUkwgZmlsZScgfCB0cmFuc2xhdGUgfX1cIlxuICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAoY2xpY2spPVwiZG93bmxvYWRDcmwoKVwiXG4gICAgPlxuICAgICAgPGlcbiAgICAgICAgY2xhc3M9XCJtLXItNFwiXG4gICAgICAgIGM4eUljb249XCJkb3dubG9hZFwiXG4gICAgICA+PC9pPlxuICAgICAge3sgJ0Rvd25sb2FkIENSTCBmaWxlJyB8IHRyYW5zbGF0ZSB9fVxuICAgIDwvYnV0dG9uPlxuICAgIDxidXR0b25cbiAgICAgIGNsYXNzPVwiYnRuIGJ0bi1wcmltYXJ5XCJcbiAgICAgIHRpdGxlPVwie3sgJ1NhdmUnIHwgdHJhbnNsYXRlIH19XCJcbiAgICAgIHR5cGU9XCJzdWJtaXRcIlxuICAgICAgKGNsaWNrKT1cInNhdmUoKVwiXG4gICAgICBbZGlzYWJsZWRdPVwiIShjcmxNYW51YWxGb3JtLmZvcm0udmFsaWQgfHwgaXNMaXN0RW1wdHkpIHx8IChpc0xpc3RFbXB0eSAmJiAhaXNGaWxlRHJvcHBlZClcIlxuICAgID5cbiAgICAgIHt7ICdTYXZlJyB8IHRyYW5zbGF0ZSB9fVxuICAgIDwvYnV0dG9uPlxuICA8L2Rpdj5cbjwvZm9ybT5cbiJdfQ==