UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

137 lines 39.8 kB
import { Component } from '@angular/core'; import { FormGroup, ReactiveFormsModule, FormControl } from '@angular/forms'; import { CommonModule } from '@angular/common'; import { ApplicationService, TenantService } from '@c8y/client'; import { ActivatedRoute, RouterLink } from '@angular/router'; import { AlertService, CoreModule, FormsModule, gettext, GainsightService, OptionsService } from '@c8y/ngx-components'; import { tenantLimitsCustomProperties } from './tenant-limits-definitions'; import { PRODUCT_EXPERIENCE_TENANT_MANAGEMENT } from '../tenants.model'; import { take } from 'rxjs/operators'; import * as i0 from "@angular/core"; import * as i1 from "@c8y/client"; import * as i2 from "@c8y/ngx-components"; import * as i3 from "@angular/router"; import * as i4 from "@angular/common"; import * as i5 from "@angular/forms"; const { ACTIONS, EVENTS, COMPONENTS, RESULTS } = PRODUCT_EXPERIENCE_TENANT_MANAGEMENT; export class TenantLimitsComponent { constructor(tenantService, alertService, activatedRoute, applicationService, options, gainsightService) { this.tenantService = tenantService; this.alertService = alertService; this.activatedRoute = activatedRoute; this.applicationService = applicationService; this.options = options; this.gainsightService = gainsightService; this.fieldDefinitions = { ...tenantLimitsCustomProperties }; this.limitsForm = new FormGroup({}); this.tenant = null; this.initialized = false; this.deviceStorageLimitInfoMessage = gettext('Default: 0 (unlimited storage)'); } async ngOnInit() { await this.loadTenantDetails(); await this.setupConditionalFields(); this.generateForm(); this.initialized = true; this.sendGainsightEvent(ACTIONS.TENANT_LIMITS_OPENED); } async onSubmit() { if (this.limitsForm.invalid || !this.tenant) { return; } const { deviceStorageLimitMiB, ...customProperties } = this.getDirtyValues(); const updatedTenant = { ...this.tenant, customProperties: { ...this.tenant.customProperties, ...customProperties } }; if (this.storageLimitFeatureEnabled) { updatedTenant.storageLimitPerDevice = (deviceStorageLimitMiB || 0) * 1024 * 1024; } else { updatedTenant.storageLimitPerDevice = 0; } try { await this.tenantService.update(updatedTenant); this.alertService.success(gettext('Limit values saved.')); this.sendGainsightEvent(ACTIONS.TENANT_LIMITS_SAVED, { result: RESULTS.SUCCESS }); } catch (error) { this.alertService.addServerFailure(error); this.sendGainsightEvent(ACTIONS.TENANT_LIMITS_SAVED, { result: RESULTS.FAILURE, error }); } } async loadTenantDetails() { try { const result = await this.tenantService.detail(this.activatedRoute.snapshot.parent.data.contextData.id); this.tenant = result.data; } catch (error) { this.alertService.addServerFailure(error); } } async setupConditionalFields() { try { const apps = (await this.applicationService.listByUser(undefined, { dropOverwrittenApps: true, noPaging: true })).data; const cepModuleEnabled = apps.some(app => app.name === 'cep' || app.contextPath === 'cep'); const dataBrokerModuleEnabled = apps.some(app => app.name === 'feature-broker' || app.contextPath === 'feature-broker'); this.storageLimitFeatureEnabled = this.options.storageLimitationFeatureEnabled; if (!cepModuleEnabled) { delete this.fieldDefinitions.cepServerQueueLimit; } if (!dataBrokerModuleEnabled) { delete this.fieldDefinitions.dataBrokerQueueLimit; } if (!this.storageLimitFeatureEnabled) { delete this.fieldDefinitions.deviceStorageLimitMiB; } this.fieldKeys = Object.keys(this.fieldDefinitions); } catch (ex) { this.alertService.addServerFailure(ex); } } generateForm() { for (const field of Object.values(tenantLimitsCustomProperties)) { this.limitsForm.addControl(field.id, new FormControl(field.defaultValue, field.validators)); } const customProps = this.tenant?.customProperties || {}; this.limitsForm.patchValue(customProps); if (this.storageLimitFeatureEnabled) { const deviceStorageLimitMiB = (this.tenant?.storageLimitPerDevice || 0) / 1024 / 1024; this.limitsForm.patchValue({ deviceStorageLimitMiB }); } this.limitsForm.valueChanges.pipe(take(1)).subscribe(() => { this.sendGainsightEvent(ACTIONS.TENANT_LIMITS_STARTED_CHANGING); }); } getDirtyValues() { const dirtyValues = {}; Object.keys(this.limitsForm.controls).forEach(key => { const control = this.limitsForm.controls[key]; if (control && control.dirty) { dirtyValues[key] = control.value; } }); return dirtyValues; } sendGainsightEvent(action, props = {}) { this.gainsightService.triggerEvent(EVENTS.TENANT_MANAGEMENT, { component: COMPONENTS.TENANT_LIMITS, action, ...props }); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TenantLimitsComponent, deps: [{ token: i1.TenantService }, { token: i2.AlertService }, { token: i3.ActivatedRoute }, { token: i1.ApplicationService }, { token: i2.OptionsService }, { token: i2.GainsightService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: TenantLimitsComponent, isStandalone: true, selector: "c8y-tenant-limits", ngImport: i0, template: "<c8y-title *ngIf=\"tenant\">\n {{ tenant.company }}\n</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Tenants' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Subtenants' | translate\"\n [path]=\"'/tenants'\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<form\n [formGroup]=\"limitsForm\"\n (ngSubmit)=\"onSubmit()\"\n>\n <div class=\"card card--fullpage m-b-0\">\n <div class=\"card-header separator\">\n <div\n class=\"card-title\"\n translate\n >\n Limits\n </div>\n </div>\n\n <c8y-help src=\"/docs/enterprise-tenant/managing-tenants/#setting-limits\"></c8y-help>\n\n <div class=\"inner-scroll\">\n <div\n class=\"card-block\"\n *ngIf=\"!initialized\"\n >\n <c8y-loading></c8y-loading>\n </div>\n\n <div\n class=\"card-block\"\n *ngIf=\"initialized\"\n >\n <ng-container *ngFor=\"let key of fieldKeys\">\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'text'\">\n <ng-container\n *ngTemplateOutlet=\"textField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'number'\">\n <ng-container\n *ngTemplateOutlet=\"numberField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'checkbox'\">\n <ng-container\n *ngTemplateOutlet=\"checkboxField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n </ng-container>\n </div>\n </div>\n\n <div\n class=\"card-footer separator\"\n *ngIf=\"initialized\"\n >\n <button\n class=\"btn btn-default\"\n type=\"button\"\n [routerLink]=\"['/tenants']\"\n translate\n >\n Cancel\n </button>\n <button\n class=\"btn btn-primary\"\n type=\"submit\"\n [disabled]=\"limitsForm.invalid\"\n translate\n >\n Save\n </button>\n </div>\n </div>\n\n <ng-template\n #textField\n let-fieldDefinition\n >\n <c8y-form-group>\n <label [for]=\"fieldDefinition.id\">\n {{ fieldDefinition.label | translate: fieldDefinition.labelArgs }}\n </label>\n <input\n class=\"form-control\"\n type=\"text\"\n [id]=\"fieldDefinition.id\"\n [placeholder]=\"fieldDefinition.placeholder | translate: fieldDefinition.placeholderArgs\"\n [formControlName]=\"fieldDefinition.id\"\n />\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #numberField\n let-fieldDefinition\n >\n <c8y-form-group>\n <label [for]=\"fieldDefinition.id\">\n {{ fieldDefinition.label | translate: fieldDefinition.labelArgs }}\n </label>\n <input\n class=\"form-control\"\n type=\"number\"\n [id]=\"fieldDefinition.id\"\n [placeholder]=\"fieldDefinition.placeholder | translate: fieldDefinition.placeholderArgs\"\n [formControlName]=\"fieldDefinition.id\"\n />\n <ng-container\n *ngIf=\"\n fieldDefinitions.deviceStorageLimitMiB &&\n fieldDefinitions.deviceStorageLimitMiB.id === fieldDefinition.id &&\n limitsForm.controls[fieldDefinition.id] as control\n \"\n >\n <div\n class=\"icon-flex\"\n *ngIf=\"!control.dirty || (control.dirty && !control.errors)\"\n >\n <p class=\"help-block\">\n <i\n class=\"text-info m-r-4\"\n c8yIcon=\"info-circle\"\n ></i>\n <span>{{ deviceStorageLimitInfoMessage | translate }}</span>\n </p>\n </div>\n </ng-container>\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #checkboxField\n let-fieldDefinition\n >\n <c8y-form-group>\n <label\n class=\"c8y-checkbox\"\n [title]=\"fieldDefinition.label | translate\"\n [for]=\"fieldDefinition.id\"\n >\n <input\n type=\"checkbox\"\n [id]=\"fieldDefinition.id\"\n [formControlName]=\"fieldDefinition.id\"\n />\n <span></span>\n <span>{{ fieldDefinition.label | translate }}</span>\n </label>\n </c8y-form-group>\n </ng-template>\n</form>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i5.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { 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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i5.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i5.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i5.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i5.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: FormsModule }, { 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: "ngmodule", type: CoreModule }, { 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: "pipe", type: i2.C8yTranslatePipe, name: "translate" }, { kind: "directive", type: i2.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: i2.LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "component", type: i2.TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "component", type: i2.HelpComponent, selector: "c8y-help", inputs: ["src", "isCollapsed", "priority", "icon"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TenantLimitsComponent, decorators: [{ type: Component, args: [{ selector: 'c8y-tenant-limits', standalone: true, imports: [CommonModule, ReactiveFormsModule, FormsModule, CoreModule, RouterLink], template: "<c8y-title *ngIf=\"tenant\">\n {{ tenant.company }}\n</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Tenants' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Subtenants' | translate\"\n [path]=\"'/tenants'\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<form\n [formGroup]=\"limitsForm\"\n (ngSubmit)=\"onSubmit()\"\n>\n <div class=\"card card--fullpage m-b-0\">\n <div class=\"card-header separator\">\n <div\n class=\"card-title\"\n translate\n >\n Limits\n </div>\n </div>\n\n <c8y-help src=\"/docs/enterprise-tenant/managing-tenants/#setting-limits\"></c8y-help>\n\n <div class=\"inner-scroll\">\n <div\n class=\"card-block\"\n *ngIf=\"!initialized\"\n >\n <c8y-loading></c8y-loading>\n </div>\n\n <div\n class=\"card-block\"\n *ngIf=\"initialized\"\n >\n <ng-container *ngFor=\"let key of fieldKeys\">\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'text'\">\n <ng-container\n *ngTemplateOutlet=\"textField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'number'\">\n <ng-container\n *ngTemplateOutlet=\"numberField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'checkbox'\">\n <ng-container\n *ngTemplateOutlet=\"checkboxField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n </ng-container>\n </div>\n </div>\n\n <div\n class=\"card-footer separator\"\n *ngIf=\"initialized\"\n >\n <button\n class=\"btn btn-default\"\n type=\"button\"\n [routerLink]=\"['/tenants']\"\n translate\n >\n Cancel\n </button>\n <button\n class=\"btn btn-primary\"\n type=\"submit\"\n [disabled]=\"limitsForm.invalid\"\n translate\n >\n Save\n </button>\n </div>\n </div>\n\n <ng-template\n #textField\n let-fieldDefinition\n >\n <c8y-form-group>\n <label [for]=\"fieldDefinition.id\">\n {{ fieldDefinition.label | translate: fieldDefinition.labelArgs }}\n </label>\n <input\n class=\"form-control\"\n type=\"text\"\n [id]=\"fieldDefinition.id\"\n [placeholder]=\"fieldDefinition.placeholder | translate: fieldDefinition.placeholderArgs\"\n [formControlName]=\"fieldDefinition.id\"\n />\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #numberField\n let-fieldDefinition\n >\n <c8y-form-group>\n <label [for]=\"fieldDefinition.id\">\n {{ fieldDefinition.label | translate: fieldDefinition.labelArgs }}\n </label>\n <input\n class=\"form-control\"\n type=\"number\"\n [id]=\"fieldDefinition.id\"\n [placeholder]=\"fieldDefinition.placeholder | translate: fieldDefinition.placeholderArgs\"\n [formControlName]=\"fieldDefinition.id\"\n />\n <ng-container\n *ngIf=\"\n fieldDefinitions.deviceStorageLimitMiB &&\n fieldDefinitions.deviceStorageLimitMiB.id === fieldDefinition.id &&\n limitsForm.controls[fieldDefinition.id] as control\n \"\n >\n <div\n class=\"icon-flex\"\n *ngIf=\"!control.dirty || (control.dirty && !control.errors)\"\n >\n <p class=\"help-block\">\n <i\n class=\"text-info m-r-4\"\n c8yIcon=\"info-circle\"\n ></i>\n <span>{{ deviceStorageLimitInfoMessage | translate }}</span>\n </p>\n </div>\n </ng-container>\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #checkboxField\n let-fieldDefinition\n >\n <c8y-form-group>\n <label\n class=\"c8y-checkbox\"\n [title]=\"fieldDefinition.label | translate\"\n [for]=\"fieldDefinition.id\"\n >\n <input\n type=\"checkbox\"\n [id]=\"fieldDefinition.id\"\n [formControlName]=\"fieldDefinition.id\"\n />\n <span></span>\n <span>{{ fieldDefinition.label | translate }}</span>\n </label>\n </c8y-form-group>\n </ng-template>\n</form>\n" }] }], ctorParameters: () => [{ type: i1.TenantService }, { type: i2.AlertService }, { type: i3.ActivatedRoute }, { type: i1.ApplicationService }, { type: i2.OptionsService }, { type: i2.GainsightService }] }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVuYW50LWxpbWl0cy5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi90ZW5hbnRzL3RlbmFudC1saW1pdHMvdGVuYW50LWxpbWl0cy5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi90ZW5hbnRzL3RlbmFudC1saW1pdHMvdGVuYW50LWxpbWl0cy5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFVLE1BQU0sZUFBZSxDQUFDO0FBQ2xELE9BQU8sRUFBRSxTQUFTLEVBQUUsbUJBQW1CLEVBQUUsV0FBVyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDN0UsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQy9DLE9BQU8sRUFBRSxrQkFBa0IsRUFBb0IsYUFBYSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ2xGLE9BQU8sRUFBRSxjQUFjLEVBQUUsVUFBVSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDN0QsT0FBTyxFQUNMLFlBQVksRUFDWixVQUFVLEVBQ1YsV0FBVyxFQUNYLE9BQU8sRUFDUCxnQkFBZ0IsRUFDaEIsY0FBYyxFQUNmLE1BQU0scUJBQXFCLENBQUM7QUFDN0IsT0FBTyxFQUNMLDRCQUE0QixFQUU3QixNQUFNLDZCQUE2QixDQUFDO0FBQ3JDLE9BQU8sRUFFTCxvQ0FBb0MsRUFFckMsTUFBTSxrQkFBa0IsQ0FBQztBQUMxQixPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7Ozs7Ozs7QUFFdEMsTUFBTSxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLE9BQU8sRUFBRSxHQUFHLG9DQUFvQyxDQUFDO0FBUXRGLE1BQU0sT0FBTyxxQkFBcUI7SUFTaEMsWUFDVSxhQUE0QixFQUM1QixZQUEwQixFQUMxQixjQUE4QixFQUM5QixrQkFBc0MsRUFDdEMsT0FBdUIsRUFDdkIsZ0JBQWtDO1FBTGxD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QyxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxFQUFFO1lBQ2xELE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQzlDLElBQUksT0FBTyxJQUFJLE9BQU8sQ0FBQyxLQUFLLEVBQUUsQ0FBQztnQkFDN0IsV0FBVyxDQUFDLEdBQUcsQ0FBQyxHQUFHLE9BQU8sQ0FBQyxLQUFLLENBQUM7WUFDbkMsQ0FBQztRQUNILENBQUMsQ0FBQyxDQUFDO1FBQ0gsT0FBTyxXQUFXLENBQUM7SUFDckIsQ0FBQztJQUVPLGtCQUFrQixDQUN4QixNQUFrQyxFQUNsQyxRQUFrRSxFQUFFO1FBRXBFLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLGlCQUFpQixFQUFFO1lBQzNELFNBQVMsRUFBRSxVQUFVLENBQUMsYUFBYTtZQUNuQyxNQUFNO1lBQ04sR0FBRyxLQUFLO1NBQ1QsQ0FBQyxDQUFDO0lBQ0wsQ0FBQzsrR0F4SVUscUJBQXFCO21HQUFyQixxQkFBcUIsNkVDaENsQyxtL0lBb0tBLDJDRHZJWSxZQUFZLHNhQUFFLG1CQUFtQiwreENBQUUsV0FBVyxrVEFBRSxVQUFVLG8xQkFBRSxVQUFVOzs0RkFHckUscUJBQXFCO2tCQU5qQyxTQUFTOytCQUNFLG1CQUFtQixjQUNqQixJQUFJLFdBQ1AsQ0FBQyxZQUFZLEVBQUUsbUJBQW1CLEVBQUUsV0FBVyxFQUFFLFVBQVUsRUFBRSxVQUFVLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQsIE9uSW5pdCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgRm9ybUdyb3VwLCBSZWFjdGl2ZUZvcm1zTW9kdWxlLCBGb3JtQ29udHJvbCB9IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcbmltcG9ydCB7IENvbW1vbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XG5pbXBvcnQgeyBBcHBsaWNhdGlvblNlcnZpY2UsIElSZXN1bHQsIElUZW5hbnQsIFRlbmFudFNlcnZpY2UgfSBmcm9tICdAYzh5L2NsaWVudCc7XG5pbXBvcnQgeyBBY3RpdmF0ZWRSb3V0ZSwgUm91dGVyTGluayB9IGZyb20gJ0Bhbmd1bGFyL3JvdXRlcic7XG5pbXBvcnQge1xuICBBbGVydFNlcnZpY2UsXG4gIENvcmVNb2R1bGUsXG4gIEZvcm1zTW9kdWxlLFxuICBnZXR0ZXh0LFxuICBHYWluc2lnaHRTZXJ2aWNlLFxuICBPcHRpb25zU2VydmljZVxufSBmcm9tICdAYzh5L25neC1jb21wb25lbnRzJztcbmltcG9ydCB7XG4gIHRlbmFudExpbWl0c0N1c3RvbVByb3BlcnRpZXMsXG4gIFRlbmFudExpbWl0c0N1c3RvbVByb3BlcnRpZXNLZXlzXG59IGZyb20gJy4vdGVuYW50LWxpbWl0cy1kZWZpbml0aW9ucyc7XG5pbXBvcnQge1xuICBUZW5hbnRNYW5hZ2VtZW50QWN0aW9uVHlwZSxcbiAgUFJPRFVDVF9FWFBFUklFTkNFX1RFTkFOVF9NQU5BR0VNRU5ULFxuICBUZW5hbnRNYW5hZ2VtZW50UmVzdWx0VHlwZVxufSBmcm9tICcuLi90ZW5hbnRzLm1vZGVsJztcbmltcG9ydCB7IHRha2UgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5cbmNvbnN0IHsgQUNUSU9OUywgRVZFTlRTLCBDT01QT05FTlRTLCBSRVNVTFRTIH0gPSBQUk9EVUNUX0VYUEVSSUVOQ0VfVEVOQU5UX01BTkFHRU1FTlQ7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2M4eS10ZW5hbnQtbGltaXRzJyxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgaW1wb3J0czogW0NvbW1vbk1vZHVsZSwgUmVhY3RpdmVGb3Jtc01vZHVsZSwgRm9ybXNNb2R1bGUsIENvcmVNb2R1bGUsIFJvdXRlckxpbmtdLFxuICB0ZW1wbGF0ZVVybDogJy4vdGVuYW50LWxpbWl0cy5jb21wb25lbnQuaHRtbCdcbn0pXG5leHBvcnQgY2xhc3MgVGVuYW50TGltaXRzQ29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0IHtcbiAgZmllbGREZWZpbml0aW9ucyA9IHsgLi4udGVuYW50TGltaXRzQ3VzdG9tUHJvcGVydGllcyB9O1xuICBmaWVsZEtleXM6IHN0cmluZ1tdO1xuICBzdG9yYWdlTGltaXRGZWF0dXJlRW5hYmxlZDogYm9vbGVhbjtcbiAgbGltaXRzRm9ybTogRm9ybUdyb3VwID0gbmV3IEZvcm1Hcm91cCh7fSk7XG4gIHRlbmFudDogSVRlbmFudCB8IG51bGwgPSBudWxsO1xuICBpbml0aWFsaXplZCA9IGZhbHNlO1xuICBkZXZpY2VTdG9yYWdlTGltaXRJbmZvTWVzc2FnZSA9IGdldHRleHQoJ0RlZmF1bHQ6IDAgKHVubGltaXRlZCBzdG9yYWdlKScpO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgdGVuYW50U2VydmljZTogVGVuYW50U2VydmljZSxcbiAgICBwcml2YXRlIGFsZXJ0U2VydmljZTogQWxlcnRTZXJ2aWNlLFxuICAgIHByaXZhdGUgYWN0aXZhdGVkUm91dGU6IEFjdGl2YXRlZFJvdXRlLFxuICAgIHByaXZhdGUgYXBwbGljYXRpb25TZXJ2aWNlOiBBcHBsaWNhdGlvblNlcnZpY2UsXG4gICAgcHJpdmF0ZSBvcHRpb25zOiBPcHRpb25zU2VydmljZSxcbiAgICBwcml2YXRlIGdhaW5zaWdodFNlcnZpY2U6IEdhaW5zaWdodFNlcnZpY2VcbiAgKSB7fVxuXG4gIGFzeW5jIG5nT25Jbml0KCkge1xuICAgIGF3YWl0IHRoaXMubG9hZFRlbmFudERldGFpbHMoKTtcbiAgICBhd2FpdCB0aGlzLnNldHVwQ29uZGl0aW9uYWxGaWVsZHMoKTtcbiAgICB0aGlzLmdlbmVyYXRlRm9ybSgpO1xuICAgIHRoaXMuaW5pdGlhbGl6ZWQgPSB0cnVlO1xuXG4gICAgdGhpcy5zZW5kR2FpbnNpZ2h0RXZlbnQoQUNUSU9OUy5URU5BTlRfTElNSVRTX09QRU5FRCk7XG4gIH1cblxuICBhc3luYyBvblN1Ym1pdCgpIHtcbiAgICBpZiAodGhpcy5saW1pdHNGb3JtLmludmFsaWQgfHwgIXRoaXMudGVuYW50KSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgeyBkZXZpY2VTdG9yYWdlTGltaXRNaUIsIC4uLmN1c3RvbVByb3BlcnRpZXMgfSA9IHRoaXMuZ2V0RGlydHlWYWx1ZXMoKTtcblxuICAgIGNvbnN0IHVwZGF0ZWRUZW5hbnQ6IElUZW5hbnQgPSB7XG4gICAgICAuLi50aGlzLnRlbmFudCxcbiAgICAgIGN1c3RvbVByb3BlcnRpZXM6IHtcbiAgICAgICAgLi4udGhpcy50ZW5hbnQuY3VzdG9tUHJvcGVydGllcyxcbiAgICAgICAgLi4uY3VzdG9tUHJvcGVydGllc1xuICAgICAgfVxuICAgIH07XG5cbiAgICBpZiAodGhpcy5zdG9yYWdlTGltaXRGZWF0dXJlRW5hYmxlZCkge1xuICAgICAgdXBkYXRlZFRlbmFudC5zdG9yYWdlTGltaXRQZXJEZXZpY2UgPSAoZGV2aWNlU3RvcmFnZUxpbWl0TWlCIHx8IDApICogMTAyNCAqIDEwMjQ7XG4gICAgfSBlbHNlIHtcbiAgICAgIHVwZGF0ZWRUZW5hbnQuc3RvcmFnZUxpbWl0UGVyRGV2aWNlID0gMDtcbiAgICB9XG5cbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy50ZW5hbnRTZXJ2aWNlLnVwZGF0ZSh1cGRhdGVkVGVuYW50KTtcbiAgICAgIHRoaXMuYWxlcnRTZXJ2aWNlLnN1Y2Nlc3MoZ2V0dGV4dCgnTGltaXQgdmFsdWVzIHNhdmVkLicpKTtcbiAgICAgIHRoaXMuc2VuZEdhaW5zaWdodEV2ZW50KEFDVElPTlMuVEVOQU5UX0xJTUlUU19TQVZFRCwgeyByZXN1bHQ6IFJFU1VMVFMuU1VDQ0VTUyB9KTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgdGhpcy5hbGVydFNlcnZpY2UuYWRkU2VydmVyRmFpbHVyZShlcnJvcik7XG4gICAgICB0aGlzLnNlbmRHYWluc2lnaHRFdmVudChBQ1RJT05TLlRFTkFOVF9MSU1JVFNfU0FWRUQsIHsgcmVzdWx0OiBSRVNVTFRTLkZBSUxVUkUsIGVycm9yIH0pO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgbG9hZFRlbmFudERldGFpbHMoKSB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3VsdDogSVJlc3VsdDxJVGVuYW50PiA9IGF3YWl0IHRoaXMudGVuYW50U2VydmljZS5kZXRhaWwoXG4gICAgICAgIHRoaXMuYWN0aXZhdGVkUm91dGUuc25hcHNob3QucGFyZW50LmRhdGEuY29udGV4dERhdGEuaWRcbiAgICAgICk7XG4gICAgICB0aGlzLnRlbmFudCA9IHJlc3VsdC5kYXRhO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICB0aGlzLmFsZXJ0U2VydmljZS5hZGRTZXJ2ZXJGYWlsdXJlKGVycm9yKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHNldHVwQ29uZGl0aW9uYWxGaWVsZHMoKSB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGFwcHMgPSAoXG4gICAgICAgIGF3YWl0IHRoaXMuYXBwbGljYXRpb25TZXJ2aWNlLmxpc3RCeVVzZXIodW5kZWZpbmVkLCB7XG4gICAgICAgICAgZHJvcE92ZXJ3cml0dGVuQXBwczogdHJ1ZSxcbiAgICAgICAgICBub1BhZ2luZzogdHJ1ZVxuICAgICAgICB9KVxuICAgICAgKS5kYXRhO1xuXG4gICAgICBjb25zdCBjZXBNb2R1bGVFbmFibGVkID0gYXBwcy5zb21lKGFwcCA9PiBhcHAubmFtZSA9PT0gJ2NlcCcgfHwgYXBwLmNvbnRleHRQYXRoID09PSAnY2VwJyk7XG4gICAgICBjb25zdCBkYXRhQnJva2VyTW9kdWxlRW5hYmxlZCA9IGFwcHMuc29tZShcbiAgICAgICAgYXBwID0+IGFwcC5uYW1lID09PSAnZmVhdHVyZS1icm9rZXInIHx8IGFwcC5jb250ZXh0UGF0aCA9PT0gJ2ZlYXR1cmUtYnJva2VyJ1xuICAgICAgKTtcbiAgICAgIHRoaXMuc3RvcmFnZUxpbWl0RmVhdHVyZUVuYWJsZWQgPSB0aGlzLm9wdGlvbnMuc3RvcmFnZUxpbWl0YXRpb25GZWF0dXJlRW5hYmxlZDtcblxuICAgICAgaWYgKCFjZXBNb2R1bGVFbmFibGVkKSB7XG4gICAgICAgIGRlbGV0ZSB0aGlzLmZpZWxkRGVmaW5pdGlvbnMuY2VwU2VydmVyUXVldWVMaW1pdDtcbiAgICAgIH1cbiAgICAgIGlmICghZGF0YUJyb2tlck1vZHVsZUVuYWJsZWQpIHtcbiAgICAgICAgZGVsZXRlIHRoaXMuZmllbGREZWZpbml0aW9ucy5kYXRhQnJva2VyUXVldWVMaW1pdDtcbiAgICAgIH1cbiAgICAgIGlmICghdGhpcy5zdG9yYWdlTGltaXRGZWF0dXJlRW5hYmxlZCkge1xuICAgICAgICBkZWxldGUgdGhpcy5maWVsZERlZmluaXRpb25zLmRldmljZVN0b3JhZ2VMaW1pdE1pQjtcbiAgICAgIH1cblxuICAgICAgdGhpcy5maWVsZEtleXMgPSBPYmplY3Qua2V5cyh0aGlzLmZpZWxkRGVmaW5pdGlvbnMpO1xuICAgIH0gY2F0Y2ggKGV4KSB7XG4gICAgICB0aGlzLmFsZXJ0U2VydmljZS5hZGRTZXJ2ZXJGYWlsdXJlKGV4KTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGdlbmVyYXRlRm9ybSgpIHtcbiAgICBmb3IgKGNvbnN0IGZpZWxkIG9mIE9iamVjdC52YWx1ZXModGVuYW50TGltaXRzQ3VzdG9tUHJvcGVydGllcykpIHtcbiAgICAgIHRoaXMubGltaXRzRm9ybS5hZGRDb250cm9sKGZpZWxkLmlkLCBuZXcgRm9ybUNvbnRyb2woZmllbGQuZGVmYXVsdFZhbHVlLCBmaWVsZC52YWxpZGF0b3JzKSk7XG4gICAgfVxuICAgIGNvbnN0IGN1c3RvbVByb3BzID0gdGhpcy50ZW5hbnQ/LmN1c3RvbVByb3BlcnRpZXMgfHwge307XG4gICAgdGhpcy5saW1pdHNGb3JtLnBhdGNoVmFsdWUoY3VzdG9tUHJvcHMpO1xuICAgIGlmICh0aGlzLnN0b3JhZ2VMaW1pdEZlYXR1cmVFbmFibGVkKSB7XG4gICAgICBjb25zdCBkZXZpY2VTdG9yYWdlTGltaXRNaUIgPSAodGhpcy50ZW5hbnQ/LnN0b3JhZ2VMaW1pdFBlckRldmljZSB8fCAwKSAvIDEwMjQgLyAxMDI0O1xuICAgICAgdGhpcy5saW1pdHNGb3JtLnBhdGNoVmFsdWUoeyBkZXZpY2VTdG9yYWdlTGltaXRNaUIgfSk7XG4gICAgfVxuXG4gICAgdGhpcy5saW1pdHNGb3JtLnZhbHVlQ2hhbmdlcy5waXBlKHRha2UoMSkpLnN1YnNjcmliZSgoKSA9PiB7XG4gICAgICB0aGlzLnNlbmRHYWluc2lnaHRFdmVudChBQ1RJT05TLlRFTkFOVF9MSU1JVFNfU1RBUlRFRF9DSEFOR0lORyk7XG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIGdldERpcnR5VmFsdWVzKCk6IFBhcnRpYWw8eyBbSyBpbiBUZW5hbnRMaW1pdHNDdXN0b21Qcm9wZXJ0aWVzS2V5c106IGFueSB9PiB7XG4gICAgY29uc3QgZGlydHlWYWx1ZXM6IHsgW2tleTogc3RyaW5nXTogYW55IH0gPSB7fTtcbiAgICBPYmplY3Qua2V5cyh0aGlzLmxpbWl0c0Zvcm0uY29udHJvbHMpLmZvckVhY2goa2V5ID0+IHtcbiAgICAgIGNvbnN0IGNvbnRyb2wgPSB0aGlzLmxpbWl0c0Zvcm0uY29udHJvbHNba2V5XTtcbiAgICAgIGlmIChjb250cm9sICYmIGNvbnRyb2wuZGlydHkpIHtcbiAgICAgICAgZGlydHlWYWx1ZXNba2V5XSA9IGNvbnRyb2wudmFsdWU7XG4gICAgICB9XG4gICAgfSk7XG4gICAgcmV0dXJuIGRpcnR5VmFsdWVzO1xuICB9XG5cbiAgcHJpdmF0ZSBzZW5kR2FpbnNpZ2h0RXZlbnQoXG4gICAgYWN0aW9uOiBUZW5hbnRNYW5hZ2VtZW50QWN0aW9uVHlwZSxcbiAgICBwcm9wczogeyByZXN1bHQ/OiBUZW5hbnRNYW5hZ2VtZW50UmVzdWx0VHlwZTsgZXJyb3I/OiB1bmtub3duIH0gPSB7fVxuICApIHtcbiAgICB0aGlzLmdhaW5zaWdodFNlcnZpY2UudHJpZ2dlckV2ZW50KEVWRU5UUy5URU5BTlRfTUFOQUdFTUVOVCwge1xuICAgICAgY29tcG9uZW50OiBDT01QT05FTlRTLlRFTkFOVF9MSU1JVFMsXG4gICAgICBhY3Rpb24sXG4gICAgICAuLi5wcm9wc1xuICAgIH0pO1xuICB9XG59XG4iLCI8Yzh5LXRpdGxlICpuZ0lmPVwidGVuYW50XCI+XG4gIHt7IHRlbmFudC5jb21wYW55IH19XG48L2M4eS10aXRsZT5cblxuPGM4eS1icmVhZGNydW1iPlxuICA8Yzh5LWJyZWFkY3J1bWItaXRlbVxuICAgIFtpY29uXT1cIidjOHktbGF5ZXJzJ1wiXG4gICAgW2xhYmVsXT1cIidUZW5hbnRzJyB8IHRyYW5zbGF0ZVwiXG4gID48L2M4eS1icmVhZGNydW1iLWl0ZW0+XG4gIDxjOHktYnJlYWRjcnVtYi1pdGVtXG4gICAgW2ljb25dPVwiJ2M4eS1sYXllcnMnXCJcbiAgICBbbGFiZWxdPVwiJ1N1YnRlbmFudHMnIHwgdHJhbnNsYXRlXCJcbiAgICBbcGF0aF09XCInL3RlbmFudHMnXCJcbiAgPjwvYzh5LWJyZWFkY3J1bWItaXRlbT5cbjwvYzh5LWJyZWFkY3J1bWI+XG5cbjxmb3JtXG4gIFtmb3JtR3JvdXBdPVwibGltaXRzRm9ybVwiXG4gIChuZ1N1Ym1pdCk9XCJvblN1Ym1pdCgpXCJcbj5cbiAgPGRpdiBjbGFzcz1cImNhcmQgY2FyZC0tZnVsbHBhZ2UgbS1iLTBcIj5cbiAgICA8ZGl2IGNsYXNzPVwiY2FyZC1oZWFkZXIgc2VwYXJhdG9yXCI+XG4gICAgICA8ZGl2XG4gICAgICAgIGNsYXNzPVwiY2FyZC10aXRsZVwiXG4gICAgICAgIHRyYW5zbGF0ZVxuICAgICAgPlxuICAgICAgICBMaW1pdHNcbiAgICAgIDwvZGl2PlxuICAgIDwvZGl2PlxuXG4gICAgPGM4eS1oZWxwIHNyYz1cIi9kb2NzL2VudGVycHJpc2UtdGVuYW50L21hbmFnaW5nLXRlbmFudHMvI3NldHRpbmctbGltaXRzXCI+PC9jOHktaGVscD5cblxuICAgIDxkaXYgY2xhc3M9XCJpbm5lci1zY3JvbGxcIj5cbiAgICAgIDxkaXZcbiAgICAgICAgY2xhc3M9XCJjYXJkLWJsb2NrXCJcbiAgICAgICAgKm5nSWY9XCIhaW5pdGlhbGl6ZWRcIlxuICAgICAgPlxuICAgICAgICA8Yzh5LWxvYWRpbmc+PC9jOHktbG9hZGluZz5cbiAgICAgIDwvZGl2PlxuXG4gICAgICA8ZGl2XG4gICAgICAgIGNsYXNzPVwiY2FyZC1ibG9ja1wiXG4gICAgICAgICpuZ0lmPVwiaW5pdGlhbGl6ZWRcIlxuICAgICAgPlxuICAgICAgICA8bmctY29udGFpbmVyICpuZ0Zvcj1cImxldCBrZXkgb2YgZmllbGRLZXlzXCI+XG4gICAgICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImZpZWxkRGVmaW5pdGlvbnNba2V5XS50eXBlID09PSAndGV4dCdcIj5cbiAgICAgICAgICAgIDxuZy1jb250YWluZXJcbiAgICAgICAgICAgICAgKm5nVGVtcGxhdGVPdXRsZXQ9XCJ0ZXh0RmllbGQ7IGNvbnRleHQ6IHsgJGltcGxpY2l0OiBmaWVsZERlZmluaXRpb25zW2tleV0gfVwiXG4gICAgICAgICAgICA+PC9uZy1jb250YWluZXI+XG4gICAgICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImZpZWxkRGVmaW5pdGlvbnNba2V5XS50eXBlID09PSAnbnVtYmVyJ1wiPlxuICAgICAgICAgICAgPG5nLWNvbnRhaW5lclxuICAgICAgICAgICAgICAqbmdUZW1wbGF0ZU91dGxldD1cIm51bWJlckZpZWxkOyBjb250ZXh0OiB7ICRpbXBsaWNpdDogZmllbGREZWZpbml0aW9uc1trZXldIH1cIlxuICAgICAgICAgICAgPjwvbmctY29udGFpbmVyPlxuICAgICAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJmaWVsZERlZmluaXRpb25zW2tleV0udHlwZSA9PT0gJ2NoZWNrYm94J1wiPlxuICAgICAgICAgICAgPG5nLWNvbnRhaW5lclxuICAgICAgICAgICAgICAqbmdUZW1wbGF0ZU91dGxldD1cImNoZWNrYm94RmllbGQ7IGNvbnRleHQ6IHsgJGltcGxpY2l0OiBmaWVsZERlZmluaXRpb25zW2tleV0gfVwiXG4gICAgICAgICAgICA+PC9uZy1jb250YWluZXI+XG4gICAgICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgPC9kaXY+XG4gICAgPC9kaXY+XG5cbiAgICA8ZGl2XG4gICAgICBjbGFzcz1cImNhcmQtZm9vdGVyIHNlcGFyYXRvclwiXG4gICAgICAqbmdJZj1cImluaXRpYWxpemVkXCJcbiAgICA+XG4gICAgICA8YnV0dG9uXG4gICAgICAgIGNsYXNzPVwiYnRuIGJ0bi1kZWZhdWx0XCJcbiAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgIFtyb3V0ZXJMaW5rXT1cIlsnL3RlbmFudHMnXVwiXG4gICAgICAgIHRyYW5zbGF0ZVxuICAgICAgPlxuICAgICAgICBDYW5jZWxcbiAgICAgIDwvYnV0dG9uPlxuICAgICAgPGJ1dHRvblxuICAgICAgICBjbGFzcz1cImJ0biBidG4tcHJpbWFyeVwiXG4gICAgICAgIHR5cGU9XCJzdWJtaXRcIlxuICAgICAgICBbZGlzYWJsZWRdPVwibGltaXRzRm9ybS5pbnZhbGlkXCJcbiAgICAgICAgdHJhbnNsYXRlXG4gICAgICA+XG4gICAgICAgIFNhdmVcbiAgICAgIDwvYnV0dG9uPlxuICAgIDwvZGl2PlxuICA8L2Rpdj5cblxuICA8bmctdGVtcGxhdGVcbiAgICAjdGV4dEZpZWxkXG4gICAgbGV0LWZpZWxkRGVmaW5pdGlvblxuICA+XG4gICAgPGM4eS1mb3JtLWdyb3VwPlxuICAgICAgPGxhYmVsIFtmb3JdPVwiZmllbGREZWZpbml0aW9uLmlkXCI+XG4gICAgICAgIHt7IGZpZWxkRGVmaW5pdGlvbi5sYWJlbCB8IHRyYW5zbGF0ZTogZmllbGREZWZpbml0aW9uLmxhYmVsQXJncyB9fVxuICAgICAgPC9sYWJlbD5cbiAgICAgIDxpbnB1dFxuICAgICAgICBjbGFzcz1cImZvcm0tY29udHJvbFwiXG4gICAgICAgIHR5cGU9XCJ0ZXh0XCJcbiAgICAgICAgW2lkXT1cImZpZWxkRGVmaW5pdGlvbi5pZFwiXG4gICAgICAgIFtwbGFjZWhvbGRlcl09XCJmaWVsZERlZmluaXRpb24ucGxhY2Vob2xkZXIgfCB0cmFuc2xhdGU6IGZpZWxkRGVmaW5pdGlvbi5wbGFjZWhvbGRlckFyZ3NcIlxuICAgICAgICBbZm9ybUNvbnRyb2xOYW1lXT1cImZpZWxkRGVmaW5pdGlvbi5pZFwiXG4gICAgICAvPlxuICAgIDwvYzh5LWZvcm0tZ3JvdXA+XG4gIDwvbmctdGVtcGxhdGU+XG5cbiAgPG5nLXRlbXBsYXRlXG4gICAgI251bWJlckZpZWxkXG4gICAgbGV0LWZpZWxkRGVmaW5pdGlvblxuICA+XG4gICAgPGM4eS1mb3JtLWdyb3VwPlxuICAgICAgPGxhYmVsIFtmb3JdPVwiZmllbGREZWZpbml0aW9uLmlkXCI+XG4gICAgICAgIHt7IGZpZWxkRGVmaW5pdGlvbi5sYWJlbCB8IHRyYW5zbGF0ZTogZmllbGREZWZpbml0aW9uLmxhYmVsQXJncyB9fVxuICAgICAgPC9sYWJlbD5cbiAgICAgIDxpbnB1dFxuICAgICAgICBjbGFzcz1cImZvcm0tY29udHJvbFwiXG4gICAgICAgIHR5cGU9XCJudW1iZXJcIlxuICAgICAgICBbaWRdPVwiZmllbGREZWZpbml0aW9uLmlkXCJcbiAgICAgICAgW3BsYWNlaG9sZGVyXT1cImZpZWxkRGVmaW5pdGlvbi5wbGFjZWhvbGRlciB8IHRyYW5zbGF0ZTogZmllbGREZWZpbml0aW9uLnBsYWNlaG9sZGVyQXJnc1wiXG4gICAgICAgIFtmb3JtQ29udHJvbE5hbWVdPVwiZmllbGREZWZpbml0aW9uLmlkXCJcbiAgICAgIC8+XG4gICAgICA8bmctY29udGFpbmVyXG4gICAgICAgICpuZ0lmPVwiXG4gICAgICAgICAgZmllbGREZWZpbml0aW9ucy5kZXZpY2VTdG9yYWdlTGltaXRNaUIgJiZcbiAgICAgICAgICBmaWVsZERlZmluaXRpb25zLmRldmljZVN0b3JhZ2VMaW1pdE1pQi5pZCA9PT0gZmllbGREZWZpbml0aW9uLmlkICYmXG4gICAgICAgICAgbGltaXRzRm9ybS5jb250cm9sc1tmaWVsZERlZmluaXRpb24uaWRdIGFzIGNvbnRyb2xcbiAgICAgICAgXCJcbiAgICAgID5cbiAgICAgICAgPGRpdlxuICAgICAgICAgIGNsYXNzPVwiaWNvbi1mbGV4XCJcbiAgICAgICAgICAqbmdJZj1cIiFjb250cm9sLmRpcnR5IHx8IChjb250cm9sLmRpcnR5ICYmICFjb250cm9sLmVycm9ycylcIlxuICAgICAgICA+XG4gICAgICAgICAgPHAgY2xhc3M9XCJoZWxwLWJsb2NrXCI+XG4gICAgICAgICAgICA8aVxuICAgICAgICAgICAgICBjbGFzcz1cInRleHQtaW5mbyBtLXItNFwiXG4gICAgICAgICAgICAgIGM4eUljb249XCJpbmZvLWNpcmNsZVwiXG4gICAgICAgICAgICA+PC9pPlxuICAgICAgICAgICAgPHNwYW4+e3sgZGV2aWNlU3RvcmFnZUxpbWl0SW5mb01lc3NhZ2UgfCB0cmFuc2xhdGUgfX08L3NwYW4+XG4gICAgICAgICAgPC9wPlxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuICAgIDwvYzh5LWZvcm0tZ3JvdXA+XG4gIDwvbmctdGVtcGxhdGU+XG5cbiAgPG5nLXRlbXBsYXRlXG4gICAgI2NoZWNrYm94RmllbGRcbiAgICBsZXQtZmllbGREZWZpbml0aW9uXG4gID5cbiAgICA8Yzh5LWZvcm0tZ3JvdXA+XG4gICAgICA8bGFiZWxcbiAgICAgICAgY2xhc3M9XCJjOHktY2hlY2tib3hcIlxuICAgICAgICBbdGl0bGVdPVwiZmllbGREZWZpbml0aW9uLmxhYmVsIHwgdHJhbnNsYXRlXCJcbiAgICAgICAgW2Zvcl09XCJmaWVsZERlZmluaXRpb24uaWRcIlxuICAgICAgPlxuICAgICAgICA8aW5wdXRcbiAgICAgICAgICB0eXBlPVwiY2hlY2tib3hcIlxuICAgICAgICAgIFtpZF09XCJmaWVsZERlZmluaXRpb24uaWRcIlxuICAgICAgICAgIFtmb3JtQ29udHJvbE5hbWVdPVwiZmllbGREZWZpbml0aW9uLmlkXCJcbiAgICAgICAgLz5cbiAgICAgICAgPHNwYW4+PC9zcGFuPlxuICAgICAgICA8c3Bhbj57eyBmaWVsZERlZmluaXRpb24ubGFiZWwgfCB0cmFuc2xhdGUgfX08L3NwYW4+XG4gICAgICA8L2xhYmVsPlxuICAgIDwvYzh5LWZvcm0tZ3JvdXA+XG4gIDwvbmctdGVtcGxhdGU+XG48L2Zvcm0+XG4iXX0=