@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
134 lines • 40.9 kB
JavaScript
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { TenantLoginOptionsService, TenantService, UserService } from '@c8y/client';
import { clone } from 'lodash-es';
import { BsModalService } from 'ngx-bootstrap/modal';
import { AlertService } from '../alert/alert.service';
import { AppStateService } from '../common/ui-state.service';
import { TranslateService } from '../i18n/translate.service';
import { UserTotpSetupComponent } from './user-totp-setup.component';
import * as i0 from "@angular/core";
import * as i1 from "../common/ui-state.service";
import * as i2 from "../i18n/translate.service";
import * as i3 from "ngx-bootstrap/modal";
import * as i4 from "../alert/alert.service";
import * as i5 from "@c8y/client";
import * as i6 from "../forms/phone-validation.directive";
import * as i7 from "../forms/default-validation.directive";
import * as i8 from "@angular/forms";
import * as i9 from "../forms/form-group.component";
import * as i10 from "../forms/required-input-placeholder.directive";
import * as i11 from "../i18n/c8y-translate.directive";
import * as i12 from "@angular/common";
import * as i13 from "../authentication/new-password.component";
import * as i14 from "../i18n/c8y-translate.pipe";
export class UserEditComponent {
set user(u) {
if (u) {
this._user = clone(u);
this.userIsExternal = u.customProperties.userOrigin === 'OAUTH2';
this.isPhoneRequired = this.isPhoneRequired && u.twoFactorAuthenticationEnabled;
}
}
get user() {
return this._user;
}
constructor(state, translate, bsModalService, alert, userService, tenantLoginOptionsService, tenantService) {
this.state = state;
this.translate = translate;
this.bsModalService = bsModalService;
this.alert = alert;
this.userService = userService;
this.tenantLoginOptionsService = tenantLoginOptionsService;
this.tenantService = tenantService;
this.loading = false;
this.showProductExperienceOptions = false;
this.isUsageTrackingEnabled = true;
this.isUserEngagementPreferenceEnabled = true;
this.onUser = new EventEmitter();
this.onUsageTrackingChange = new EventEmitter();
this.onUserEngagementPreferenceChange = new EventEmitter();
this.onCancel = new EventEmitter();
this.userHasActiveTotp = false;
this.userCanSetupTotp = false;
this.isPhoneRequired = false;
}
async ngOnInit() {
const currentTenant = (await this.tenantService.current()).data;
const { enabledOnSystemLevel, enabledOnTenantLevel } = await this.tenantService.getTfaSettings(currentTenant);
this.isTfaEnabled = enabledOnSystemLevel || enabledOnTenantLevel;
await this.initializeTotpSettings();
if (this.user.twoFactorAuthenticationEnabled && !this.userCanSetupTotp) {
this.isPhoneRequired = true;
}
}
setupTotp() {
this.bsModalService.show(UserTotpSetupComponent, {
class: 'modal-sm',
ariaDescribedby: 'modal-body',
ariaLabelledBy: 'modal-title'
});
this.cancel(); // to close the user edit modal and prevent console errors on logout
}
cancel() {
this.onCancel.emit();
}
async save() {
if (this.loading) {
return;
}
if (this.showProductExperienceOptions) {
this.onUsageTrackingChange.emit(this.isUsageTrackingEnabled);
/**
* Emits a user engagement preference change event.
* If usage tracking is disabled, it emits `false`. Otherwise, it emits the current state of the user engagement preference.
*/
this.onUserEngagementPreferenceChange.emit(this.isUsageTrackingEnabled === false ? false : this.isUserEngagementPreferenceEnabled);
}
this.onUser.emit(this._user);
}
onNewPasswordChanged(newPassword) {
this._user.password = newPassword.password;
}
async initializeTotpSettings() {
try {
this.userCanSetupTotp = await this.canUserSetupTotp();
if (this.userCanSetupTotp) {
const { data: totpActivity } = await this.userService.getActivityTotp();
this.userHasActiveTotp = totpActivity.isActive;
}
}
catch (ex) {
this.alert.removeLastDanger();
}
}
async canUserSetupTotp() {
// we don't check for tenant options here due to permission restrictions on that end-point
const loginOptions = (await this.tenantLoginOptionsService.listForCurrentTenant()).data;
return loginOptions.some(({ tfaStrategy = '' }) => tfaStrategy.toLowerCase() === 'totp');
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserEditComponent, deps: [{ token: i1.AppStateService }, { token: i2.TranslateService }, { token: i3.BsModalService }, { token: i4.AlertService }, { token: i5.UserService }, { token: i5.TenantLoginOptionsService }, { token: i5.TenantService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: UserEditComponent, selector: "c8y-user-edit", inputs: { loading: "loading", user: "user", showProductExperienceOptions: "showProductExperienceOptions", isUsageTrackingEnabled: "isUsageTrackingEnabled", isUserEngagementPreferenceEnabled: "isUserEngagementPreferenceEnabled" }, outputs: { onUser: "onUser", onUsageTrackingChange: "onUsageTrackingChange", onUserEngagementPreferenceChange: "onUserEngagementPreferenceChange", onCancel: "onCancel" }, ngImport: i0, template: "<form #userForm=\"ngForm\" (ngSubmit)=\"userForm.form.valid && save()\">\n <div class=\"d-block p-24 p-b-0\">\n <div class=\"alert alert-warning\" role=\"alert\" *ngIf=\"userIsExternal\" translate>\n Some of the user settings are not editable here because they are managed via your\n authorization server.\n </div>\n <c8y-form-group>\n <label translate for=\"userName\">Username</label>\n <input\n id=\"userName\"\n class=\"form-control\"\n [(ngModel)]=\"user.userName\"\n name=\"userName\"\n autocomplete=\"off\"\n required\n maxlength=\"254\"\n placeholder=\"{{ 'e.g. joe.doe@example.com`LOCALIZE`' | translate }}\"\n [disabled]=\"user.id\"\n c8yDefaultValidation=\"user\"\n />\n </c8y-form-group>\n\n <c8y-form-group>\n <label translate for=\"displayName\">Login alias</label>\n <input\n id=\"displayName\"\n class=\"form-control\"\n [(ngModel)]=\"user.displayName\"\n name=\"displayName\"\n autocomplete=\"off\"\n maxlength=\"254\"\n placeholder=\"{{ 'e.g. joe.doe`LOCALIZE`' | translate }}\"\n [disabled]=\"userIsExternal\"\n c8yDefaultValidation=\"loginAlias\"\n />\n </c8y-form-group>\n\n <c8y-form-group [hasWarning]=\"!user.email\">\n <label translate for=\"userEmail\">Email</label>\n <input\n id=\"userEmail\"\n class=\"form-control\"\n type=\"email\"\n name=\"email\"\n [maxlength]=\"254\"\n autocomplete=\"off\"\n placeholder=\"{{ 'e.g. joe.doe@example.com`LOCALIZE`' | translate }}\"\n [(ngModel)]=\"user.email\"\n email\n [required]=\"true\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n\n <div class=\"row\">\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label translate for=\"userFirstName\">First name</label>\n <input\n id=\"userFirstName\"\n class=\"form-control\"\n autocomplete=\"off\"\n placeholder=\"{{ 'e.g. Joe`LOCALIZE`' | translate }}\"\n maxlength=\"50\"\n name=\"firstName\"\n [(ngModel)]=\"user.firstName\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n </div>\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label translate for=\"userLastName\">Last name</label>\n <input\n id=\"userLastName\"\n class=\"form-control\"\n autocomplete=\"off\"\n placeholder=\"{{ 'e.g. Doe`LOCALIZE`' | translate }}\"\n maxlength=\"50\"\n name=\"lastName\"\n [(ngModel)]=\"user.lastName\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n </div>\n </div>\n\n <c8y-form-group>\n <label translate for=\"userTelephone\">Telephone</label>\n <input\n id=\"userTelephone\"\n class=\"form-control\"\n autocomplete=\"off\"\n name=\"phone\"\n maxlength=\"254\"\n [(ngModel)]=\"user.phone\"\n placeholder=\"{{ 'e.g. +49 9 876 543 210`LOCALIZE`' | translate }}\"\n c8yPhoneValidation\n [required]=\"isPhoneRequired\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n\n <c8y-form-group class=\"p-t-16 separator-top\" *ngIf=\"showProductExperienceOptions\">\n <label translate>Product experience</label>\n <label class=\"c8y-switch\" for=\"productUsageTracking\">\n <input\n id=\"productUsageTracking\"\n name=\"productUsageTracking\"\n type=\"checkbox\"\n [(ngModel)]=\"isUsageTrackingEnabled\"\n />\n <span></span>\n {{ 'Enable personalized product experience tracking' | translate }}\n </label>\n <ng-container *ngIf=\"isUsageTrackingEnabled\">\n <label class=\"c8y-switch m-l-0\" for=\"userEngagementPreference\">\n <input\n id=\"userEngagementPreference\"\n name=\"userEngagementPreference\"\n type=\"checkbox\"\n [(ngModel)]=\"isUserEngagementPreferenceEnabled\"\n />\n <span></span>\n {{ 'Enable in-product information & communication' | translate }}\n </label>\n </ng-container>\n </c8y-form-group>\n\n <div class=\"form-group p-t-16 separator-top\" *ngIf=\"!userIsExternal\">\n <label class=\"control-label\">{{ 'Login options' | translate }}</label>\n <c8y-new-password (password)=\"onNewPasswordChanged($event)\"></c8y-new-password>\n <button\n class=\"btn btn-default\"\n type=\"button\"\n title=\"{{ 'Set up two-factor authentication' | translate }}\"\n (click)=\"setupTotp()\"\n *ngIf=\"userCanSetupTotp && !userHasActiveTotp && isTfaEnabled\"\n >\n {{ 'Set up two-factor authentication' | translate }}\n </button>\n </div>\n\n <c8y-form-group *ngIf=\"!!(state.state$ | async).newsletter\">\n <label translate>Newsletter</label>\n <label\n title=\"{{ 'Send me information about outages, maintenance or updates.' | translate }}\"\n class=\"c8y-checkbox\"\n >\n <input\n type=\"checkbox\"\n name=\"newsletter\"\n [(ngModel)]=\"user.newsletter\"\n [disabled]=\"userIsExternal\"\n />\n <span></span>\n <span>\n {{ 'Send me information about outages, maintenance or updates.' | translate }}\n </span>\n </label>\n </c8y-form-group>\n </div>\n <div class=\"modal-footer separator-top bg-level-0 sticky-bottom\">\n <button\n class=\"btn btn-default\"\n type=\"button\"\n title=\"{{ 'Cancel' | translate }}\"\n (click)=\"cancel()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary\"\n type=\"submit\"\n title=\"{{ 'Save' | translate }}\"\n [disabled]=\"!userForm.form.valid || userForm.form.pristine || loading\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n</form>\n", dependencies: [{ kind: "directive", type: i6.PhoneValidationDirective, selector: "[c8yPhoneValidation]" }, { kind: "directive", type: i7.DefaultValidationDirective, selector: "[c8yDefaultValidation]", inputs: ["c8yDefaultValidation"] }, { kind: "directive", type: i8.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i8.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: i8.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i8.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i8.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i8.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i8.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i8.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i8.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i8.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: i9.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i10.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i11.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i12.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i13.NewPasswordComponent, selector: "c8y-new-password", outputs: ["password"] }, { kind: "pipe", type: i14.C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i12.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserEditComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-user-edit', template: "<form #userForm=\"ngForm\" (ngSubmit)=\"userForm.form.valid && save()\">\n <div class=\"d-block p-24 p-b-0\">\n <div class=\"alert alert-warning\" role=\"alert\" *ngIf=\"userIsExternal\" translate>\n Some of the user settings are not editable here because they are managed via your\n authorization server.\n </div>\n <c8y-form-group>\n <label translate for=\"userName\">Username</label>\n <input\n id=\"userName\"\n class=\"form-control\"\n [(ngModel)]=\"user.userName\"\n name=\"userName\"\n autocomplete=\"off\"\n required\n maxlength=\"254\"\n placeholder=\"{{ 'e.g. joe.doe@example.com`LOCALIZE`' | translate }}\"\n [disabled]=\"user.id\"\n c8yDefaultValidation=\"user\"\n />\n </c8y-form-group>\n\n <c8y-form-group>\n <label translate for=\"displayName\">Login alias</label>\n <input\n id=\"displayName\"\n class=\"form-control\"\n [(ngModel)]=\"user.displayName\"\n name=\"displayName\"\n autocomplete=\"off\"\n maxlength=\"254\"\n placeholder=\"{{ 'e.g. joe.doe`LOCALIZE`' | translate }}\"\n [disabled]=\"userIsExternal\"\n c8yDefaultValidation=\"loginAlias\"\n />\n </c8y-form-group>\n\n <c8y-form-group [hasWarning]=\"!user.email\">\n <label translate for=\"userEmail\">Email</label>\n <input\n id=\"userEmail\"\n class=\"form-control\"\n type=\"email\"\n name=\"email\"\n [maxlength]=\"254\"\n autocomplete=\"off\"\n placeholder=\"{{ 'e.g. joe.doe@example.com`LOCALIZE`' | translate }}\"\n [(ngModel)]=\"user.email\"\n email\n [required]=\"true\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n\n <div class=\"row\">\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label translate for=\"userFirstName\">First name</label>\n <input\n id=\"userFirstName\"\n class=\"form-control\"\n autocomplete=\"off\"\n placeholder=\"{{ 'e.g. Joe`LOCALIZE`' | translate }}\"\n maxlength=\"50\"\n name=\"firstName\"\n [(ngModel)]=\"user.firstName\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n </div>\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label translate for=\"userLastName\">Last name</label>\n <input\n id=\"userLastName\"\n class=\"form-control\"\n autocomplete=\"off\"\n placeholder=\"{{ 'e.g. Doe`LOCALIZE`' | translate }}\"\n maxlength=\"50\"\n name=\"lastName\"\n [(ngModel)]=\"user.lastName\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n </div>\n </div>\n\n <c8y-form-group>\n <label translate for=\"userTelephone\">Telephone</label>\n <input\n id=\"userTelephone\"\n class=\"form-control\"\n autocomplete=\"off\"\n name=\"phone\"\n maxlength=\"254\"\n [(ngModel)]=\"user.phone\"\n placeholder=\"{{ 'e.g. +49 9 876 543 210`LOCALIZE`' | translate }}\"\n c8yPhoneValidation\n [required]=\"isPhoneRequired\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n\n <c8y-form-group class=\"p-t-16 separator-top\" *ngIf=\"showProductExperienceOptions\">\n <label translate>Product experience</label>\n <label class=\"c8y-switch\" for=\"productUsageTracking\">\n <input\n id=\"productUsageTracking\"\n name=\"productUsageTracking\"\n type=\"checkbox\"\n [(ngModel)]=\"isUsageTrackingEnabled\"\n />\n <span></span>\n {{ 'Enable personalized product experience tracking' | translate }}\n </label>\n <ng-container *ngIf=\"isUsageTrackingEnabled\">\n <label class=\"c8y-switch m-l-0\" for=\"userEngagementPreference\">\n <input\n id=\"userEngagementPreference\"\n name=\"userEngagementPreference\"\n type=\"checkbox\"\n [(ngModel)]=\"isUserEngagementPreferenceEnabled\"\n />\n <span></span>\n {{ 'Enable in-product information & communication' | translate }}\n </label>\n </ng-container>\n </c8y-form-group>\n\n <div class=\"form-group p-t-16 separator-top\" *ngIf=\"!userIsExternal\">\n <label class=\"control-label\">{{ 'Login options' | translate }}</label>\n <c8y-new-password (password)=\"onNewPasswordChanged($event)\"></c8y-new-password>\n <button\n class=\"btn btn-default\"\n type=\"button\"\n title=\"{{ 'Set up two-factor authentication' | translate }}\"\n (click)=\"setupTotp()\"\n *ngIf=\"userCanSetupTotp && !userHasActiveTotp && isTfaEnabled\"\n >\n {{ 'Set up two-factor authentication' | translate }}\n </button>\n </div>\n\n <c8y-form-group *ngIf=\"!!(state.state$ | async).newsletter\">\n <label translate>Newsletter</label>\n <label\n title=\"{{ 'Send me information about outages, maintenance or updates.' | translate }}\"\n class=\"c8y-checkbox\"\n >\n <input\n type=\"checkbox\"\n name=\"newsletter\"\n [(ngModel)]=\"user.newsletter\"\n [disabled]=\"userIsExternal\"\n />\n <span></span>\n <span>\n {{ 'Send me information about outages, maintenance or updates.' | translate }}\n </span>\n </label>\n </c8y-form-group>\n </div>\n <div class=\"modal-footer separator-top bg-level-0 sticky-bottom\">\n <button\n class=\"btn btn-default\"\n type=\"button\"\n title=\"{{ 'Cancel' | translate }}\"\n (click)=\"cancel()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary\"\n type=\"submit\"\n title=\"{{ 'Save' | translate }}\"\n [disabled]=\"!userForm.form.valid || userForm.form.pristine || loading\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n</form>\n" }]
}], ctorParameters: () => [{ type: i1.AppStateService }, { type: i2.TranslateService }, { type: i3.BsModalService }, { type: i4.AlertService }, { type: i5.UserService }, { type: i5.TenantLoginOptionsService }, { type: i5.TenantService }], propDecorators: { loading: [{
type: Input
}], user: [{
type: Input
}], showProductExperienceOptions: [{
type: Input
}], isUsageTrackingEnabled: [{
type: Input
}], isUserEngagementPreferenceEnabled: [{
type: Input
}], onUser: [{
type: Output
}], onUsageTrackingChange: [{
type: Output
}], onUserEngagementPreferenceChange: [{
type: Output
}], onCancel: [{
type: Output
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXNlci1lZGl0LmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2NvcmUvdXNlci91c2VyLWVkaXQuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vY29yZS91c2VyL3VzZXItZWRpdC5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLFlBQVksRUFBRSxLQUFLLEVBQVUsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQy9FLE9BQU8sRUFBUyx5QkFBeUIsRUFBRSxhQUFhLEVBQUUsV0FBVyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQzNGLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDbEMsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ3JELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUV0RCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDN0QsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDN0QsT0FBTyxFQUFFLHNCQUFzQixFQUFFLE1BQU0sNkJBQTZCLENBQUM7Ozs7Ozs7Ozs7Ozs7Ozs7QUFZckUsTUFBTSxPQUFPLGlCQUFpQjtJQUU1QixJQUFhLElBQUksQ0FBQyxDQUFPO1FBQ3ZCLElBQUksQ0FBQyxFQUFFLENBQUM7WUFDTixJQUFJLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUN0QixJQUFJLENBQUMsY0FBYyxHQUFHLENBQUMsQ0FBQyxnQkFBZ0IsQ0FBQyxVQUFVLEtBQUssUUFBUSxDQUFDO1lBQ2pFLElBQUksQ0FBQyxlQUFlLEdBQUcsSUFBSSxDQUFDLGVBQWUsSUFBSSxDQUFDLENBQUMsOEJBQThCLENBQUM7UUFDbEYsQ0FBQztJQUNILENBQUM7SUFDRCxJQUFJLElBQUk7UUFD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QixNQUFNO2dCQUNHLGdDQUFnQztzQkFBekMsTUFBTTtnQkFDRyxRQUFRO3NCQUFqQixNQUFNIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBFdmVudEVtaXR0ZXIsIElucHV0LCBPbkluaXQsIE91dHB1dCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgSVVzZXIsIFRlbmFudExvZ2luT3B0aW9uc1NlcnZpY2UsIFRlbmFudFNlcnZpY2UsIFVzZXJTZXJ2aWNlIH0gZnJvbSAnQGM4eS9jbGllbnQnO1xuaW1wb3J0IHsgY2xvbmUgfSBmcm9tICdsb2Rhc2gtZXMnO1xuaW1wb3J0IHsgQnNNb2RhbFNlcnZpY2UgfSBmcm9tICduZ3gtYm9vdHN0cmFwL21vZGFsJztcbmltcG9ydCB7IEFsZXJ0U2VydmljZSB9IGZyb20gJy4uL2FsZXJ0L2FsZXJ0LnNlcnZpY2UnO1xuaW1wb3J0IHsgTmV3UGFzc3dvcmQgfSBmcm9tICcuLi9hdXRoZW50aWNhdGlvbi9wYXNzd29yZC5tb2RlbCc7XG5pbXBvcnQgeyBBcHBTdGF0ZVNlcnZpY2UgfSBmcm9tICcuLi9jb21tb24vdWktc3RhdGUuc2VydmljZSc7XG5pbXBvcnQgeyBUcmFuc2xhdGVTZXJ2aWNlIH0gZnJvbSAnLi4vaTE4bi90cmFuc2xhdGUuc2VydmljZSc7XG5pbXBvcnQgeyBVc2VyVG90cFNldHVwQ29tcG9uZW50IH0gZnJvbSAnLi91c2VyLXRvdHAtc2V0dXAuY29tcG9uZW50JztcblxuZXhwb3J0IGludGVyZmFjZSBVc2VyIGV4dGVuZHMgSVVzZXIge1xuICBwaG9uZTogc3RyaW5nO1xuICBzZW5kUGFzc3dvcmRSZXNldEVtYWlsOiBib29sZWFuO1xuICBuZXdzbGV0dGVyPzogYm9vbGVhbjtcbn1cblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnYzh5LXVzZXItZWRpdCcsXG4gIHRlbXBsYXRlVXJsOiAnLi91c2VyLWVkaXQuY29tcG9uZW50Lmh0bWwnXG59KVxuZXhwb3J0IGNsYXNzIFVzZXJFZGl0Q29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0IHtcbiAgQElucHV0KCkgbG9hZGluZyA9IGZhbHNlO1xuICBASW5wdXQoKSBzZXQgdXNlcih1OiBVc2VyKSB7XG4gICAgaWYgKHUpIHtcbiAgICAgIHRoaXMuX3VzZXIgPSBjbG9uZSh1KTtcbiAgICAgIHRoaXMudXNlcklzRXh0ZXJuYWwgPSB1LmN1c3RvbVByb3BlcnRpZXMudXNlck9yaWdpbiA9PT0gJ09BVVRIMic7XG4gICAgICB0aGlzLmlzUGhvbmVSZXF1aXJlZCA9IHRoaXMuaXNQaG9uZVJlcXVpcmVkICYmIHUudHdvRmFjdG9yQXV0aGVudGljYXRpb25FbmFibGVkO1xuICAgIH1cbiAgfVxuICBnZXQgdXNlcigpIHtcbiAgICByZXR1cm4gdGhpcy5fdXNlcjtcbiAgfVxuICBASW5wdXQoKSBzaG93UHJvZHVjdEV4cGVyaWVuY2VPcHRpb25zID0gZmFsc2U7XG4gIEBJbnB1dCgpIGlzVXNhZ2VUcmFja2luZ0VuYWJsZWQgPSB0cnVlO1xuICBASW5wdXQoKSBpc1VzZXJFbmdhZ2VtZW50UHJlZmVyZW5jZUVuYWJsZWQgPSB0cnVlO1xuICBAT3V0cHV0KCkgb25Vc2VyOiBFdmVudEVtaXR0ZXI8VXNlcj4gPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG4gIEBPdXRwdXQoKSBvblVzYWdlVHJhY2tpbmdDaGFuZ2U6IEV2ZW50RW1pdHRlcjxib29sZWFuPiA9IG5ldyBFdmVudEVtaXR0ZXIoKTtcbiAgQE91dHB1dCgpIG9uVXNlckVuZ2FnZW1lbnRQcmVmZXJlbmNlQ2hhbmdlOiBFdmVudEVtaXR0ZXI8Ym9vbGVhbj4gPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG4gIEBPdXRwdXQoKSBvbkNhbmNlbDogRXZlbnRFbWl0dGVyPHZvaWQ+ID0gbmV3IEV2ZW50RW1pdHRlcigpO1xuICB1c2VySGFzQWN0aXZlVG90cCA9IGZhbHNlO1xuICB1c2VyQ2FuU2V0dXBUb3RwID0gZmFsc2U7XG4gIGlzUGhvbmVSZXF1aXJlZCA9IGZhbHNlO1xuICB1c2VySXNFeHRlcm5hbDogYm9vbGVhbjtcbiAgaXNUZmFFbmFibGVkOiBib29sZWFuO1xuXG4gIHByaXZhdGUgX3VzZXI6IFVzZXI7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHVibGljIHN0YXRlOiBBcHBTdGF0ZVNlcnZpY2UsXG4gICAgcHVibGljIHRyYW5zbGF0ZTogVHJhbnNsYXRlU2VydmljZSxcbiAgICBwcml2YXRlIGJzTW9kYWxTZXJ2aWNlOiBCc01vZGFsU2VydmljZSxcbiAgICBwcml2YXRlIGFsZXJ0OiBBbGVydFNlcnZpY2UsXG4gICAgcHJpdmF0ZSB1c2VyU2VydmljZTogVXNlclNlcnZpY2UsXG4gICAgcHJpdmF0ZSB0ZW5hbnRMb2dpbk9wdGlvbnNTZXJ2aWNlOiBUZW5hbnRMb2dpbk9wdGlvbnNTZXJ2aWNlLFxuICAgIHByaXZhdGUgdGVuYW50U2VydmljZTogVGVuYW50U2VydmljZVxuICApIHt9XG5cbiAgYXN5bmMgbmdPbkluaXQoKSB7XG4gICAgY29uc3QgY3VycmVudFRlbmFudCA9IChhd2FpdCB0aGlzLnRlbmFudFNlcnZpY2UuY3VycmVudCgpKS5kYXRhO1xuICAgIGNvbnN0IHsgZW5hYmxlZE9uU3lzdGVtTGV2ZWwsIGVuYWJsZWRPblRlbmFudExldmVsIH0gPVxuICAgICAgYXdhaXQgdGhpcy50ZW5hbnRTZXJ2aWNlLmdldFRmYVNldHRpbmdzKGN1cnJlbnRUZW5hbnQpO1xuICAgIHRoaXMuaXNUZmFFbmFibGVkID0gZW5hYmxlZE9uU3lzdGVtTGV2ZWwgfHwgZW5hYmxlZE9uVGVuYW50TGV2ZWw7XG5cbiAgICBhd2FpdCB0aGlzLmluaXRpYWxpemVUb3RwU2V0dGluZ3MoKTtcbiAgICBpZiAodGhpcy51c2VyLnR3b0ZhY3RvckF1dGhlbnRpY2F0aW9uRW5hYmxlZCAmJiAhdGhpcy51c2VyQ2FuU2V0dXBUb3RwKSB7XG4gICAgICB0aGlzLmlzUGhvbmVSZXF1aXJlZCA9IHRydWU7XG4gICAgfVxuICB9XG5cbiAgc2V0dXBUb3RwKCkge1xuICAgIHRoaXMuYnNNb2RhbFNlcnZpY2Uuc2hvdyhVc2VyVG90cFNldHVwQ29tcG9uZW50LCB7XG4gICAgICBjbGFzczogJ21vZGFsLXNtJyxcbiAgICAgIGFyaWFEZXNjcmliZWRieTogJ21vZGFsLWJvZHknLFxuICAgICAgYXJpYUxhYmVsbGVkQnk6ICdtb2RhbC10aXRsZSdcbiAgICB9KTtcbiAgICB0aGlzLmNhbmNlbCgpOyAvLyB0byBjbG9zZSB0aGUgdXNlciBlZGl0IG1vZGFsIGFuZCBwcmV2ZW50IGNvbnNvbGUgZXJyb3JzIG9uIGxvZ291dFxuICB9XG5cbiAgY2FuY2VsKCkge1xuICAgIHRoaXMub25DYW5jZWwuZW1pdCgpO1xuICB9XG5cbiAgYXN5bmMgc2F2ZSgpIHtcbiAgICBpZiAodGhpcy5sb2FkaW5nKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKHRoaXMuc2hvd1Byb2R1Y3RFeHBlcmllbmNlT3B0aW9ucykge1xuICAgICAgdGhpcy5vblVzYWdlVHJhY2tpbmdDaGFuZ2UuZW1pdCh0aGlzLmlzVXNhZ2VUcmFja2luZ0VuYWJsZWQpO1xuICAgICAgLyoqXG4gICAgICAgKiBFbWl0cyBhIHVzZXIgZW5nYWdlbWVudCBwcmVmZXJlbmNlIGNoYW5nZSBldmVudC5cbiAgICAgICAqIElmIHVzYWdlIHRyYWNraW5nIGlzIGRpc2FibGVkLCBpdCBlbWl0cyBgZmFsc2VgLiBPdGhlcndpc2UsIGl0IGVtaXRzIHRoZSBjdXJyZW50IHN0YXRlIG9mIHRoZSB1c2VyIGVuZ2FnZW1lbnQgcHJlZmVyZW5jZS5cbiAgICAgICAqL1xuICAgICAgdGhpcy5vblVzZXJFbmdhZ2VtZW50UHJlZmVyZW5jZUNoYW5nZS5lbWl0KFxuICAgICAgICB0aGlzLmlzVXNhZ2VUcmFja2luZ0VuYWJsZWQgPT09IGZhbHNlID8gZmFsc2UgOiB0aGlzLmlzVXNlckVuZ2FnZW1lbnRQcmVmZXJlbmNlRW5hYmxlZFxuICAgICAgKTtcbiAgICB9XG5cbiAgICB0aGlzLm9uVXNlci5lbWl0KHRoaXMuX3VzZXIpO1xuICB9XG5cbiAgb25OZXdQYXNzd29yZENoYW5nZWQobmV3UGFzc3dvcmQ6IE5ld1Bhc3N3b3JkKSB7XG4gICAgdGhpcy5fdXNlci5wYXNzd29yZCA9IG5ld1Bhc3N3b3JkLnBhc3N3b3JkO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBpbml0aWFsaXplVG90cFNldHRpbmdzKCkge1xuICAgIHRyeSB7XG4gICAgICB0aGlzLnVzZXJDYW5TZXR1cFRvdHAgPSBhd2FpdCB0aGlzLmNhblVzZXJTZXR1cFRvdHAoKTtcbiAgICAgIGlmICh0aGlzLnVzZXJDYW5TZXR1cFRvdHApIHtcbiAgICAgICAgY29uc3QgeyBkYXRhOiB0b3RwQWN0aXZpdHkgfSA9IGF3YWl0IHRoaXMudXNlclNlcnZpY2UuZ2V0QWN0aXZpdHlUb3RwKCk7XG4gICAgICAgIHRoaXMudXNlckhhc0FjdGl2ZVRvdHAgPSB0b3RwQWN0aXZpdHkuaXNBY3RpdmU7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXgpIHtcbiAgICAgIHRoaXMuYWxlcnQucmVtb3ZlTGFzdERhbmdlcigpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgY2FuVXNlclNldHVwVG90cCgpIHtcbiAgICAvLyB3ZSBkb24ndCBjaGVjayBmb3IgdGVuYW50IG9wdGlvbnMgaGVyZSBkdWUgdG8gcGVybWlzc2lvbiByZXN0cmljdGlvbnMgb24gdGhhdCBlbmQtcG9pbnRcbiAgICBjb25zdCBsb2dpbk9wdGlvbnMgPSAoYXdhaXQgdGhpcy50ZW5hbnRMb2dpbk9wdGlvbnNTZXJ2aWNlLmxpc3RGb3JDdXJyZW50VGVuYW50KCkpLmRhdGE7XG4gICAgcmV0dXJuIGxvZ2luT3B0aW9ucy5zb21lKCh7IHRmYVN0cmF0ZWd5ID0gJycgfSkgPT4gdGZhU3RyYXRlZ3kudG9Mb3dlckNhc2UoKSA9PT0gJ3RvdHAnKTtcbiAgfVxufVxuIiwiPGZvcm0gI3VzZXJGb3JtPVwibmdGb3JtXCIgKG5nU3VibWl0KT1cInVzZXJGb3JtLmZvcm0udmFsaWQgJiYgc2F2ZSgpXCI+XG4gIDxkaXYgY2xhc3M9XCJkLWJsb2NrIHAtMjQgcC1iLTBcIj5cbiAgICA8ZGl2IGNsYXNzPVwiYWxlcnQgYWxlcnQtd2FybmluZ1wiIHJvbGU9XCJhbGVydFwiICpuZ0lmPVwidXNlcklzRXh0ZXJuYWxcIiB0cmFuc2xhdGU+XG4gICAgICBTb21lIG9mIHRoZSB1c2VyIHNldHRpbmdzIGFyZSBub3QgZWRpdGFibGUgaGVyZSBiZWNhdXNlIHRoZXkgYXJlIG1hbmFnZWQgdmlhIHlvdXJcbiAgICAgIGF1dGhvcml6YXRpb24gc2VydmVyLlxuICAgIDwvZGl2PlxuICAgIDxjOHktZm9ybS1ncm91cD5cbiAgICAgIDxsYWJlbCB0cmFuc2xhdGUgZm9yPVwidXNlck5hbWVcIj5Vc2VybmFtZTwvbGFiZWw+XG4gICAgICA8aW5wdXRcbiAgICAgICAgaWQ9XCJ1c2VyTmFtZVwiXG4gICAgICAgIGNsYXNzPVwiZm9ybS1jb250cm9sXCJcbiAgICAgICAgWyhuZ01vZGVsKV09XCJ1c2VyLnVzZXJOYW1lXCJcbiAgICAgICAgbmFtZT1cInVzZXJOYW1lXCJcbiAgICAgICAgYXV0b2NvbXBsZXRlPVwib2ZmXCJcbiAgICAgICAgcmVxdWlyZWRcbiAgICAgICAgbWF4bGVuZ3RoPVwiMjU0XCJcbiAgICAgICAgcGxhY2Vob2xkZXI9XCJ7eyAnZS5nLiBqb2UuZG9lQGV4YW1wbGUuY29tYExPQ0FMSVpFYCcgfCB0cmFuc2xhdGUgfX1cIlxuICAgICAgICBbZGlzYWJsZWRdPVwidXNlci5pZFwiXG4gICAgICAgIGM4eURlZmF1bHRWYWxpZGF0aW9uPVwidXNlclwiXG4gICAgICAvPlxuICAgIDwvYzh5LWZvcm0tZ3JvdXA+XG5cbiAgICA8Yzh5LWZvcm0tZ3JvdXA+XG4gICAgICA8bGFiZWwgdHJhbnNsYXRlIGZvcj1cImRpc3BsYXlOYW1lXCI+TG9naW4gYWxpYXM8L2xhYmVsPlxuICAgICAgPGlucHV0XG4gICAgICAgIGlkPVwiZGlzcGxheU5hbWVcIlxuICAgICAgICBjbGFzcz1cImZvcm0tY29udHJvbFwiXG4gICAgICAgIFsobmdNb2RlbCldPVwidXNlci5kaXNwbGF5TmFtZVwiXG4gICAgICAgIG5hbWU9XCJkaXNwbGF5TmFtZVwiXG4gICAgICAgIGF1dG9jb21wbGV0ZT1cIm9mZlwiXG4gICAgICAgIG1heGxlbmd0aD1cIjI1NFwiXG4gICAgICAgIHBsYWNlaG9sZGVyPVwie3sgJ2UuZy4gam9lLmRvZWBMT0NBTElaRWAnIHwgdHJhbnNsYXRlIH19XCJcbiAgICAgICAgW2Rpc2FibGVkXT1cInVzZXJJc0V4dGVybmFsXCJcbiAgICAgICAgYzh5RGVmYXVsdFZhbGlkYXRpb249XCJsb2dpbkFsaWFzXCJcbiAgICAgIC8+XG4gICAgPC9jOHktZm9ybS1ncm91cD5cblxuICAgIDxjOHktZm9ybS1ncm91cCBbaGFzV2FybmluZ109XCIhdXNlci5lbWFpbFwiPlxuICAgICAgPGxhYmVsIHRyYW5zbGF0ZSBmb3I9XCJ1c2VyRW1haWxcIj5FbWFpbDwvbGFiZWw+XG4gICAgICA8aW5wdXRcbiAgICAgICAgaWQ9XCJ1c2VyRW1haWxcIlxuICAgICAgICBjbGFzcz1cImZvcm0tY29udHJvbFwiXG4gICAgICAgIHR5cGU9XCJlbWFpbFwiXG4gICAgICAgIG5hbWU9XCJlbWFpbFwiXG4gICAgICAgIFttYXhsZW5ndGhdPVwiMjU0XCJcbiAgICAgICAgYXV0b2NvbXBsZXRlPVwib2ZmXCJcbiAgICAgICAgcGxhY2Vob2xkZXI9XCJ7eyAnZS5nLiBqb2UuZG9lQGV4YW1wbGUuY29tYExPQ0FMSVpFYCcgfCB0cmFuc2xhdGUgfX1cIlxuICAgICAgICBbKG5nTW9kZWwpXT1cInVzZXIuZW1haWxcIlxuICAgICAgICBlbWFpbFxuICAgICAgICBbcmVxdWlyZWRdPVwidHJ1ZVwiXG4gICAgICAgIFtkaXNhYmxlZF09XCJ1c2VySXNFeHRlcm5hbFwiXG4gICAgICAvPlxuICAgIDwvYzh5LWZvcm0tZ3JvdXA+XG5cbiAgICA8ZGl2IGNsYXNzPVwicm93XCI+XG4gICAgICA8ZGl2IGNsYXNzPVwiY29sLXNtLTZcIj5cbiAgICAgICAgPGM4eS1mb3JtLWdyb3VwPlxuICAgICAgICAgIDxsYWJlbCB0cmFuc2xhdGUgZm9yPVwidXNlckZpcnN0TmFtZVwiPkZpcnN0IG5hbWU8L2xhYmVsPlxuICAgICAgICAgIDxpbnB1dFxuICAgICAgICAgICAgaWQ9XCJ1c2VyRmlyc3ROYW1lXCJcbiAgICAgICAgICAgIGNsYXNzPVwiZm9ybS1jb250cm9sXCJcbiAgICAgICAgICAgIGF1dG9jb21wbGV0ZT1cIm9mZlwiXG4gICAgICAgICAgICBwbGFjZWhvbGRlcj1cInt7ICdlLmcuIEpvZWBMT0NBTElaRWAnIHwgdHJhbnNsYXRlIH19XCJcbiAgICAgICAgICAgIG1heGxlbmd0aD1cIjUwXCJcbiAgICAgICAgICAgIG5hbWU9XCJmaXJzdE5hbWVcIlxuICAgICAgICAgICAgWyhuZ01vZGVsKV09XCJ1c2VyLmZpcnN0TmFtZVwiXG4gICAgICAgICAgICBbZGlzYWJsZWRdPVwidXNlcklzRXh0ZXJuYWxcIlxuICAgICAgICAgIC8+XG4gICAgICAgIDwvYzh5LWZvcm0tZ3JvdXA+XG4gICAgICA8L2Rpdj5cbiAgICAgIDxkaXYgY2xhc3M9XCJjb2wtc20tNlwiPlxuICAgICAgICA8Yzh5LWZvcm0tZ3JvdXA+XG4gICAgICAgICAgPGxhYmVsIHRyYW5zbGF0ZSBmb3I9XCJ1c2VyTGFzdE5hbWVcIj5MYXN0IG5hbWU8L2xhYmVsPlxuICAgICAgICAgIDxpbnB1dFxuICAgICAgICAgICAgaWQ9XCJ1c2VyTGFzdE5hbWVcIlxuICAgICAgICAgICAgY2xhc3M9XCJmb3JtLWNvbnRyb2xcIlxuICAgICAgICAgICAgYXV0b2NvbXBsZXRlPVwib2ZmXCJcbiAgICAgICAgICAgIHBsYWNlaG9sZGVyPVwie3sgJ2UuZy4gRG9lYExPQ0FMSVpFYCcgfCB0cmFuc2xhdGUgfX1cIlxuICAgICAgICAgICAgbWF4bGVuZ3RoPVwiNTBcIlxuICAgICAgICAgICAgbmFtZT1cImxhc3ROYW1lXCJcbiAgICAgICAgICAgIFsobmdNb2RlbCldPVwidXNlci5sYXN0TmFtZVwiXG4gICAgICAgICAgICBbZGlzYWJsZWRdPVwidXNlcklzRXh0ZXJuYWxcIlxuICAgICAgICAgIC8+XG4gICAgICAgIDwvYzh5LWZvcm0tZ3JvdXA+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cblxuICAgIDxjOHktZm9ybS1ncm91cD5cbiAgICAgIDxsYWJlbCB0cmFuc2xhdGUgZm9yPVwidXNlclRlbGVwaG9uZVwiPlRlbGVwaG9uZTwvbGFiZWw+XG4gICAgICA8aW5wdXRcbiAgICAgICAgaWQ9XCJ1c2VyVGVsZXBob25lXCJcbiAgICAgICAgY2xhc3M9XCJmb3JtLWNvbnRyb2xcIlxuICAgICAgICBhdXRvY29tcGxldGU9XCJvZmZcIlxuICAgICAgICBuYW1lPVwicGhvbmVcIlxuICAgICAgICBtYXhsZW5ndGg9XCIyNTRcIlxuICAgICAgICBbKG5nTW9kZWwpXT1cInVzZXIucGhvbmVcIlxuICAgICAgICBwbGFjZWhvbGRlcj1cInt7ICdlLmcuICs0OSA5IDg3NiA1NDMgMjEwYExPQ0FMSVpFYCcgfCB0cmFuc2xhdGUgfX1cIlxuICAgICAgICBjOHlQaG9uZVZhbGlkYXRpb25cbiAgICAgICAgW3JlcXVpcmVkXT1cImlzUGhvbmVSZXF1aXJlZFwiXG4gICAgICAgIFtkaXNhYmxlZF09XCJ1c2VySXNFeHRlcm5hbFwiXG4gICAgICAvPlxuICAgIDwvYzh5LWZvcm0tZ3JvdXA+XG5cbiAgICA8Yzh5LWZvcm0tZ3JvdXAgY2xhc3M9XCJwLXQtMTYgc2VwYXJhdG9yLXRvcFwiICpuZ0lmPVwic2hvd1Byb2R1Y3RFeHBlcmllbmNlT3B0aW9uc1wiPlxuICAgICAgPGxhYmVsIHRyYW5zbGF0ZT5Qcm9kdWN0IGV4cGVyaWVuY2U8L2xhYmVsPlxuICAgICAgPGxhYmVsIGNsYXNzPVwiYzh5LXN3aXRjaFwiIGZvcj1cInByb2R1Y3RVc2FnZVRyYWNraW5nXCI+XG4gICAgICAgIDxpbnB1dFxuICAgICAgICAgIGlkPVwicHJvZHVjdFVzYWdlVHJhY2tpbmdcIlxuICAgICAgICAgIG5hbWU9XCJwcm9kdWN0VXNhZ2VUcmFja2luZ1wiXG4gICAgICAgICAgdHlwZT1cImNoZWNrYm94XCJcbiAgICAgICAgICBbKG5nTW9kZWwpXT1cImlzVXNhZ2VUcmFja2luZ0VuYWJsZWRcIlxuICAgICAgICAvPlxuICAgICAgICA8c3Bhbj48L3NwYW4+XG4gICAgICAgIHt7ICdFbmFibGUgcGVyc29uYWxpemVkIHByb2R1Y3QgZXhwZXJpZW5jZSB0cmFja2luZycgfCB0cmFuc2xhdGUgfX1cbiAgICAgIDwvbGFiZWw+XG4gICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiaXNVc2FnZVRyYWNraW5nRW5hYmxlZFwiPlxuICAgICAgICA8bGFiZWwgY2xhc3M9XCJjOHktc3dpdGNoIG0tbC0wXCIgZm9yPVwidXNlckVuZ2FnZW1lbnRQcmVmZXJlbmNlXCI+XG4gICAgICAgICAgPGlucHV0XG4gICAgICAgICAgICBpZD1cInVzZXJFbmdhZ2VtZW50UHJlZmVyZW5jZVwiXG4gICAgICAgICAgICBuYW1lPVwidXNlckVuZ2FnZW1lbnRQcmVmZXJlbmNlXCJcbiAgICAgICAgICAgIHR5cGU9XCJjaGVja2JveFwiXG4gICAgICAgICAgICBbKG5nTW9kZWwpXT1cImlzVXNlckVuZ2FnZW1lbnRQcmVmZXJlbmNlRW5hYmxlZFwiXG4gICAgICAgICAgLz5cbiAgICAgICAgICA8c3Bhbj48L3NwYW4+XG4gICAgICAgICAge3sgJ0VuYWJsZSBpbi1wcm9kdWN0IGluZm9ybWF0aW9uICYgY29tbXVuaWNhdGlvbicgfCB0cmFuc2xhdGUgfX1cbiAgICAgICAgPC9sYWJlbD5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuICAgIDwvYzh5LWZvcm0tZ3JvdXA+XG5cbiAgICA8ZGl2IGNsYXNzPVwiZm9ybS1ncm91cCBwLXQtMTYgc2VwYXJhdG9yLXRvcFwiICpuZ0lmPVwiIXVzZXJJc0V4dGVybmFsXCI+XG4gICAgICA8bGFiZWwgY2xhc3M9XCJjb250cm9sLWxhYmVsXCI+e3sgJ0xvZ2luIG9wdGlvbnMnIHwgdHJhbnNsYXRlIH19PC9sYWJlbD5cbiAgICAgIDxjOHktbmV3LXBhc3N3b3JkIChwYXNzd29yZCk9XCJvbk5ld1Bhc3N3b3JkQ2hhbmdlZCgkZXZlbnQpXCI+PC9jOHktbmV3LXBhc3N3b3JkPlxuICAgICAgPGJ1dHRvblxuICAgICAgICBjbGFzcz1cImJ0biBidG4tZGVmYXVsdFwiXG4gICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICB0aXRsZT1cInt7ICdTZXQgdXAgdHdvLWZhY3RvciBhdXRoZW50aWNhdGlvbicgfCB0cmFuc2xhdGUgfX1cIlxuICAgICAgICAoY2xpY2spPVwic2V0dXBUb3RwKClcIlxuICAgICAgICAqbmdJZj1cInVzZXJDYW5TZXR1cFRvdHAgJiYgIXVzZXJIYXNBY3RpdmVUb3RwICYmIGlzVGZhRW5hYmxlZFwiXG4gICAgICA+XG4gICAgICAgIHt7ICdTZXQgdXAgdHdvLWZhY3RvciBhdXRoZW50aWNhdGlvbicgfCB0cmFuc2xhdGUgfX1cbiAgICAgIDwvYnV0dG9uPlxuICAgIDwvZGl2PlxuXG4gICAgPGM4eS1mb3JtLWdyb3VwICpuZ0lmPVwiISEoc3RhdGUuc3RhdGUkIHwgYXN5bmMpLm5ld3NsZXR0ZXJcIj5cbiAgICAgIDxsYWJlbCB0cmFuc2xhdGU+TmV3c2xldHRlcjwvbGFiZWw+XG4gICAgICA8bGFiZWxcbiAgICAgICAgdGl0bGU9XCJ7eyAnU2VuZCBtZSBpbmZvcm1hdGlvbiBhYm91dCBvdXRhZ2VzLCBtYWludGVuYW5jZSBvciB1cGRhdGVzLicgfCB0cmFuc2xhdGUgfX1cIlxuICAgICAgICBjbGFzcz1cImM4eS1jaGVja2JveFwiXG4gICAgICA+XG4gICAgICAgIDxpbnB1dFxuICAgICAgICAgIHR5cGU9XCJjaGVja2JveFwiXG4gICAgICAgICAgbmFtZT1cIm5ld3NsZXR0ZXJcIlxuICAgICAgICAgIFsobmdNb2RlbCldPVwidXNlci5uZXdzbGV0dGVyXCJcbiAgICAgICAgICBbZGlzYWJsZWRdPVwidXNlcklzRXh0ZXJuYWxcIlxuICAgICAgICAvPlxuICAgICAgICA8c3Bhbj48L3NwYW4+XG4gICAgICAgIDxzcGFuPlxuICAgICAgICAgIHt7ICdTZW5kIG1lIGluZm9ybWF0aW9uIGFib3V0IG91dGFnZXMsIG1haW50ZW5hbmNlIG9yIHVwZGF0ZXMuJyB8IHRyYW5zbGF0ZSB9fVxuICAgICAgICA8L3NwYW4+XG4gICAgICA8L2xhYmVsPlxuICAgIDwvYzh5LWZvcm0tZ3JvdXA+XG4gIDwvZGl2PlxuICA8ZGl2IGNsYXNzPVwibW9kYWwtZm9vdGVyIHNlcGFyYXRvci10b3AgYmctbGV2ZWwtMCBzdGlja3ktYm90dG9tXCI+XG4gICAgPGJ1dHRvblxuICAgICAgY2xhc3M9XCJidG4gYnRuLWRlZmF1bHRcIlxuICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICB0aXRsZT1cInt7ICdDYW5jZWwnIHwgdHJhbnNsYXRlIH19XCJcbiAgICAgIChjbGljayk9XCJjYW5jZWwoKVwiXG4gICAgPlxuICAgICAge3sgJ0NhbmNlbCcgfCB0cmFuc2xhdGUgfX1cbiAgICA8L2J1dHRvbj5cbiAgICA8YnV0dG9uXG4gICAgICBjbGFzcz1cImJ0biBidG4tcHJpbWFyeVwiXG4gICAgICB0eXBlPVwic3VibWl0XCJcbiAgICAgIHRpdGxlPVwie3sgJ1NhdmUnIHwgdHJhbnNsYXRlIH19XCJcbiAgICAgIFtkaXNhYmxlZF09XCIhdXNlckZvcm0uZm9ybS52YWxpZCB8fCB1c2VyRm9ybS5mb3JtLnByaXN0aW5lIHx8IGxvYWRpbmdcIlxuICAgID5cbiAgICAgIHt7ICdTYXZlJyB8IHRyYW5zbGF0ZSB9fVxuICAgIDwvYnV0dG9uPlxuICA8L2Rpdj5cbjwvZm9ybT5cbiJdfQ==